Runge-Kutta code reuse: fpm

Hello. I’m an undergraduate student new to Fortran.

I currently write a function, i.e in rungekutta.f90 and I copy-paste it to another project directory when needed. I’m not sure if this is the typical workflow. I wish I could reuse code with fpm. Thanks!

1 Like

I would like to save the rk4 function in a separate unit, because I don’t want to modify it unintentionally. I currently save everything (rk4, the equation and the main program) in a single unit.

Hi @une and welcome!

Assuming you got an fpm binary in your path, first create a new fpm package like this:

$ fpm new ode
 + mkdir -p ode
 + cd ode
 + mkdir -p ode/src
 + mkdir -p ode/test
 + mkdir -p ode/app
 + git init ode
Initialized empty Git repository in /home/milan/Work/fortran/ode/.git/

You’ll get the ode directory in your current path.

$ cd ode
$ tree
.
├── app
│   └── main.f90
├── fpm.toml
├── README.md
├── src
│   └── ode.f90
└── test
    └── main.f90

3 directories, 5 files

Now put your ode module in src/ode.f90 and your program in app/main.f90, and type

$ fpm build

to only compile the library and program, or

$ fpm run

to compile and run.

1 Like

Hello. Thank you @milancurcic for the detailed response. It helped me a lot.

When I run “fpm new ode”, fpm creates the ode directory, but the subdirectories src, test and app are not created. I manually create the subdirectories and I follow your instructions and now the program works as expected.

However, there’s one problem with my code. The compiler says
“Procedure ‘f’ called with an implicit interface”. Is it important? “fpm run” shows the correct response though. Thanks!

ode-f90

“fpm build”, output

In general, yes. It’s not hurting you here, but using explicit interfaces when passing procedures to other procedures can save you from one kind of nasty bug. As you have it now:

real function rk4(f, x, y, h)
    implicit none
    real, external :: f
    ...

procedure argument f doesn’t have an explicit interface. In other words, rk4 doesn’t know how many and what type of arguments can f take. It only knows that its result is real. So the compiler has to allow any real-valued function to be passed, and it also can’t check that you’re passing a function that is meaningful for rk4. The compiler is warning you that this can be a source of bugs.

To illustrate, suppose that in the module you also defined this function:

real function dy_bad(x)
    real, intent(in) :: x
    dy_bad = 2 * x
end function

I called it dy_bad to emphasize that it shouldn’t work when passed to rk4, because in rk4 you’re calling f with two input arguments, not one. If you now pass dy_bad to rk4, e.g.:

print *, rk4(dy_bad, 1., 1., 0.1)

not only will the code compile, but it will also run, and you may get a numerical result that is wrong but appears correct. Of course, here it’s obvious that dy_bad shouldn’t be passed to rk4, but the danger arises in larger programs where this kind of mistake could be easily made, either due to lack of attention or due to a typo.

Instead, define your procedure argument with an explicit interface like this:

real function rk4(f, x, y, h)
    procedure(dy) :: f
    ...

Now if you try to pass dy_bad to rk4, you’ll get something like this when you try to compile the program:

$ gfortran test_explicit.f90 
test_explicit.f90:32:15:

   print *, rk4(dy_bad, 1., 1., 0.1)
               1
Error: Interface mismatch in dummy procedure ‘f’ at (1): 'dy_bad' has the wrong number of arguments

Thanks to the explicit interface, the compiler can tell you exactly what’s wrong with this rk4 invocation.

A few other tips:

  • You can state implicit none once in the module, and let your functions defined in the module inherit the explicit typing rule.
  • Define your procedures as pure if they can be pure. It won’t change the behavior here but I think it’s a good practice. pure attribute prevents your procedures from causing side-effects elsewhere in the program. Another benefit is that if you use pure functions, you can’t pass procedure arguments with an implicit interface, so the compiler would force you to use explicit interface.
1 Like

I don’t know why that is. Did you download an fpm binary and which one or did you build it from source?

I’m using this one: https://github.com/fortran-lang/fpm/releases/download/v0.1.0/fpm-v0.1.0-linux-x86_64

1 Like

@une,

Welcome. Please also try to study @milancurcic 's excellent book! You’ll become proficient in modern Fortran quickly, a useful skill for your numerical (and scientific?) journey ahead.

3 Likes

I now see that that is a really important Warning. Thanks!

I’m using fpm from
https://github.com/fortran-lang/fpm/releases/download/v0.1.0/fpm-bootstrap-v0.1.0-windows-x86_64.exe

Okay, try this one instead: https://github.com/fortran-lang/fpm/releases/download/v0.1.0/fpm-v0.1.0-windows-x86_64.exe

fpm-bootstrap-* is the experimental Haskell implementation which was used early on to prototype and design the fpm specification.

fpm-* is the Fortran implementation that is being actively developed and which I recommend to end users.

2 Likes

fpm output is

ERROR: the new directory basename must be an allowed
       Fortran name. It must be composed of 1 to 63 ASCII
       characters and start with a letter and be composed
       entirely of alphanumeric characters [a-zA-Z0-9]
       and underscores.
STOP 4

Thanks!

Hi @une welcome to the Forum!

Thanks for reporting your problem with fpm; it looks like you are encountering a bug in the Windows release that was recently fixed.

I will create a new binary release with the fix included for you to download.

2 Likes

Hi @une, please try the fixed Windows version here:
https://github.com/fortran-lang/fpm/releases/download/v0.1.1/fpm-v0.1.1-windows-x86_64.exe

Do let us know if you encounter any other problems with fpm.

2 Likes

Hi @lkedward.

Thanks for the new release. In this version, actions “new” and “build” work. “run” action says
‘build’ is unknown command. Thanks!

fpm-v0.1.1-windows-x86_64 run
 + build/gfortran_debug\app\test.exe
'build' não é reconhecido como um comando interno
ou externo, um programa operável ou um arquivo em lotes.
 Command failed
ERROR STOP

Error termination. Backtrace:

Could not print backtrace: libbacktrace could not find executable to open
#0  0xffffffff
#1  0xffffffff
#2  0xffffffff
#3  0xffffffff
#4  0xffffffff
#5  0xffffffff
#6  0xffffffff
#7  0xffffffff
#8  0xffffffff
#9  0xffffffff
#10  0xffffffff

OK, I found out I can use the bootstrap version for the “run” action. Thank you everyone.

Many thanks for reporting. A fix has been put together which should be available soon.

1 Like

@une it looks like the v0.1.2 that should fix this is now out: https://github.com/fortran-lang/fpm/releases/tag/v0.1.2. Give it a try.

Also, we just enabled GitHub Discussions for the fpm repository:

Of course, you can continue posting on Discourse, and there’s now an fpm-specific place to ask questions.

2 Likes

Hi.

Thank you for the new release. I’ve a created a topic in the GitHub forum.

I believe this ‘Runge-Kutta’ topic can be closed. Thanks!