1

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).

Captain
  • 21
  • 2

1 Answers1

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.

Community
  • 1
  • 1
roygvib
  • 7,218
  • 2
  • 19
  • 36
  • Is there a way though to interpret a string input for use in the program? This takes a string and provides a numeric. Say for instance I had a time-marching simulation that evolves an initial function. I'd like the program to read an initial function externally as though I'd written it internally, and work with it. – Captain Nov 19 '16 at 01:23
  • It is for PDE evaluation, however it's for more than the initial conditions. I want the code to have an adjustable nonlinearity function f(x,t) that I define externally. This nonlinearity is within the time-marching algorithm, so I believe an evaluation numerically is either incompatible or a poor option process-wise. – Captain Nov 19 '16 at 01:50
  • If the definition of the initial function involves some codes having multiple lines (and possibly depends on various parameters), hmm, I think the shared-library approach may be better and pass all variables as dummy arguments. Because Fortran cannot evaluate codes at run time, I guess possible approaches are rather limited here... – roygvib Nov 19 '16 at 01:52
  • 1
    If it's a matter of compilation or runtime, re-compilation is fine. I'd rather recompile after writing a function externally in a two line txt file than sort through the code, write it there everytime (I'd have to re-compile anyways). – Captain Nov 19 '16 at 01:59
  • Hmm, if re-compilation is acceptable, I guess there may be better approaches... I think if you edit your question and clarify more about this, then different (more useful) answers may come :) – roygvib Nov 19 '16 at 02:03
  • 1
    I edited the question. Generally speaking, if can get the code to take any externally written code (whether .f90 or .txt) and insert it into the code that'd be wonderful (so not just mathematical statements, although this is the primary objective). – Captain Nov 19 '16 at 02:12
  • Just to make sure, use of the `include` statement is not a very desirable option (for your purpose), correct? – roygvib Nov 19 '16 at 02:21
  • I'm not sure. I was taught it was used to include outer functions and what not. Does it insert directly operable code? – Captain Nov 19 '16 at 02:37
  • Yes include does that... However a module also does that for both code and variables. Then your makefile usually builds the module if the module code changes and it percolates upwards to rebuild the library that uses the module, then the code that uses the library. Sometimes you want only parts of the include, which is conceptually easier with USE, modulename, ONLY: partofmodule – Holmz Nov 19 '16 at 02:49
  • 1
    SIND and others will be in the new version of gfortran. – Vladimir F Героям слава Nov 19 '16 at 09:57
  • @VladimirF Hi, I have just tried re-compiling with gfortran-6, but still not successful (even with -fdec-math option), although this page indeed shows it is supported. https://gcc.gnu.org/onlinedocs/gfortran/SIND.html#SIND Do I need gfortran-7 ? (Btw, ifort and oracle were able to compile sind etc.) – roygvib Nov 20 '16 at 00:30
  • @roygvib The patch was quite recent, probably v.7. – Vladimir F Героям слава Nov 20 '16 at 18:55
  • @VladimirF Thanks, I will try once I get access to v.7 :) – roygvib Nov 22 '16 at 16:11
  • @Captain There was a very similar question recently, and another approach for invoking Python from Fortran is suggested (please see "EDIT" above). So this might be a "third" option for some cases... (though this is in fact very close to compiling a piece of Fortran code on-the-fly and referring to it via a shared library). Cheers :) – roygvib Nov 27 '16 at 03:28