Background
Since writing this post I got in touch with the authors of air2stream
in Oct 2023. They have a post-doc porting this work to Python or perhaps Julia.
air2stream
is a model to predict River Water Temperature (RWT) using air temperature and discharge.. A project I work on intend to use it for simulation and forecasting of river water temperature, as part of a system where Python is likely to be the common interface language.
This post starts with the guess that I will end up using f90wrap
to generate glue code, something I’ve already used to wrap a Fortran code (WAA tool) and transition to Python. That said, I’ll consider other options on the table, not only for the sake of exercise but for future project needs, so the end target may well be a complete rewrite to Python.
Assessing
First, is there existing work that has ported air2stream
to Python?
There is a recent paper Modeling river water temperature with limiting forcing data: Air2stream v1.0.0, machine learning and multiple regression and companion codebase https://github.com/mcvta/WaterPythonTemp. air2stream
and Python are used, but as joint tools for dealing with input/output files by the look of it.
There is no match for a package air2stream
on the Python Package Index (PyPI).
Options
One can slice things up in many ways, but broadly speaking the options are:
use the command line tool as-is and deal with input-output from Python
wrapping with Fortran-Python interop glue code
re-write to a “pure” Python package
re-write to a Python package with Cython for performance sensitive parts
use the command line tool as-is and deal with input-output from Python
- using the reference implementation
- little risk of bugs
- tedious input/output code
- possibly not scalable to a system or some use cases
wrapping with Fortran-Python interop glue code
- using the reference implementation with minimal changes
- moderate risks of bugs
- move away from rigid file system for I/O
- grey-box access to model states
- possibly not scalable to a system or some use cases
re-write to a “pure” Python package
- contemporary mainstream language, better long term maintenance
- needs stringent side by side testing for validation against reference implementation
- risks of bugs moderate to likely (notwithsanding tests)
- runtime performance degradation likely
- possibly less ownership from initial authors.
- concurrent implementation if the Fortran implementation is further evolved.
re-write to a Python package with Cython for performance sensitive parts
- Mostly same as previous, except:
- little runtime performance degradation
- Mostly same as previous, except:
Assessing the prior air2stream
codebase
air2stream
seems written in a Fortran90 syntax. Although in includes a calibration procedure the codebase is rather compact, with the core subroutines fitting in a file with ~500 lines of code. A fork of it already includes additional material with cmake
to facilitate compilation, so I start from this fork to create my air2stream
fork.
AIR2STREAM_MODULES.f90 has global variables, so far as I recall my last foray into Fortran code. Effectively, if we wrap the code as-is we cannot create multiple instances of models within the same process.
Fortran wrapping tools
Back in 2019 I used f90wrap
to wrap a Fortran 90 codebase (Design and implementation of a software tool supporting the Inter-Provincial Water Apportionment Accord in Pakistan). This was to help validate a new implementation in “pure” Python. In 2019 f2py
was not an option as fortran90, in particular custom types, was not supported. In 2023, however, it appears to be the case.
Resources
Main resources on Fortran/python interop:
f90wrap
and the f90wrap github repo
How to Call Fortran from Python
How to Work with Legacy Fortran Code: A Short Guideline interesting read
Python page in Fortran Wiki has a few dead or stale links, but is informative still.
fmodpy is something I may not have noticed a few years ago. It looks neat, but this is unclear how it could work in a program with multilple files.
f2py
is now part of numpy (Beware not to use the legacy packages on pypi and conda, if they are still available at the time of writing)