peteksim 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- peteksim-0.1.0/.cargo/config.toml +9 -0
- peteksim-0.1.0/.github/workflows/ci.yml +84 -0
- peteksim-0.1.0/.github/workflows/release.yml +77 -0
- peteksim-0.1.0/.gitignore +29 -0
- peteksim-0.1.0/API.md +311 -0
- peteksim-0.1.0/CHANGELOG.md +128 -0
- peteksim-0.1.0/CLAUDE.md +95 -0
- peteksim-0.1.0/CONTRIBUTING.md +128 -0
- peteksim-0.1.0/Cargo.lock +1483 -0
- peteksim-0.1.0/Cargo.toml +84 -0
- peteksim-0.1.0/LICENSE +91 -0
- peteksim-0.1.0/Makefile +49 -0
- peteksim-0.1.0/PKG-INFO +20 -0
- peteksim-0.1.0/README.md +193 -0
- peteksim-0.1.0/SPEC.md +158 -0
- peteksim-0.1.0/VIEWER.md +83 -0
- peteksim-0.1.0/crates/srs-py/Cargo.toml +25 -0
- peteksim-0.1.0/crates/srs-py/src/facade/framework.rs +358 -0
- peteksim-0.1.0/crates/srs-py/src/facade/grid.rs +319 -0
- peteksim-0.1.0/crates/srs-py/src/facade/mod.rs +83 -0
- peteksim-0.1.0/crates/srs-py/src/facade/model.rs +713 -0
- peteksim-0.1.0/crates/srs-py/src/facade/project.rs +367 -0
- peteksim-0.1.0/crates/srs-py/src/facade/specs.rs +279 -0
- peteksim-0.1.0/crates/srs-py/src/facade/uncertainty.rs +374 -0
- peteksim-0.1.0/crates/srs-py/src/lib.rs +689 -0
- peteksim-0.1.0/crates/srs-py/src/viewer.rs +603 -0
- peteksim-0.1.0/crates/srs-py/tests/acceptance_render.mjs +85 -0
- peteksim-0.1.0/crates/srs-py/tests/conftest.py +32 -0
- peteksim-0.1.0/crates/srs-py/tests/test_acceptance.py +681 -0
- peteksim-0.1.0/crates/srs-py/tests/test_acceptance_v2.py +266 -0
- peteksim-0.1.0/crates/srs-py/tests/test_facade.py +395 -0
- peteksim-0.1.0/crates/srs-py/tests/test_peteksim.py +412 -0
- peteksim-0.1.0/crates/srs-py/tests/test_realshape.py +429 -0
- peteksim-0.1.0/crates/srs-py/tests/test_scatter_dedup.py +115 -0
- peteksim-0.1.0/crates/srs-py/tests/test_spec_conformance.py +187 -0
- peteksim-0.1.0/crates/srs-py/tests/test_synth_asset.py +609 -0
- peteksim-0.1.0/crates/srs-py/tests/test_viewer.py +192 -0
- peteksim-0.1.0/crates/srs-py/tests/test_volume_recut.py +93 -0
- peteksim-0.1.0/crates/srs-py/tests/test_wells_payload.py +180 -0
- peteksim-0.1.0/crates/srs-py/tests/test_zonation.py +230 -0
- peteksim-0.1.0/pyproject.toml +41 -0
- peteksim-0.1.0/python/peteksim/__init__.py +209 -0
- peteksim-0.1.0/python/peteksim/apply.py +458 -0
- peteksim-0.1.0/python/peteksim/specs/__init__.py +89 -0
- peteksim-0.1.0/python/peteksim/specs/asset.py +119 -0
- peteksim-0.1.0/python/peteksim/specs/base.py +173 -0
- peteksim-0.1.0/python/peteksim/specs/mc.py +159 -0
- peteksim-0.1.0/python/peteksim/specs/props.py +150 -0
- peteksim-0.1.0/python/peteksim/specs/settings.py +163 -0
- peteksim-0.1.0/python/peteksim/specs/structure.py +320 -0
- peteksim-0.1.0/python/peteksim/synth_asset.py +790 -0
- peteksim-0.1.0/rustfmt.toml +2 -0
- peteksim-0.1.0/src/core/charts/bins.rs +103 -0
- peteksim-0.1.0/src/core/charts/crossplot.rs +206 -0
- peteksim-0.1.0/src/core/charts/distribution.rs +122 -0
- peteksim-0.1.0/src/core/charts/mod.rs +171 -0
- peteksim-0.1.0/src/core/charts/regression.rs +97 -0
- peteksim-0.1.0/src/core/charts/tornado.rs +110 -0
- peteksim-0.1.0/src/core/data_adapter.rs +112 -0
- peteksim-0.1.0/src/core/facade/charts.rs +148 -0
- peteksim-0.1.0/src/core/facade/framework.rs +1042 -0
- peteksim-0.1.0/src/core/facade/grid.rs +806 -0
- peteksim-0.1.0/src/core/facade/mod.rs +29 -0
- peteksim-0.1.0/src/core/facade/model.rs +658 -0
- peteksim-0.1.0/src/core/facade/project/helpers.rs +413 -0
- peteksim-0.1.0/src/core/facade/project/mod.rs +643 -0
- peteksim-0.1.0/src/core/facade/spec.rs +242 -0
- peteksim-0.1.0/src/core/facade/uncertainty.rs +285 -0
- peteksim-0.1.0/src/core/facade/wells.rs +462 -0
- peteksim-0.1.0/src/core/facade/zonation.rs +180 -0
- peteksim-0.1.0/src/core/facade/zoned_mc.rs +700 -0
- peteksim-0.1.0/src/core/inplace.rs +199 -0
- peteksim-0.1.0/src/core/mod.rs +66 -0
- peteksim-0.1.0/src/core/model.rs +50 -0
- peteksim-0.1.0/src/core/refine.rs +374 -0
- peteksim-0.1.0/src/core/run.rs +134 -0
- peteksim-0.1.0/src/core/view.rs +113 -0
- peteksim-0.1.0/src/lib.rs +33 -0
- peteksim-0.1.0/src/pvt/fvf.rs +92 -0
- peteksim-0.1.0/src/pvt/mod.rs +8 -0
- peteksim-0.1.0/src/units/error.rs +39 -0
- peteksim-0.1.0/src/units/mod.rs +10 -0
- peteksim-0.1.0/view.sh +31 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# PyO3 builds `srs-py` as a cdylib that resolves CPython symbols at load time.
|
|
2
|
+
# Plain `cargo build`/`cargo test` (no maturin) must tell the macOS linker to
|
|
3
|
+
# defer those symbols, or the link fails with "_Py* not found". Maturin sets
|
|
4
|
+
# this itself; this config makes the bare cargo gate work too.
|
|
5
|
+
[target.x86_64-apple-darwin]
|
|
6
|
+
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
7
|
+
|
|
8
|
+
[target.aarch64-apple-darwin]
|
|
9
|
+
rustflags = ["-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup"]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
env:
|
|
9
|
+
CARGO_TERM_COLOR: always
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
rust:
|
|
13
|
+
name: cargo build / clippy / test
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
18
|
+
with:
|
|
19
|
+
components: clippy, rustfmt
|
|
20
|
+
- uses: Swatinem/rust-cache@v2
|
|
21
|
+
- name: fmt
|
|
22
|
+
run: cargo fmt --all --check
|
|
23
|
+
- name: clippy (warnings = errors)
|
|
24
|
+
run: cargo clippy --workspace --all-targets -- -D warnings
|
|
25
|
+
- name: build
|
|
26
|
+
run: cargo build --workspace
|
|
27
|
+
- name: test
|
|
28
|
+
run: cargo test --workspace
|
|
29
|
+
|
|
30
|
+
python:
|
|
31
|
+
name: maturin develop + import
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v4
|
|
35
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.11"
|
|
39
|
+
- name: install maturin
|
|
40
|
+
run: pip install maturin
|
|
41
|
+
- name: build + import smoke test
|
|
42
|
+
run: |
|
|
43
|
+
maturin develop -m crates/srs-py/Cargo.toml
|
|
44
|
+
python -c "import peteksim; print(peteksim.version())"
|
|
45
|
+
|
|
46
|
+
wheels:
|
|
47
|
+
# Build the abi3 wheel and import + run it across a Python matrix so a
|
|
48
|
+
# toolchain regression (e.g. a pyo3 that can't link a new CPython — the
|
|
49
|
+
# 0.24-vs-3.14 blocker) is caught here, not by users. abi3-py39 means one
|
|
50
|
+
# wheel spans 3.9+, but we still build AND import on each interpreter so a
|
|
51
|
+
# per-version link/ABI break surfaces.
|
|
52
|
+
name: wheel build + run (py ${{ matrix.python-version }})
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
strategy:
|
|
55
|
+
fail-fast: false
|
|
56
|
+
matrix:
|
|
57
|
+
# 3.9 = oldest abi3 floor; through the latest stable CPython.
|
|
58
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
59
|
+
steps:
|
|
60
|
+
- uses: actions/checkout@v4
|
|
61
|
+
- uses: dtolnay/rust-toolchain@stable
|
|
62
|
+
- uses: Swatinem/rust-cache@v2
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: ${{ matrix.python-version }}
|
|
66
|
+
allow-prereleases: true
|
|
67
|
+
- name: install maturin
|
|
68
|
+
run: pip install maturin
|
|
69
|
+
- name: build release wheel
|
|
70
|
+
run: maturin build --release -m crates/srs-py/Cargo.toml --out dist
|
|
71
|
+
- name: install wheel + run run_box_model
|
|
72
|
+
run: |
|
|
73
|
+
pip install --find-links dist --no-index peteksim
|
|
74
|
+
python - <<'PY'
|
|
75
|
+
import peteksim
|
|
76
|
+
r = peteksim.run_box_model(
|
|
77
|
+
area_acres=(80, 100, 130), gross_height_ft=(40, 50, 65),
|
|
78
|
+
porosity=0.25, net_to_gross=0.8, water_saturation=0.3, fvf=1.25,
|
|
79
|
+
fluid="oil", contact_depth_ft=9000, realizations=2000, seed=1,
|
|
80
|
+
)
|
|
81
|
+
assert r.p90 < r.p50 < r.p10, (r.p90, r.p50, r.p10)
|
|
82
|
+
assert len(r.samples) == r.realizations == 2000
|
|
83
|
+
print("peteksim", peteksim.version(), "ok:", r)
|
|
84
|
+
PY
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Tag-triggered PyPI publish (v* tags) via OIDC trusted publishing — no secret.
|
|
4
|
+
# The `peteksim` wheel is built from crates/srs-py (its pyproject.toml). The
|
|
5
|
+
# crates.io publish of the consolidated `peteksim` library crate is done from the
|
|
6
|
+
# local runbook (ordered, cross-repo deps), not here.
|
|
7
|
+
#
|
|
8
|
+
# Configure a pending publisher on PyPI: project `peteksim`, owner kkollsga, repo
|
|
9
|
+
# peteksim, workflow release.yml, environment pypi.
|
|
10
|
+
#
|
|
11
|
+
# NOTE: before pushing a v* tag, the sibling path dep on `petekstatic`
|
|
12
|
+
# (`../petekStatic` in the root Cargo.toml [workspace.dependencies]) must resolve
|
|
13
|
+
# from crates.io — its version pin (0.1.0) does; by release time the upstream
|
|
14
|
+
# family crates are all published so the registry pins resolve on the CI runner.
|
|
15
|
+
on:
|
|
16
|
+
push:
|
|
17
|
+
tags: ["v*"]
|
|
18
|
+
workflow_dispatch:
|
|
19
|
+
|
|
20
|
+
permissions:
|
|
21
|
+
contents: read
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
sdist:
|
|
25
|
+
name: build sdist
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: PyO3/maturin-action@v1
|
|
30
|
+
with:
|
|
31
|
+
command: sdist
|
|
32
|
+
args: --out dist
|
|
33
|
+
working-directory: crates/srs-py
|
|
34
|
+
- uses: actions/upload-artifact@v4
|
|
35
|
+
with:
|
|
36
|
+
name: wheels-sdist
|
|
37
|
+
path: crates/srs-py/dist
|
|
38
|
+
|
|
39
|
+
wheels:
|
|
40
|
+
name: build wheels ${{ matrix.runner }} ${{ matrix.target }}
|
|
41
|
+
runs-on: ${{ matrix.runner }}
|
|
42
|
+
strategy:
|
|
43
|
+
fail-fast: false
|
|
44
|
+
matrix:
|
|
45
|
+
include:
|
|
46
|
+
- { runner: ubuntu-latest, target: x86_64 }
|
|
47
|
+
- { runner: ubuntu-latest, target: aarch64 }
|
|
48
|
+
- { runner: macos-14, target: universal2-apple-darwin }
|
|
49
|
+
- { runner: windows-latest, target: x64 }
|
|
50
|
+
steps:
|
|
51
|
+
- uses: actions/checkout@v4
|
|
52
|
+
- uses: PyO3/maturin-action@v1
|
|
53
|
+
with:
|
|
54
|
+
target: ${{ matrix.target }}
|
|
55
|
+
args: --release --out dist
|
|
56
|
+
sccache: "true"
|
|
57
|
+
manylinux: auto
|
|
58
|
+
working-directory: crates/srs-py
|
|
59
|
+
- uses: actions/upload-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: wheels-${{ matrix.runner }}-${{ matrix.target }}
|
|
62
|
+
path: crates/srs-py/dist
|
|
63
|
+
|
|
64
|
+
publish-pypi:
|
|
65
|
+
name: PyPI (trusted publishing)
|
|
66
|
+
needs: [sdist, wheels]
|
|
67
|
+
runs-on: ubuntu-latest
|
|
68
|
+
environment: pypi
|
|
69
|
+
permissions:
|
|
70
|
+
id-token: write
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/download-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
pattern: wheels-*
|
|
75
|
+
path: dist
|
|
76
|
+
merge-multiple: true
|
|
77
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Local working folders + internal tooling (not part of the published library)
|
|
2
|
+
/dev-docs/
|
|
3
|
+
/inbox/
|
|
4
|
+
/research/
|
|
5
|
+
/tools/
|
|
6
|
+
/notes/
|
|
7
|
+
/.claude/
|
|
8
|
+
/.mcp.json
|
|
9
|
+
/CLAUDE.local.md
|
|
10
|
+
/Makefile.local
|
|
11
|
+
|
|
12
|
+
# Python env / caches
|
|
13
|
+
/.venv-srs/
|
|
14
|
+
**/__pycache__/
|
|
15
|
+
*.pyc
|
|
16
|
+
|
|
17
|
+
# Build artifacts
|
|
18
|
+
/target/
|
|
19
|
+
**/*.rs.bk
|
|
20
|
+
*.tmp
|
|
21
|
+
|
|
22
|
+
# maturin editable-install artifact (built extension placed in the source pkg)
|
|
23
|
+
crates/srs-py/python/peteksim/_core*.so
|
|
24
|
+
|
|
25
|
+
# macOS
|
|
26
|
+
.DS_Store
|
|
27
|
+
**/.DS_Store
|
|
28
|
+
dist/
|
|
29
|
+
dist-launch/
|
peteksim-0.1.0/API.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# petekSim — locked public API (`peteksim`)
|
|
2
|
+
|
|
3
|
+
> **This file is the contract.** The `peteksim` wheel must expose exactly these
|
|
4
|
+
> names and signatures (arguments, defaults, return shapes). Bodies are the
|
|
5
|
+
> implementer's; the *surface* is fixed. Changing a signature here requires
|
|
6
|
+
> sign-off (coordinator + any downstream consumer for a cross-library seam) and an
|
|
7
|
+
> edit to this file — the code must never silently drift from it. See
|
|
8
|
+
> [SPEC.md](SPEC.md) for the design constitution.
|
|
9
|
+
|
|
10
|
+
**Rust is canonical; the Python surface mirrors it.** The compute lives in the Rust
|
|
11
|
+
core (`peteksim._core`, over the petekStatic/petekIO/petekTools crates); this
|
|
12
|
+
document specifies the **Python facade** — the product surface. The v2 spec layer is
|
|
13
|
+
a thin Python facade over `_core`; a handful of names (`Project`, `collocated`)
|
|
14
|
+
override the raw `_core` binding with a v2 wrapper.
|
|
15
|
+
|
|
16
|
+
**Conventions:**
|
|
17
|
+
- **SI / metric everywhere** (`decision_si_units_standard`): areas km², lengths /
|
|
18
|
+
depths **metres, positive-down**, volumes Sm³ (reported MSm³ oil / bcm gas), GRV
|
|
19
|
+
mcm (10⁶ m³), FVF dimensionless Rm³/Sm³. Imperial is caller-side, never a default.
|
|
20
|
+
- **A spec holds NAMES, resolved at apply.** A spec value is declarative and
|
|
21
|
+
project-independent: it references horizons / surfaces / picks / properties by
|
|
22
|
+
name, and those names are resolved against a loaded project only at the apply
|
|
23
|
+
moment (`geom.build`, `grid.model`, `model.zoned_uncertainty`). Resolution errors
|
|
24
|
+
are loud and name **both** the missing project object and the spec entry.
|
|
25
|
+
- **Value semantics.** Every spec supports `to_dict()` / `from_dict()` /
|
|
26
|
+
`ps.spec_from_dict()`, value `==` + `hash`, `.replace(...)` derivation, and a
|
|
27
|
+
domain-table `repr`. A scenario is a savable, diffable file.
|
|
28
|
+
- **`import peteksim as ps`** throughout.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Module
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
ps.version() -> str # the peteksim/crate version string
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Exceptions** (both `from peteksim import ...`):
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
ps.NotYetSupported(NotImplementedError) # a spec field serializes but the engine
|
|
42
|
+
# capability has not landed — raised loudly
|
|
43
|
+
# at apply, naming the carrying task
|
|
44
|
+
ps.ApplyError(ValueError) # a spec could not resolve against the
|
|
45
|
+
# project (missing name / illegal combo);
|
|
46
|
+
# the message names object AND spec entry
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Project & geometry
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
ps.Project.load(path: str, crs: str | None = None,
|
|
53
|
+
aliases: dict[str, str] | None = None,
|
|
54
|
+
settings: ps.LoadSettings | None = None) -> Project
|
|
55
|
+
# Load a project tree. Pass settings=ps.LoadSettings(...) (v2) OR the legacy
|
|
56
|
+
# crs=/aliases= kwargs — not both (ApplyError on both).
|
|
57
|
+
|
|
58
|
+
proj.grid_geometry(cell, extent=None, orient: float = 0.0) -> GridGeometry
|
|
59
|
+
# Open the v2 build. `cell` is the output cell size (scalar or (dx, dy);
|
|
60
|
+
# anisotropic dx != dy -> NotYetSupported). `orient` must be 0 (rotation is
|
|
61
|
+
# NotYetSupported). `extent` is advisory.
|
|
62
|
+
|
|
63
|
+
# v1 / shared project accessors (forwarded unchanged):
|
|
64
|
+
proj.inventory() -> Inventory # what loaded + what was skipped-with-reason
|
|
65
|
+
proj.wells() -> Wells # .ids(), .heads() -> [(id, x, y)]
|
|
66
|
+
proj.surface(name: str) -> Surface # .value_at(x, y), .values_at(points)
|
|
67
|
+
proj.tops -> Tops # .pick(name, wells=None) -> TopsPick
|
|
68
|
+
proj.crossplot_bundle(x, y, wells=None, color_by="well",
|
|
69
|
+
x_log=False, y_log=False, regression=False)
|
|
70
|
+
proj.framework(...) -> Framework # DEPRECATED (v1 chain); see "v1" below
|
|
71
|
+
|
|
72
|
+
geom.build(horizons: Horizons, subzones: Subzones | None = None,
|
|
73
|
+
layering: Layering | None = None, collapse_negative: bool = True,
|
|
74
|
+
outline: str = "ModelEdge", min_thickness_m: float = 0.0,
|
|
75
|
+
ties: TieSettings | None = None,
|
|
76
|
+
gridding: Gridding | None = None) -> Grid
|
|
77
|
+
# Freeze geometry + structure specs. Resolves horizon names early (loud on a
|
|
78
|
+
# miss); the engine build is deferred to grid.model so contacts + props land
|
|
79
|
+
# in one construction. `ties`/`gridding` default to the ones on `horizons`.
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Structure specs
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
ps.Horizons(*rows: HorizonRow, zones=None, ties=None, gridding=None) -> Horizons
|
|
86
|
+
# The ordered stratigraphic column (top->down) + the zones between horizons;
|
|
87
|
+
# zone i sits between rows[i] and rows[i+1]. .replace("H1"|glob, surface=...)
|
|
88
|
+
# derives a changed column.
|
|
89
|
+
ps.hz(name: str, surface: str | None = None, tie: str | None = None,
|
|
90
|
+
sd: float = 0.0, vgm: tuple[str, float] | None = None) -> HorizonRow
|
|
91
|
+
# One horizon. `surface` defaults to `name` (a loaded point-set -> Scatter, a
|
|
92
|
+
# loaded grid -> Mapped). `tie` names the pick set (defaults to `name`).
|
|
93
|
+
# `sd`(m) + `vgm`=(model, range) declare the structural-uncertainty field
|
|
94
|
+
# (applied through the zoned MC path).
|
|
95
|
+
|
|
96
|
+
ps.Subzones(mapping: dict[str, Split] | None = None) -> Subzones # per-zone splits
|
|
97
|
+
ps.splits(*entries, conformity: str = "proportional") -> Split
|
|
98
|
+
# each entry: a name, or (name, dict(surface=, tie=)).
|
|
99
|
+
# conformity: "proportional" | "follow_top" | "follow_base".
|
|
100
|
+
ps.zone(name: str, color: str | None = None) -> ZoneColor # a zone's colour
|
|
101
|
+
|
|
102
|
+
ps.Layering(dz: float | None = None, nk: int | None = None,
|
|
103
|
+
min_cell: float | None = None) -> Layering
|
|
104
|
+
# Layer allocation; dz XOR nk set the default. .replace("Z*", dz=0.5) adds a
|
|
105
|
+
# per-glob override. min_cell(m) = sub-threshold cell-collapse floor.
|
|
106
|
+
|
|
107
|
+
ps.Contacts(mapping: dict[str, dict[str, float]] | None = None) -> Contacts
|
|
108
|
+
# Per-zone fluid contacts by glob: {"Z4": dict(goc=.., fwl=..), "Z2": dict(owc=..)}.
|
|
109
|
+
# A zone with no matching entry is contactless. .replace("Z4", fwl=...) derives.
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Settings specs (the HOW objects)
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
ps.TieSettings(method: str = "convergent", radius_m: float | None = None) -> TieSettings
|
|
116
|
+
# method: "convergent" (control-replacement) | "radius" (tie-locality; radius_m).
|
|
117
|
+
|
|
118
|
+
ps.Gridding(fidelity_m=None, extrapolation: Extrapolation | None = None,
|
|
119
|
+
collapse: bool = True, min_cell: float | None = None) -> Gridding
|
|
120
|
+
ps.decay_to_flat(range_m: float) -> Extrapolation
|
|
121
|
+
ps.flat() -> Extrapolation
|
|
122
|
+
ps.nearest() -> Extrapolation
|
|
123
|
+
|
|
124
|
+
ps.Run(memory_budget: int | None = None, workers: int = 0) -> Run
|
|
125
|
+
# Run resources. memory_budget (BYTES) forwards to the engine out-of-core
|
|
126
|
+
# switch (loud spill, never an OOM kill); workers shards the MC realize loop.
|
|
127
|
+
|
|
128
|
+
ps.LoadSettings(crs: str | None = None,
|
|
129
|
+
aliases: dict[str, str] | None = None) -> LoadSettings
|
|
130
|
+
ps.ViewSettings(property=None, open_browser: bool = True,
|
|
131
|
+
port: int = 0, block: bool = False) -> ViewSettings
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Property specs
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
ps.Props(*items: Prop) -> Props # the set applied at grid.model(props=)
|
|
138
|
+
ps.Prop(name: str, zone: str | None = None, upscale_method: str = "arithmetic",
|
|
139
|
+
net_only: bool = False, net_cutoff: float = 0.5,
|
|
140
|
+
propagate: Propagate | None = None) -> Prop
|
|
141
|
+
# One cube's population: upscale (from wells) then propagate (SGS). `zone`
|
|
142
|
+
# scopes the pipe to one zone of a stack.
|
|
143
|
+
ps.Propagate(variogram: Variogram = Variogram(), seed: int = 1,
|
|
144
|
+
max_neighbours: int | None = None, radius_m: float | None = None,
|
|
145
|
+
trend: CollocatedTrend | None = None, mode: str = "level_shift",
|
|
146
|
+
allow_mean_fill: bool = False) -> Propagate
|
|
147
|
+
# mode: "level_shift" | "resimulate".
|
|
148
|
+
ps.variogram(model: str, range_m: float, sill: float = 1.0,
|
|
149
|
+
nugget: float = 0.0) -> Variogram
|
|
150
|
+
# model: "spherical" | "exponential" | "gaussian".
|
|
151
|
+
ps.collocated(surface, corr: float, as_depth: bool = False)
|
|
152
|
+
# surface a NAME (str) -> a CollocatedTrend spec resolved at apply (the v2 form).
|
|
153
|
+
# surface a _core.Surface -> the v1 eager trend (DEPRECATED — bind by name).
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Monte-Carlo specs
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
ps.Mc(porosity=None, net_to_gross=None, water_saturation=None,
|
|
160
|
+
fvf=None, gas_fvf=None, contacts=None, goc=None,
|
|
161
|
+
per_zone: dict[str, Mc] | None = None,
|
|
162
|
+
settings: McSettings | None = None, n: int = 10_000, seed: int = 42) -> Mc
|
|
163
|
+
# Property fields take a scalar sd / ps.shift(...) / ps.dist(...); contact
|
|
164
|
+
# fields (contacts = lower FWL/OWC, goc) take a scalar sd_m / ps.pick_spread(...).
|
|
165
|
+
# per_zone overrides by zone. Auto-routes to zoned_uncertainty on a zoned model,
|
|
166
|
+
# else uncertainty.
|
|
167
|
+
ps.McSettings(lo_pct: float = 10.0, hi_pct: float = 90.0, workers: int = 0) -> McSettings
|
|
168
|
+
ps.shift(sd: float) -> Uncertain # a zero-mean level shift
|
|
169
|
+
ps.dist(kind: str, *params: float) -> Uncertain
|
|
170
|
+
# kind: "level_shift"(sd) | "normal"(mean,sd) | "lognormal"(mean,sd) |
|
|
171
|
+
# "uniform"(lo,hi) | "triangular"(lo,mode,hi) |
|
|
172
|
+
# "truncated_normal"(mean,sd,lo,hi).
|
|
173
|
+
|
|
174
|
+
# Distribution builders (the _core samplers, for the v1 kwargs and Uncertain.resolve):
|
|
175
|
+
ps.normal(mean, sd); ps.lognormal(mean, sd); ps.uniform(lo, hi)
|
|
176
|
+
ps.triangular(lo, mode, hi); ps.truncated_normal(mean, sd, lo, hi)
|
|
177
|
+
ps.level_shift(sd); ps.pick_spread(sd_m) # .clamped(lo, hi) on samplers
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Chart specs + the asset bundle
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
ps.Crossplot(x, y, wells=(), color_by="well", x_log=False, y_log=False,
|
|
184
|
+
regression=False) -> Crossplot # applied on proj.crossplot_bundle
|
|
185
|
+
ps.Tornado(base=None, units: str = "MSm³", fold_count: int = 8) -> Tornado
|
|
186
|
+
ps.Distribution(gas=False, zone=None, name=None) -> Distribution
|
|
187
|
+
|
|
188
|
+
ps.AssetSpec(name="", load=None, horizons=None, subzones=None, layering=None,
|
|
189
|
+
contacts=None, ties=None, gridding=None, props=None, mc=None,
|
|
190
|
+
run=None, view=None) -> AssetSpec
|
|
191
|
+
# A whole modelling scenario as one durable value; every field is a spec, so
|
|
192
|
+
# asset.to_dict() is a total, savable scenario file.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## The apply moments
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
grid.model(props: Props | None = None, con: Contacts | dict | None = None, *,
|
|
199
|
+
fluid: str = "oil", fvf: float = 1.25, gas_fvf: float | None = None,
|
|
200
|
+
wells=None, run: Run | None = None, sugar_cube: bool = False) -> Model
|
|
201
|
+
# Execute the build with the property + contact specs -> a populated Model.
|
|
202
|
+
# Zoned (Horizons.zones present) -> contacts fold into the zonation; non-zoned
|
|
203
|
+
# -> contacts go to the model. `run` carries memory_budget + workers.
|
|
204
|
+
|
|
205
|
+
model.zoned_uncertainty(mc: Mc | None = None, **legacy) -> ZonedUncertainty
|
|
206
|
+
model.uncertainty(mc: Mc | None = None, **legacy) -> Uncertainty
|
|
207
|
+
model.mc(spec: Mc) # auto-routes on model.is_zoned()
|
|
208
|
+
# Pass EITHER a ps.Mc spec OR the legacy kwargs — not both. Legacy kwargs emit
|
|
209
|
+
# a DeprecationWarning.
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## The result surface — `Model`
|
|
213
|
+
|
|
214
|
+
```python
|
|
215
|
+
model.summary() -> dict # STOIIP / GIIP [MSm³], GRV [mcm]
|
|
216
|
+
model.in_place_by_zone() -> dict # {"zones": [...], "total": {...}}
|
|
217
|
+
model.zone_stats(property: str) -> list # per-zone count/mean/min/max
|
|
218
|
+
model.well_tie_residuals() -> list # [{well, horizon, measured_depth_m,
|
|
219
|
+
# model_depth_m, residual_m}]
|
|
220
|
+
model.property_names() -> list[str]
|
|
221
|
+
model.is_zoned() -> bool
|
|
222
|
+
model.well_ids() -> list[str]
|
|
223
|
+
model.warnings() -> list
|
|
224
|
+
|
|
225
|
+
# viewer bundles + render:
|
|
226
|
+
model.map_bundle(property=None, k_slice=None) -> dict
|
|
227
|
+
model.intersection_bundle(line=None, well=None, property=None) -> dict
|
|
228
|
+
model.volume_bundle(property=None) -> dict
|
|
229
|
+
model.wells_bundle() -> dict
|
|
230
|
+
model.view(settings: ViewSettings | None = None, *, open_browser=True, port=0,
|
|
231
|
+
block=False, property=None, lines=None, charts=None) -> str # URL
|
|
232
|
+
model.save_view(path: str, property=None, lines=None, charts=None) -> None
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## The result surface — uncertainty
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
# flat (non-zoned) — model.uncertainty(...):
|
|
239
|
+
unc.stoiip -> dict # {p90, p50, p10, mean, *_msm3, samples}
|
|
240
|
+
unc.giip -> dict
|
|
241
|
+
unc.tornado() -> list # ranked input swings
|
|
242
|
+
unc.tornado_bundle(base=None, units="MSm³", fold_count=8) -> dict
|
|
243
|
+
unc.distribution_bundle(gas=False, name=None) -> dict
|
|
244
|
+
unc.view(open_browser=True, port=0, block=False, charts=None) -> str
|
|
245
|
+
unc.save_view(path, charts=None) -> None
|
|
246
|
+
|
|
247
|
+
# zoned — model.zoned_uncertainty(...):
|
|
248
|
+
zunc.total -> dict # {"stoiip": {..}, "giip": {..}, "two_contact": bool}
|
|
249
|
+
zunc.zones -> list # [{"zone", "stoiip", "giip", "two_contact"}, ...]
|
|
250
|
+
zunc.distribution_bundle(gas=False, zone=None, name=None) -> dict
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Spec value semantics (shared)
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
spec.to_dict() -> dict # tagged with "spec"; JSON-able
|
|
257
|
+
Spec.from_dict(d) -> Spec # per class
|
|
258
|
+
ps.spec_from_dict(d) -> Spec # dispatch on the "spec" tag
|
|
259
|
+
ps.registered_specs() -> tuple[type, ...]
|
|
260
|
+
spec.replace(**changes) -> Spec # collection specs also accept a leading
|
|
261
|
+
# name/glob: hz.replace("H1", surface=...)
|
|
262
|
+
spec == other; hash(spec); repr(spec) # value equality; domain-table repr
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
## The analytic box model
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
ps.run_box_model(area_km2, gross_height_m, porosity, net_to_gross,
|
|
269
|
+
water_saturation, fvf, *, fluid="oil", top_m=0.0,
|
|
270
|
+
contact_m=math.inf, ni=10, nj=10, nk=5,
|
|
271
|
+
realizations=10_000, seed=1) -> ModelResult
|
|
272
|
+
# Each volumetric input: a number (constant) | (min, mode, max) triangular |
|
|
273
|
+
# {"normal"|"lognormal"|"uniform"|"triangular": [...]} tagged dict.
|
|
274
|
+
# contact_m is REQUIRED and finite (a non-finite contact is a loud error).
|
|
275
|
+
|
|
276
|
+
m.samples -> list[float] # per-realization in-place [Sm³]
|
|
277
|
+
m.summary_msm3 -> dict # {p90, p50, p10, mean} in MSm³ (oil)
|
|
278
|
+
m.summary_bcm -> dict # the same in bcm (gas)
|
|
279
|
+
m.scaled_summary(per: float) -> dict
|
|
280
|
+
m.view(open_browser=True, port=0, block=False, property=None) -> str
|
|
281
|
+
m.save_view(path, property=None) -> None
|
|
282
|
+
m.save_json(path, property=None) -> None
|
|
283
|
+
repr(m) # P90 / P50 / P10 / mean / deterministic [Sm³]
|
|
284
|
+
|
|
285
|
+
ps.Model(area_km2, gross_height_m, *, ni=20, nj=20, nk=8, top_m=1500.0,
|
|
286
|
+
contact_m=math.inf, porosity=0.25, net_to_gross=0.8,
|
|
287
|
+
water_saturation=0.3, fvf=1.25, fluid="oil") # a structured box
|
|
288
|
+
sm.add_control(ip: int, jp: int, depth_m: float) -> None # a structural high
|
|
289
|
+
sm.solve() -> Refined
|
|
290
|
+
sm.view(...) ; sm.save_view(...) ; sm.save_json(...)
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Aggregation + standalone charts
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
ps.aggregate(segments, correlation: str = "independent") -> list[float]
|
|
297
|
+
# correlation: "independent" | "comonotonic". Sum per-segment realization
|
|
298
|
+
# vectors under an explicit dependence assumption.
|
|
299
|
+
ps.distribution_bundle(segments, aggregate=None, names=None, gas=False,
|
|
300
|
+
title=None) -> dict
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## v1 (deprecated)
|
|
304
|
+
|
|
305
|
+
The v1 eight-call staged chain — `proj.framework(horizons=[...])` → `set_zones` /
|
|
306
|
+
`set_zonation` / `set_layering` / `set_well_ties` → `build_grid` → per-property
|
|
307
|
+
`grid.property(...).upscale(...).propagate(...)` → `grid.model(contacts=...)` →
|
|
308
|
+
`model.uncertainty(...)` / `model.zoned_uncertainty(...)` → `mc.tornado()` — remains
|
|
309
|
+
callable for a **two-minor** window and emits a `DeprecationWarning`. New code uses
|
|
310
|
+
the declarative v2 surface above. The runnable staged example is
|
|
311
|
+
`examples/staged_build.py`.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to petekSim are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
|
|
5
|
+
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
### Changed — packaging (internal, behaviour-neutral)
|
|
10
|
+
|
|
11
|
+
- **Consolidated the three published library crates into one.** `srs-units`,
|
|
12
|
+
`srs-pvt` and `srs-core` were merged into a single crate, **`peteksim`**
|
|
13
|
+
(0.1.0), at the repo root. Their boundaries are preserved as modules —
|
|
14
|
+
`peteksim::{units, pvt, core}` — and the headline Rust API (`run_model`, the
|
|
15
|
+
appraisal facade types, the `Distribution` seam, the view bundles) is
|
|
16
|
+
re-exported at the crate root. File moves + mechanical path rewrites only; no
|
|
17
|
+
logic changed. The workspace keeps its `crates/srs-py` member (the `peteksim`
|
|
18
|
+
wheel source), which now binds the `peteksim` crate; the published PyPI wheel
|
|
19
|
+
surface (`peteksim`) is unchanged.
|
|
20
|
+
- **Repointed the geomodel dependency at petekStatic's consolidated crate.** The
|
|
21
|
+
former per-crate path deps on petekStatic's `srs-*` crates are now a single
|
|
22
|
+
`petekstatic = "0.1.0"` (path `../petekStatic`); the `srs_model::` /
|
|
23
|
+
`srs_grid::` / `srs_wireframe::` (…) imports rewrote to
|
|
24
|
+
`petekstatic::{model, grid, wireframe, …}::`. petekio / petekTools pins
|
|
25
|
+
unchanged.
|
|
26
|
+
|
|
27
|
+
## [0.1.0] - 2026-07-05
|
|
28
|
+
|
|
29
|
+
First public release of **`peteksim`** — the Python-facing appraisal toolkit: a
|
|
30
|
+
pure-Rust reservoir core with thin bindings that presents the whole
|
|
31
|
+
subsurface-modelling stack (ingest → geomodel → volumetrics → uncertainty) as one
|
|
32
|
+
facade. From a Petrel export to a STOIIP P-curve in a handful of calls.
|
|
33
|
+
|
|
34
|
+
All inputs and outputs are **SI/metric**: areas in km², lengths/depths in metres
|
|
35
|
+
(positive-down), volumes in Sm³ (reported in MSm³ for oil, bcm for gas), GRV in
|
|
36
|
+
mcm (10⁶ m³), FVF as dimensionless Rm³/Sm³. Imperial is opt-in on your side, never
|
|
37
|
+
a default.
|
|
38
|
+
|
|
39
|
+
### Added — the declarative modelling API (v2, the primary surface)
|
|
40
|
+
|
|
41
|
+
- **Specs applied at explicit moments.** A model is built from immutable, declarative
|
|
42
|
+
**spec** values that say WHAT (`Horizons`, `Subzones`, `Layering`, `Contacts`,
|
|
43
|
+
`Props`, `Mc`) or HOW (`TieSettings`, `Gridding`, `Run`, `LoadSettings`,
|
|
44
|
+
`ViewSettings`). A spec holds **names**, not project objects — so it is
|
|
45
|
+
project-independent, reusable across re-exports and synthetic assets. It is applied
|
|
46
|
+
at three explicit moments: `proj.grid_geometry(...)` → `geom.build(...)` →
|
|
47
|
+
`grid.model(...)` → `model.zoned_uncertainty(...)`.
|
|
48
|
+
- **Loud at apply.** Errors when a spec is applied name **both** the missing project
|
|
49
|
+
object and the spec entry. A spec field the engine cannot yet honour raises
|
|
50
|
+
`NotYetSupported` (never a silent no-op); an unresolvable name raises `ApplyError`.
|
|
51
|
+
- **Value semantics.** Every spec round-trips through a dict (`to_dict` / `from_dict`
|
|
52
|
+
/ `ps.spec_from_dict`), compares and hashes by value, derives with `.replace(...)`,
|
|
53
|
+
and pretty-prints as its own domain table — so a scenario is a savable, diffable
|
|
54
|
+
file. `ps.AssetSpec` bundles a whole scenario (load + structure + props + mc) into
|
|
55
|
+
one durable value.
|
|
56
|
+
- **Structure specs:** `ps.Horizons(ps.hz(...), zones=[...])` (the ordered
|
|
57
|
+
stratigraphic column + the zones between horizons; per-row well ties and structural
|
|
58
|
+
uncertainty via `ps.hz(tie=, sd=, vgm=)`), `ps.Subzones` / `ps.splits` (intra-zone
|
|
59
|
+
splits + conformity), `ps.Layering(dz= | nk=)`, `ps.Contacts({zone: {...}})`, and
|
|
60
|
+
`ps.zone(name, color=)`.
|
|
61
|
+
- **Property specs:** `ps.Props(ps.Prop("PORO", net_only=True, propagate=...))` — a
|
|
62
|
+
visible per-property pipeline of well upscaling then SGS propagation
|
|
63
|
+
(`ps.Propagate` + `ps.variogram(...)` + optional `ps.collocated(name, corr=)` trend,
|
|
64
|
+
`level_shift` / `resimulate` MC modes).
|
|
65
|
+
|
|
66
|
+
### Added — volumetrics, zoned Monte-Carlo, and P-curves
|
|
67
|
+
|
|
68
|
+
- **In-place volumes.** `grid.model(...)` returns a populated model; `model.summary()`
|
|
69
|
+
reports STOIIP / GIIP (MSm³) and GRV (mcm). Two-contact columns (gas cap + oil rim)
|
|
70
|
+
split automatically from the contact picks.
|
|
71
|
+
- **Zoned uncertainty.** `model.zoned_uncertainty(ps.Mc(...))` runs Monte-Carlo over
|
|
72
|
+
static-model realizations — per-zone contact draws and per-zone property level shifts
|
|
73
|
+
— and returns per-zone and total **P-curves** (`p90` / `p50` / `p10` / `mean`, with
|
|
74
|
+
`*_msm3` reporting scales) plus the full per-realization sample vectors.
|
|
75
|
+
- **Multi-zone stacks.** A multi-horizon stack unlocks per-zone layering, per-zone
|
|
76
|
+
contacts (a contactless zone contributes GRV with zero hydrocarbon), and per-zone
|
|
77
|
+
property pipelines. `model.in_place_by_zone()`, `model.zone_stats(prop)`, and
|
|
78
|
+
`model.well_tie_residuals()` report the breakdown; well ties tie each horizon to its
|
|
79
|
+
tops picks.
|
|
80
|
+
- **Tornado + aggregation.** A flat `model.uncertainty(...)` result exposes
|
|
81
|
+
`.tornado()` (ranked input swings); `ps.aggregate([...], correlation=...)` sums
|
|
82
|
+
segment realizations under an explicit dependence assumption.
|
|
83
|
+
|
|
84
|
+
### Added — the analytic box model
|
|
85
|
+
|
|
86
|
+
- **`ps.run_box_model(...)`** — a quick STOIIP/GIIP estimate with Monte-Carlo on each
|
|
87
|
+
volumetric input (a constant, a `(min, mode, max)` triangular, or a tagged
|
|
88
|
+
`{"normal"|"lognormal"|"uniform"|"triangular": [...]}` distribution). Returns
|
|
89
|
+
P90/P50/P10/mean/deterministic plus the full sample vector.
|
|
90
|
+
- **`ps.Model(...)`** — a structured box with real structural relief built in code
|
|
91
|
+
(`add_control(i, j, depth_m)` seeds a high), for a first look before a full project.
|
|
92
|
+
|
|
93
|
+
### Added — the browser viewer
|
|
94
|
+
|
|
95
|
+
- **`model.view()`** opens a tabbed, bundle-driven inspection viewer: **Map** (areal
|
|
96
|
+
rasters, outline, contact subcrop masks, well markers, draw-a-fence to cut a
|
|
97
|
+
section), **Intersection** (the vertical cross-section with horizon + contact traces
|
|
98
|
+
and the bore path), and **Volume** (the corner-point mesh in three.js with property
|
|
99
|
+
colouring, threshold, zone toggles, i/j/k clip planes). `view()` is non-blocking
|
|
100
|
+
(prints a URL and returns).
|
|
101
|
+
- **`model.save_view("m.html")`** writes ONE self-contained HTML file that opens off
|
|
102
|
+
`file://` with all data + JS inlined — no server, no network (confidential-data
|
|
103
|
+
safe). Bundle accessors `map_bundle` / `intersection_bundle` / `volume_bundle`
|
|
104
|
+
return the JSON directly. The renderer is petekTools' horizontal `petektools.viewer`
|
|
105
|
+
unit, which `peteksim` consumes.
|
|
106
|
+
|
|
107
|
+
### Added — resources and out-of-core
|
|
108
|
+
|
|
109
|
+
- **`ps.Run(memory_budget=<bytes>, workers=N)`** carries the run resources: `workers`
|
|
110
|
+
shards the MC realize loop; `memory_budget` forwards to the engine's out-of-core
|
|
111
|
+
switch (a larger-than-memory model spills to disk with a loud notice, never an OOM
|
|
112
|
+
kill).
|
|
113
|
+
|
|
114
|
+
### Deprecated
|
|
115
|
+
|
|
116
|
+
- **The v1 eight-call model-build chain** (`proj.framework(...)` → `set_zones` →
|
|
117
|
+
`build_grid` → per-property `upscale`/`propagate` → `grid.model` → `uncertainty` →
|
|
118
|
+
`tornado`) is deprecated in favour of the declarative v2 API, with a two-minor
|
|
119
|
+
window. It keeps working and emits a `DeprecationWarning`; new code should use
|
|
120
|
+
`proj.grid_geometry(...).build(ps.Horizons(...))`.
|
|
121
|
+
|
|
122
|
+
### Licensing
|
|
123
|
+
|
|
124
|
+
- petekSim is licensed under the **Business Source License 1.1** (BUSL-1.1); each
|
|
125
|
+
released version converts to Apache-2.0 four years after publication. See `LICENSE`.
|
|
126
|
+
|
|
127
|
+
[Unreleased]: https://github.com/kkollsga/peteksim/compare/v0.1.0...HEAD
|
|
128
|
+
[0.1.0]: https://github.com/kkollsga/peteksim/releases/tag/v0.1.0
|