# petekIO
The subsurface **data layer** — a Rust library (with optional PyO3 bindings)
that turns raw subsurface files into clean, validated, interpreted data:
**surfaces, wells (trajectories / tops / logs), points, and polygons**, with
loading, mnemonic and unit normalisation, validation, petrophysical
interpretation, interpolation, and statistics.
The pipeline is the point:
**ingest → normalize → validate → interpret → characterise**
## Documentation
The canonical docs for the whole petek family live on the **petekSuite site**
— petekIO's pages there:
- **[Library guide](https://peteksuite.readthedocs.io/en/latest/libraries/petekio/)** — the petekIO guide.
- **Tutorials** — [Data ingest with petekIO](https://peteksuite.readthedocs.io/en/latest/tutorials/data-ingest/) · [Well analysis](https://peteksuite.readthedocs.io/en/latest/tutorials/well-analysis/).
- **[Notebooks](https://peteksuite.readthedocs.io/en/latest/notebooks/)** — executed examples: [ingest tour](https://peteksuite.readthedocs.io/en/latest/notebooks/petekio/01_ingest_tour/) · [well analysis](https://peteksuite.readthedocs.io/en/latest/notebooks/petekio/02_well_analysis/).
## Why build on it
Subsurface data is the unglamorous, error-prone groundwork under every reservoir
application: vendor LAS mnemonics, mismatched units, out-of-range samples,
cutoffs, gridding that has to honour its control points, uncertainty. petekIO
does that work once and behind a stable API, so the application on top stays thin
and stays in its own domain:
- **The whole path, not just parsing.** Files in; normalized, validated,
interpreted domain objects out — no re-implementing LAS aliasing, unit
harmonisation, petrophysical cutoffs (net pay included), or surface
gridding/resampling further up the stack.
- **Values know what they are.** Results come back in canonical units, each
carrying an uncertainty distribution and a provenance flag (measured /
interpolated / defaulted) — so downstream code *propagates* uncertainty rather
than re-deriving it.
- **A substrate, not a grab-bag.** Load a project once into a `GeoData` and
operations broadcast across the whole collection. Immutable, strictly layered,
fluent.
- **Rust core, thin Python.** Fast and embeddable, with PyO3 bindings that mirror
the Rust API.
## Install
Rust:
```toml
[dependencies]
petekio = "0.3"
```
Python (PyO3 wheel):
```bash
pip install petekio
```
## Quickstart (Python)
Import raw source data once, then read interpreted results — no parsing or
interpolation in your own code. Save/load is reserved for compact `.pproj`
projects:
```python
import petekio
project = petekio.Project.import_data(
"Data",
settings=petekio.ImportSettings(
crs="EPSG:32631",
aliases={"por": ["PHIE", "PORO"]},
),
)
project.inventory()
geo = project.geodata
project.rename_surface("Top reservoir", "structure/top agat")
project.surfaces.structure.top_agat
project.save("field.pproj")
project = petekio.Project.load("field.pproj")
# Or build the same substrate manually:
geo = petekio.GeoData(unit="m")
# A surface (IRAP classic) — sample, stats, volumetrics, resample.
top = geo.load_surface("top_res", "surfaces/top_res.irap")
top.stats.mean
top.area_below(2400)
# A multi-bore well: a Petrel export tree (one bore per .wellpath) + logs.
# head/kb are optional — the .wellpath header fills them.
geo.load_well("15/9-A1", files="wells/15_9-A1/")
geo.load_well_tops("WellTops.tops") # Horizon picks → matching well + bore
w = geo.well("15/9-A1")
w.bores() # e.g. ["", "A", "B", "ST2"]
bore = w.sidetrack("A")
bore.log_stats("PHIE").mean # whole-bore curve stats
# Per-zone stats, returned in lithostratigraphic order:
bore.zone_stats("PHIE") # [(zone, Stats), ...]
bore.zone_stats("PHIE", "Top A").mean # one zone directly (None if absent)
geo.strat_order # the field's lithostratigraphic column
# A tidy per-zone×bore table (pandas; pip install petekio[pandas]):
w.zone_table("PHIE", stats=("mean", "p50")) # DataFrame, zone in lithostrat order
```
### Lithostratigraphic ordering
Zones come back in true stratigraphic order, not just measured-depth order.
`load_well_tops` reads **every** well in the tops file and merges their relative
orderings into one field-wide column — so a marker that pinches out (zero
thickness) in one well is ordered correctly by a well that develops it.
Geometry is untouched; only the *order* zones are presented in follows the
column.
## Capabilities
| **Surfaces** | IRAP-classic load, sample/resample (bilinear), edge polygons, arithmetic, stats, `area_below` volumetrics, gridding from scattered points (minimum-curvature) |
| **Wells** | Positioned `.wellpath` trajectories (MD preserved; minimum-curvature interpolation), multi-bore (sidetracks), imported logs stored as MD/value pairs with mnemonic aliasing, Petrel well-tops, per-zone stats, field-wide lithostratigraphic ordering |
| **Points / polygons** | IRAP / GeoJSON / CSV load, strict regular-grid geometry inference, clip, point-to-surface gridding |
| **Project** | `GeoData` substrate — import raw data once, broadcast across the collection; views are read-only filtered subsets; compact `.pproj` load/save |
## Built in gates
petekIO grows in **gated phases** against a locked contract: every public
signature is specified in [`API.md`](API.md) (a change needs sign-off), and the
design + build roadmap live in [`SPEC.md`](SPEC.md).
> **Status:** early development. The public API is locked and the core data path
> (ingest → normalize → validate → interpret → characterise) is in place;
> surfaces, multi-bore wells (trajectories / tops / logs), per-zone stats, and
> lithostratigraphic ordering are landed. Breadth is still filling in — more
> ingest formats, fluid contacts, richer interpretation.
## Documentation
- **[API.md](API.md)** — the locked public API contract (Rust, mirrored in Python).
- **[SPEC.md](SPEC.md)** — design constitution + architecture.
- Guides + API reference: the `docs/` site (MkDocs Material; published on Read
the Docs).
## Design at a glance
- **Strictly layered, one-way deps:** `foundation → algorithms → io → core →
analysis → manager → py`.
- **A manager substrate** (`GeoData`): load once, operations broadcast across the
collection — no per-item loops.
- **Domain objects carry their operations** (arithmetic, filters, interpolation,
stats) and expose `history()` for generated objects — fluent and chainable;
immutable (ops return new objects).
- **Algorithms are isolated, QC-able kernels** grouped by discipline (e.g. the
minimum-curvature survey, the cross-well stratigraphic merge) — pure and
type-light.
- **Rust core + thin PyO3**; the Python API mirrors the Rust API.
## Built on
- **petekTools** — standalone numerics / geostatistics kernels (gridding,
interpolation) that petekIO builds on.
## License
Apache-2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE).