The day-to-day users of a petroleum model are rarely the engineers who maintain it: they're reservoir engineers in notebooks, workflows calling property math mid-simulation, ML pipelines needing fluid properties at scale. This post is about the interface those consumers want — and the engineering realities of the source code that constrain it.
What petroleum models look like from the outside
Typical legacy decks of this class: a small handful of configuration-like entry points (initialize a fluid at equivalent depths, set a reservoir temperature) followed by question-like routines (bubble point at this pressure? viscosity of this oil? relative permeability? Peaceman well index for this geometry?). The calls are cheap; the semantics are the session: state persists between them.
What the Python surface looks like
Exposed through nativegate, the same routines become an importable package where a notebook does:
Typed NumPy arguments in, typed results out. No input-deck editing, no file intermediates, no Fortran knowledge required — while underneath, the same compiled correlations answer, verified against the golden record.
The engineering realities that shape the API
- State is a session, not a singleton. COMMON-block state means configure-then-query. Sequential calls per session; sessions per process. Document it, or concurrency will invent a worse version.
- Units are the API. The discipline's units conventions — pressures, depths, densities — are embedded in the routines. Exposing them unchanged is safer than abstracting them: the exposed surface should not add a second unit system to a modernization.
- Regression is the feature. The listeners of this API plan wells. The recorded baseline is what makes every change to the surface — new wrapper, new compiler — the same meaning of "unchanged."
Over HTTP, at the second consumer
The same surface becomes HTTP the moment someone other than the authoring team needs it: operations engineers calling from web tools, or another simulator's preprocessor. The generated service adds the API-key auth, rate limiting, and health probes, and carries the same locked-for-correctness semantics — one native call at a time per process, so replicas, not workers, carry the concurrency.
The point worth repeating from each of the petroleum posts: nothing here required rewriting the correlations. The modernized part is who can call them.