Intro
In 2021 I was learning about object-oriented programming in Fortran while writing a little simulation code for industrial food dryers as part of my research. The math was not that complicated, essentially it was just a 1-D PDE solver with some coupled ODE equations.
The industrial dryers on the other hand, can be quite complicated; they have multiple sections or stages which can be controlled independently and tuned precisely to deliver the expected quality of the final product. Here is a schema of a continuous pasta dryer, taken from a patent by the company BĆ¼hler, showing also the product drying curve (Pf).
The piecewise functions for temperature (T) and relative humidity (r.F.) shown in the graph above enter the boundary condition of the PDE for moisture transport within the (wet) pasta dough.
I decided I would use an abstract base class for the drying programs:
type, abstract :: drying_program_base
integer :: n ! number of variables/settings
contains
procedure(get_air_condition_interface), deferred :: get_air_condition
procedure(getvars_interface), deferred :: getvars
procedure(setvars_interface), deferred :: setvars
end type
The child classes would implement methods to return the temperature and humidity at a given point in time. I also required methods to serialize/deserialize the classes. E.g. when performing mathematical optimization of the dryer, the optimization codes expects a packed vector of variables x(:)
and not some custom type like this child class:
!> Three-stage program with flat settings
type, extends(drying_program_base) :: threestage_flat
real(wp) :: T(3), RH(3) ! temperature and humidity
real(wp) :: f1, f2 ! fraction of time in stage 1 and 2
real(wp) :: total_time ! total_time := f1 + f2 + f3
contains
! [... omitted ...]
end type
My code grew as I added different types of programs, two-stage, three-stage, n-stage, constant temperature, rising temperatureā¦ I also made a child class where the temperature and humidity curves are Lagrange polynomials, but decided to abandon the idea. A smooth curve may seem like a good idea, but in practice flat sections and linear ramps are easier to implement and control.
The main driver of my program became obfuscated with large chunks of code dealing with allocation of the polymorphic child types and their initialization in lengthy select type
blocks. When I added the constrained mathematical optimization, things snow-balled further with the packing/unpacking of the variables.
Something wasnāt right. The object-oriented language features I thought would help me organize everything, were leading to code bloat.
Two years later
Today, I revisited the problem, forcing myself to think, what is the shortest way of getting an n-stage drying program into my simulation subroutine.
Hereās the sketch I came up with:
integer :: prog, nstages, i
real(wp) :: time, duration, temperature, heating_rate
real(wp) :: relative_humidity, humidification_rate
open(unit=prog,file="program.txt",action="read") ! program.txt contains the dryer settings
read(prog,*) num_stages ! how many stages does the dryer have
time = 0 ! start drying
do i = 1, nstages ! for each stage
! read the settings from the file
read(prog,*) duration, temperature, heating_rate, &
relative_humidity, humidification_rate
! perform simulation for stage i
call drysim(t=time, tend=time+duration, &
temp=temperature, qtemp=heating_rate, &
rh=relative_humidity, rhdot=humidification_rate, &
[... other parameters and options ...] )
! at exit from drysim,
! time := time + duration
end do
close(prog) ! close the drying program
In contrast to my previous code, there are no derived types, no polymorphism, everything is obvious. The tables of drying settings can be loaded and exchanged easily as files without the need for recompilation. In case of mathematical optimization, itās trivial to pack the variables into a vector. All I needed was a sane data representation.
Soon afterward I remembered a cautionary tale on meta-programming given by Steven Johnson at JuliaCon 2019 (Steven Johnson is co-author of FFTW, and professor of applied mathematics at MIT). The relevant section starts at 13:30 (810 s), and lasts approx. 2 minutes:
As he says from his tale on Pascal code generation (and matching my epiphany),
[ā¦] thereās a lot of cases like that, I think, where people just starting out are really tempted to do meta-programming, and you know, because spitting out code like this, itās really easy to see whatās going on, thinking about āOh, I actually have a data structure in memory that has all this,ā thatās a higher level of abstraction ā¦ not that much higher but [ā¦]