How does one insert fortran code from an external

2019-07-28 11:51发布

问题:

I'd like to have my code take code written in another document, read it, and then use it as though it was written in the code. Say we have the following:

MODULE samplemod
CONTAINS  
  FUNCTION sillysum(boudary,function) RESULT(counter)
    IMPLICIT NONE
    REAL(KIND=8) :: boundary, counter
    REAL(KIND=8), DIMENSION(:) :: function
    INTEGER :: m
    counter = 0.d0
    DO m = 1, my_mesh%me
      counter = function(m) + externalfunction
    END DO
  END FUNCTION sillysum
END MODULE samplemod

PROGRAM sampleprogram
  USE samplemod
  REAL(KIND=8), DIMENSION(:) :: function1
  ALLOCATE(function1(100))
  DO m=1, 100
     function1(i) = i
  END DO
  WRITE(*,*) sillysum(100,function1)    
END PROGRAM sampleprogram

Where in some external file (say 'externfunct.txt') one has written m**2. How can the Fortran code read the external function m**2, SIN(m), or even 0 and have that replace externalfunction. Here's a simpler example:

REAL(KIND=8) :: x = 2
CHARACTER(LEN=*) :: strng = "external"
WRITE(*,*) "Hello world, 2 + ", strng, " = ", 2 + external

Where in a txt file I have written I have written SIN(x).

回答1:

I think there are two different approaches for this (* in fact, there seems a "third" approach also, see EDIT); one is to use a shared library, and the other is to use a parser for math expressions. The first approach is described in a Rossetastone page (Call a function in a shared library) and an SO page (Fortran dynamic libraries, load at runtime?), for example. For the second approach, you can find 3rd-party libraries by searching with "math parser" or "Fortran math parser" etc. Here, I have tried this one because it seems pretty simple (only one module and no installation). If we write a simple test program like this

program test
    use interpreter, only: init, evaluate, dp => realkind
    implicit none
    integer, parameter :: mxvars = 10   !! consider 10 variables at most here
    character(10)      :: symbols( mxvars )
    real(dp)           :: values( mxvars ), answer
    character(1000)    :: funcstr  !! a user-defined math expression
    character(5)       :: stat

!> Define variable names.
    symbols( 1 ) = "x"
    symbols( 2 ) = "a"
    symbols( 3 ) = "b"
    symbols( 4 ) = "c"
    symbols( 5 ) = "foo"

!> Get a math expression.
    print *, "Please input a math expression:"
    read(*, "(a)") funcstr    !! e.g., a * x + b

!> Init the evaluator.
    call init( funcstr, symbols, stat )
    if ( stat /= "ok" ) stop "stat /= ok"

!> Set values for the variables.
    values( : ) = 0
    values( 1 ) = 2.0_dp   ! x
    values( 2 ) = 10.0_dp  ! a
    values( 3 ) = 7.0_dp   ! b

!> Evaluate.
    answer = evaluate( values )
    print *, "function value = ", answer

end program

and compile it as (*1)

$ gfortran -ffree-line-length-none interpreter.f90 mytest.f90

we can test various expressions as follows:

$ ./a.out
 Please input a math expression:
a * x + b
 function value =    27.000000000000000
$ ./a.out
 Please input a math expression:
sin( a * x ) + cos( b ) + foo
 function value =    1.6668475050709324

The usage of other libraries also seems very similar. Because the performance of each library may be rather different, it may be useful to try several different libraries for comparison.


(*1) The module has some lines with sind, cosd, and tand, but they are not supported by gfortran. So, to compile, I have commented them out and replaced them by stop, i.e.,

stop "sind not supported..."
! pdata(st) = sind(pdata(st))

(I guess sind(x) means sin( x * pi / 180 ), so it may be OK to define it as such.)


[EDIT]

A "third" approach may be to call the built-in eval() function in interpreted languages like Python or Julia via system(), e.g., this SO page. Although this also has a lot of weak points (and it is probably much easier to use such languages directly), calling eval() from Fortran might be useful for some specific purposes.