How to compile a Fortran subroutine by using the MEX engine

4 views (last 30 days)
Hi!
I have been trying to use a Matlab function (interp2) in Fortran using the Mex engine.
I have adapted the Fengdemo.F to suit my needs and the test cases work.
However, it is supposed to be part of a larger Fortran project with input coming from the rest of the program.
Therefore, I need the function as a subroutine or such, but mex does not let me compile it as anything other than a main() program, which inherentely does not accept input.
I have tried defining it as a subroutine, as well as a module.
Any ideas on what I might do wrong or how to get around this problem?
I find it hard to imagine the mex engine is only able to run main() programs as this would render this function more or less useless.
I am attaching a minimal example based on Fengdemo.F which is now a subroutine and just supposed to square an input.
Any hints are highly appreciated!
Kind regards
Lars
module InterpMatlab_mod
contains
#include "fintrf.h"
! Interpolation
#if 0
!
! fengdemo.F
! .F file need to be preprocessed to generate .for equivalent
!
#endif
!
! fengdemo.f
!
! This is a simple program that illustrates how to call the MATLAB
! Engine functions from a FORTRAN program.
!
! Copyright 1984-2018 The MathWorks, Inc.
!======================================================================
!
subroutine InterpolMatlab(x)
! Declarations
implicit none
mwPointer engOpen, engGetVariable, mxCreateDoubleMatrix
#if MX_HAS_INTERLEAVED_COMPLEX
mwPointer mxGetDoubles
#else
mwPointer mxGetPr
#endif
mwPointer x_point, xOut_point
integer engPutVariable, engEvalString, engClose
mwSize M
parameter(M=1)
integer, PARAMETER :: sp = KIND(1.0D0)
!----------------------------------------------------------------
! Input
integer :: x
! Output
integer :: x_square
!--------------------------------------------------------------------------------------------------!
ep = engOpen('matlab ')
if (ep .eq. 0) then
write(6,*) 'Can''t start MATLAB engine'
stop
endif
write(6,*) 'started MATLAB engine'
! Place the variables into the MATLAB workspace
x_point = mxCreateDoubleMatrix(M, M, 0)
#if MX_HAS_INTERLEAVED_COMPLEX
call mxCopyReal8ToPtr(x, mxGetDoubles(x_point), M)
#else
call mxCopyReal8ToPtr(x, mxGetPr(x_point),M)
#endif
status = engPutVariable(ep, 'x_point', x_point)
if (status .ne. 0) then
write(6,*) 'engPutVariable failed'
stop
endif
! Calculations in Matlab
if (engEvalString(ep, 'x_out=x^2') .ne. 0) then
write(6,*) 'engEvalString failed'
stop
endif
xOut_point = engGetVariable(ep, 'x_out')
write(6,*) 'Got pointer'
#if MX_HAS_INTERLEAVED_COMPLEX
call mxCopyPtrToReal8(mxGetDoubles(xOut_point), x_square, 1)
#else
call mxCopyPtrToReal8(mxGetPr(xOut_point), x_square, 1)
#endif
write(6,*) 'Result=', x_square
print *, 'Type 0 <return> to Exit'
print *, 'Type 1 <return> to continue'
read(*,*) temp
if (temp.eq.0) then
print *, 'EXIT!'
status = engClose(ep)
if (status .ne. 0) then
write(6,*) 'engClose failed'
endif
stop
end if
call mxDestroyArray(x_point)
call mxDestroyArray(xOut_point)
status = engClose(ep)
if (status .ne. 0) then
write(6,*) 'engClose failed'
stop
endif
stop
end
end module InterpMatlab_mod
  4 Comments
Lars Schmitz
Lars Schmitz on 16 Mar 2022
Probably right. Was hoping for a bit less effort on this and then I was already in over my head.

Sign in to comment.

Accepted Answer

