nativegate
← Blog

From a Fortran reservoir model to a Python API

Claims about modernization tools are cheap, so this post is a walkthrough of the example committed in the repository rather than a hypothetical. The source: libraries/petro/, roughly 4,800 lines — seven fixed-form F77 decks written between 1988 and 2004 (black-oil PVT correlations and rock-property routines: bubble point, viscosity, oil relative permeability, Peaceman well indices, and neighbors), behind a 2003 F90 facade. The target: an importable Python package and a verified FastAPI service. The rule: the Fortran is never edited.

What the decks actually contain

Fixed-form F77 with COMMON blocks and INCLUDE decks. Routines like PVTRS and KROIL are typed functions — DOUBLE PRECISION FUNCTION — which matters because naive discovery patterns only allow a single token before FUNCTION and skip every one of them. The original nativegate run had exactly that defect; it found 25 of 49 routines and generated a service exposing half the API with no warning. That number — 25/49 → 49/49 — is in the repo's findings file, along with the fix.

The pipeline, honestly narrated

end to end
$ ngate build petro_api$ pip install services/petro_api/dist/*.whl$ ngate serve petro_api127.0.0.1:8000$ ngate golden record petro_api && ngate golden verify petro_api4 entry point(s) unchanged (0 not covered).

Underneath the build, the unmodified decks get a generated, expanded copy — INCLUDEs spliced, parameterized kinds resolved — and f2py produces the extension. Under serve, an ordinary FastAPI app answers HTTP with the same numbers the native binary returns. Under golden verify, recorded outputs of known configurations are replayed and compared.

The two details that decide correctness

COMMON blocks mean one call at a time

The exposed routines share process-global COMMON storage — that is how the deck was designed. Two simultaneous calls would silently hand each other's state back. The generated service therefore holds a process-wide lock across each native call for Fortran services. Throughput comes from running more processes, not more concurrency within one. This is a property of the physics of the old design, not a limitation of the wrapper.

The golden record is the deliverable

services/petro_api/golden.json is committed: the fluid configured at a set of benchmark states and the results the bindings return. Every regeneration, compiler change, or flag change gets measured against it. The other artifacts — Dockerfile, Kubernetes manifests, health probes — matter for deployment, but the golden record is what makes the modernization itself defensible.

What it adds up to

A 1988-era correlation deck, unmodified, that a data scientist imports and calls from a notebook; that another team calls over HTTP in a container; that CI proves numerically unchanged after every change. That is modernization without a rewrite, and it generalizes: your deck, whatever domain it models, follows the same path.

Reproduce it from the repo → Next: deploying legacy models as APIs