Hi everyone,
I want to share a project I have been working on called CodyFortran (Correlated Dynamics with Fortran). It’s a framework for simulating the dynamics of (correlated) quantum (many-body) systems (like electrons in atoms/molecules or lattices). To be really useful, there are still some nice propagators/grids missing. I will add them in the future but for the moment I would like to share it as an architectural experiment I was trying out.
The idea was to make the configuration file (JSON) a 1:1 map of the source code’s directory structure. That way if you see a nested object in the JSON config, you know exactly where the implementation lives in src/.
For example, a linear grid with FEDVR discretization lives in src/Grid/Linear/Fedvr/, so the config looks like:
"grid": {
"linear": {
"fedvr": { ... }
}
}
I like that the abstraction hierarchies are reflected in both the code structure and the configuration file. Probably there are more elegant ways to achieve this, but I decided to use a “Fabrication” phase which parses the JSON config and dynamically wires procedure pointers to achieve the necessary polymorphism. I know it is a bit of a wild approach because wiring procedure pointers by hand can lead to segfaults during development if an unassociated procedure pointer is called (though this is easy to debug). But for a hobby project, the flexibility is really fun. Adding a new feature just means dropping a new folder in the right place and adding a key to the JSON.
I would be interested to hear your thoughts or if anyone else has tried a similar approach!
Repo: GitHub - flackner/codyfortran
4 Likes
Welcome to the forum.
This is an interesting project you have there. Given that you mentioned the dangers that are connected to the manual management of procedure pointers: a safer alternative to them is the use of objects that contain polymorphic methods, and the instantiation of such objects by class constructors (which shifts the burden of managing procedure pointers [that are still used under the hood] to Fortran’s run-time system).
I think the way you are wiring your procedure pointers would then correspond to using a dependency injection container in an OO language (that associates or registers polymorphic interfaces to concrete class implementations).
An interesting project, in any case.
Hi kkifonidis,
Thanks for the welcome and the feedback!
Standard OOP is of course great because the compiler can find unassigned procedure pointers (disguised as type members) at compile time—such as a missing implementation of an abstract method.
I actually rewrote the architecture of this project several times. Initially, I tried a standard OOP factory pattern, but I found that passing dependency objects down to every sub-function cluttered the code. It also required select type constructs when a more concrete subtype is needed.
In general, it would be great to make proper use of composition—for example, composing the Hamiltonian out of potential and kinetic operators, where the kinetic operator is composed of the grid and some differential operators and so on. That way the dependencies would be much cleaner. Currently, the module dependencies in CodyFortran are a bit of a mess.
However, since components like the grid are effectively singletons (there is only one grid), I decided to go with the manual procedure pointer approach and only use standard OOP for cases where I actually need multiple instances, such as having multiple integrators for a split-step propagator. For a hobby project, it’s pleasant to work with—and as long as the function pointers aren’t called inside tight inner loops (where they might hinder compiler optimizations), the performance has been quite good.
Thanks again for the thoughts!