James Tursa
James Tursa on 9 Mar 2022
Edited: James Tursa on 9 Mar 2022
It looks like you are taking input, calling a MATLAB function, and getting output from that function. I still say it would be easier to use a mex routine for this. However, if you insist on using an Engine application that can be done too. My comments on your posted Engine code:
1)
#if MX_HAS_INTERLEAVED_COMPLEX
mwPointer mxGetDoubles
#else
mwPointer mxGetPr
#endif
:
#if MX_HAS_INTERLEAVED_COMPLEX
call mxCopyPtrToReal8(mxGetDoubles(xOut_point), x_square, 1)
#else
call mxCopyPtrToReal8(mxGetPr(xOut_point), x_square, 1)
#endif
I know that MATLAB examples have this type of code now because of the change in how complex numbers are stored. But you can make your code simpler by just using mxGetData( ) instead:
mwPointer, external :: mxGetData
:
call mxCopyPtrToReal8(mxGetData(xOut_point), x_square, 1) ! But don't use literal 1 here, see below
2)
You don't define ep. WIth your implicit none this shouldn't have even compiled. You need something like this:
mwPointer ep
3)
You use an explicit integer argument in an API function call. NEVER do this in Fortran! You don't get automatic type conversion in Fortran like you do in C/C++. Always use typed variables for this to ensure the integer size is the correct size for the interface. E.g., this line
x_point = mxCreateDoubleMatrix(M, M, 0)
should be something like this instead
integer*4 :: ComplexFlag = 0
:
x_point = mxCreateDoubleMatrix(M, M, ComplexFlag)
Same comment applies to your mxCopyPtrToReal8(...,1) call. That last input argument needs to be an explicitly typed variable holding the value 1, not a literal integer 1.
4)
You don't use the correct names on the Engine side. E.g.,
status = engPutVariable(ep, 'x_point', x_point) ! Here you put a variable called x_point in the Engine
:
if (engEvalString(ep, 'x_out=x^2') .ne. 0) then ! Here you use a variable called x in the Engine
In the above code, there is no variable called 'x' in the Engine because you haven't put any 'x' there. You put 'x_point'. Either put 'x' in the Engine or change your string to use 'x_point' on the rhs. E.g.,
status = engPutVariable(ep, 'x', x_point) ! Here you put a variable called x in the Engine
5)
You don't use the correct sizes on the data copy, although it does work for scalars. E.g., x_point is an M x M mxArray, but you only copy M elements when you should be copying M*M elements.
6)
You are using floating point copy routines to copy integer variables. Not only could this lead to a crash if the integers are not the correct size, but the bit patterns for the same value are completely different between integer and double precision so the calculations on the MATLAB Engine side will be erroneous. E.g.,
integer :: x ! could be 4-byte or 8-byte integer depending on compiler settings
integer :: x_square ! could be 4-byte or 8-byte integer depending on compiler settings
But the following code creates a double mxArray and copies the bit patterns of the integers into the double memory locations. Interpreting integer bit patterns as double precision IEEE variables on the MATLAB side is just going to give you garbage.
x_point = mxCreateDoubleMatrix(M, M, 0)
#if MX_HAS_INTERLEAVED_COMPLEX
call mxCopyReal8ToPtr(x, mxGetDoubles(x_point), M) ! This does a bit pattern memory copy
#else
call mxCopyReal8ToPtr(x, mxGetPr(x_point),M) ! This does a bit pattern memory copy
#endif
You need x and x_square to be double precision. Using real*8 here simply to match the doc exactly:
real*8 :: x
real*8 :: x_square
If you really want to use integers, then you should not be creating a double mxArray with mxCreateDoubleMatrix( ). You would need to create an integer mxArray of the correct size with mxCreateNumericMatrix( ) and then use the correct mxCopyEtc( ) routine for that particular integer size.
  5 Comments
Lars Schmitz
Lars Schmitz on 16 Mar 2022
Thank you for all those remarks! I believe they will come in really handy!

Sign in to comment.

More Answers (0)

Products


Release

R2021b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!