petekSim
A fast field/discovery appraisal toolkit — a pure-Rust reservoir core with
thin Python bindings (peteksim). peteksim is the single Python-facing facade
over the whole subsurface-modelling stack (ingest → geomodel → volumetrics →
uncertainty): from a Petrel export to a STOIIP P-curve + tornado in a handful of
calls.
Why: a geoscientist should get from data to a defensible in-place P-curve without wiring loaders, gridders, geostatistics and a Monte-Carlo loop together by hand. petekSim presents the whole stack as declarative specs applied at a few explicit moments — the compute lives in Rust; you describe what you want.
Everything is SI/metric (decision_si_units_standard): areas in km²,
lengths/depths in metres (positive-down), volumes in Sm³ (reported in MSm³ for oil,
bcm for gas), GRV in mcm (10⁶ m³), FVF as dimensionless Rm³/Sm³. Imperial is opt-in
conversion on your side, never a default.
Documentation
The canonical docs for the whole petek family live on the petekSuite site — peteksim's pages there:
- Library guide — the peteksim guide.
- Tutorials — Simulation & uncertainty · Static model build (flagship) (driven through the
peteksimfacade). - Notebooks — executed examples: full workflow · scenarios & uncertainty.
Install
The wheel pulls its family dependencies (petektools) automatically. Rust
consumers: cargo add peteksim.
Building from source (contributors)
VIRTUAL_ENV="/.venv-srs"
First volumes — a STOIIP P-curve in a handful of calls
The primary surface is the declarative spec API. A spec is an immutable
value that says WHAT (Horizons, Subzones, Layering, Contacts, Props,
Mc) or HOW (TieSettings, Gridding, Run); it holds names, not project
objects, resolved at apply time — so a spec is project-independent, reusable across
re-exports and synthetic assets, serializes to/from a dict (a scenario is a savable
file), compares by value, derives with .replace(), and pretty-prints as its
domain table. Applications are explicit moments (geom.build, grid.model,
model.zoned_uncertainty); errors at apply are loud, naming both the missing
project object and the spec entry.
=
# Declarative structure + settings (names, not objects).
=
=
=
=
# The explicit application moments.
=
=
=
=
# {p90, p50, p10, mean, *_msm3, samples} — the P-curve
# per-zone breakdown (a contactless zone contributes zero HC)
Scenarios are derived specs — same geometry, N specs → N models:
=
=
Every spec ships value semantics (to_dict/from_dict, ==/hash, .replace,
table repr); a scenario round-trips through ps.spec_from_dict(spec.to_dict()),
and ps.AssetSpec bundles a whole scenario (load + structure + props + mc) into one
durable value.
Multi-zone stacks
A multi-horizon stack (declare more zones between more ps.hz rows) unlocks
per-zone layering + contacts, optional per-zone property pipelines, and per-zone
Monte Carlo — a contactless zone contributes GRV with zero hydrocarbon; per-zone
and total P-curves are both reachable. model.in_place_by_zone(),
model.zone_stats("PORO") and model.well_tie_residuals() report the breakdown.
Run resources + out-of-core
Pass a ps.Run to carry the run resources — workers shards the MC realize loop,
memory_budget (bytes) forwards to the engine's out-of-core switch (a
larger-than-memory model spills to disk with a loud notice, never an OOM kill):
=
The analytic box model — a quick estimate
Before a full project, a box model gives a first P-curve with Monte-Carlo on the volumetric inputs (all SI: area km², depths m positive-down, FVF Rm³/Sm³):
=
# P90 / P50 / P10 / mean / deterministic [Sm³]
# the same percentiles in MSm³ (gas: summary_bcm)
# the full per-realization in-place vector [Sm³]
# opens the viewer (background server; returns at once)
# ...or a structured box with real relief, built in code (km², m):
=
# a structural high (depth in m)
Each volumetric input accepts a number (constant), a (min, mode, max) triangular,
or a tagged dict — {"normal": [mean, sd]}, {"lognormal": [mu, sigma]},
{"uniform": [lo, hi]}, {"triangular": [lo, mode, hi]}.
The viewer — Map · Intersection · Volume
model.view() opens a tabbed, bundle-driven inspection viewer in the browser:
- Map — areal rasters (horizon depth / property zone-average / k-slice) with outline, contact subcrop masks, well markers, pan/zoom + hover; draw a fence line or click a well to cut a section.
- Intersection — the vertical cross-section (per-layer property fills, horizon
- contact traces, bore-path overlay, vertical-exaggeration slider).
- Volume — the corner-point mesh (three.js): property colouring, threshold slider, zone toggles, i/j/k clip planes, orbit.
view() is non-blocking (a background local server prints its URL and returns;
view(block=True) for the old hold-until-Ctrl-C behaviour). model.save_view("m.html")
writes one self-contained HTML file that opens straight off file:// — no
server, no network, all data + JS inlined (confidential-data safe). The bundle
accessors model.map_bundle(...) / intersection_bundle(...) / volume_bundle(...)
return the JSON dicts directly. Full guide: VIEWER.md.
./view.sh builds the extension and opens the viewer in one step
(./view.sh --box for the Monte Carlo box model). See examples/build_and_view.py.
Migrating from v1
Earlier versions used an eight-call staged chain (proj.framework(...) →
set_zones → build_grid → per-property upscale/propagate → grid.model →
uncertainty → tornado). It is deprecated (window: two minors) in favour of
the declarative API above — it keeps working and emits a DeprecationWarning.
Replace proj.framework(horizons=[...]) with
proj.grid_geometry(...).build(ps.Horizons(ps.hz(...), zones=[...])), and the
per-property upscale/propagate calls with a ps.Props(ps.Prop(...)) spec passed
to grid.model(props=...). The runnable staged example is examples/staged_build.py.
Licensing
petekSim is licensed under the Business Source License 1.1 — see LICENSE. Non-production use is freely granted; production use is permitted by the Additional Use Grant except as a competing commercial "as-a-service" offering of the Licensed Work's functionality. Each released version converts to the Change License (Apache-2.0) four years after its first publication. For alternative licensing, contact kkollsga@gmail.com.
Contributing
Building petekSim itself — the crate workspace, the build/test gates, the acceptance suite, and the planning-graph/inbox workflow — is documented in CONTRIBUTING.md. Design and architecture live in SPEC.md; the locked public API is API.md.