matlabfortranmex

Calling a Matlab function in Fortran


Using the MATLAB Engine API for Fortran, I am trying to call a simple MATLABfunction from a Fortran code.

I followed the fengdemo example found here. It worked, so I want to adapt my Fortran code to call a specific Matlab script I wrote.

My MATLAB script call_fortran.m is very simple: it takes x as an entry and multiplies it by 2:

function multiply = call_fortran(x)
     multiply = 2*x;
end

I want my FORTRAN code to generate a variable my_x, open a MATLAB session, send the variable to the workspace, apply the function call_fortran and display the result. Using the fengdemo.f code, I wrote :

program main
C     Declarations
implicit none
mwPointer engOpen, engGetVariable, mxCreateDoubleMatrix
mwPointer mxGetPr
mwPointer ep, my_x  ! ep: variable linked to engOpen, starting a Matlab session, my_x: variable que je veux donner a Matlab
double precision my_x
integer engPutVariable, engEvalString, engClose
integer temp, status
mwSize i
my_x = 6
ep = engOpen('matlab ')
if (ep .eq. 0) then
    write(6,*) 'Can''t start MATLAB engine'
    stop
endif
    
C     Place the variable my_x into the MATLAB workspace
status = engPutVariable(ep, 'my_x', my_x)
C
if (status .ne. 0) then 
    write(6,*) 'engPutVariable failed'
    stop
endif
! My issue now is to call the correct Matlab script
! nlhs = 1
! plhs = 1
! nrhs = 1
! prhs = 1
! integer*4 mexCallMATLAB(nlhs, plhs, nrhs, prhs, functionName)

So I have my my_x, I send it to MATLAB, but how do I apply the call_fortran.m function and get the new value of my_x?


Solution

  • You have two fundamental errors with this code. You do not use the correct variable type for my_x, and you cannot call mexCallMATLAB( ) from an Engine application (that can only be used in mex routines). Let's fix these. First, the my_x variable needs to be an mxArray, not a double precision variable. There are various ways to do this, but for a scalar, the easiest way to create this array is as follows:

    mwPointer, external :: mxCreateDoubleScalar
    mwPointer my_x
    my_x = mxCreateDoubleScalar(6.d0)
    

    Then you can pass this to the MATLAB Engine per your current code. To call your function in the MATLAB Engine workspace, you need to evaluate a string there:

    integer*4, external :: engEvalString
    integer*4 status
    status = engEvalString( ep, 'result = call_fortran(my_x)' )
    

    The result should display in the Engine workspace since we did not terminate the string with a semi-colon. If you want to get the result back into your Fortran code, you would need to do something like this:

    mwPointer, external :: engGetVariable
    mwPointer result
    result = engGetVariable( ep, 'result' )
    

    The result inside your Fortran code will be an mxArray. To extract the number there are various ways, but for a scalar it would be easiest to just do as follows (the real*8 is used instead of double precision to match the MATLAB API signature in the doc exactly):

    real*8, external :: mxGetScalar
    real*8 myresult
    myresult = mxGetScalar(result)
    

    To avoid memory leaks, once you are done with the mxArray variables you should destroy them. E.g.,

    call mxDestroyArray(my_x)
    call mxDestroyArray(result)
    

    Having written all this, are you sure you want to create MATLAB Engine applications, and not mex routines? Mex routines are generally easier to work with and don't involve extra data copies to pass variables back & forth.