Metadata-Version: 2.4
Name: fracpta
Version: 0.1.1
Summary: GeoType catalogues of fractured-reservoir pressure transients: analytic ensembles, GeoDFN networks and descriptors, open-DARTS discrete-fracture simulation, the clustering ladder, conformal assignment and attribution.
Author: Felipe Santibanez-Leal
License: MIT
Project-URL: Homepage, https://github.com/fsantibanezleal/CAOS_FracPTA
Project-URL: Documentation, https://github.com/fsantibanezleal/CAOS_FracPTA/blob/main/docs/README.md
Project-URL: Issues, https://github.com/fsantibanezleal/CAOS_FracPTA/issues
Project-URL: Changelog, https://github.com/fsantibanezleal/CAOS_FracPTA/blob/main/CHANGELOG.md
Keywords: pressure transient analysis,well test,fractured reservoir,dual porosity,warren-root,discrete fracture network,DFN,GeoDFN,open-darts,DTW,k-medoids,conformal prediction,SHAP,geothermal,reservoir engineering
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.26
Requires-Dist: pygeotypes>=0.1.3
Provides-Extra: dfn
Requires-Dist: GeoDFN>=2.0; extra == "dfn"
Requires-Dist: networkx>=3.2; extra == "dfn"
Requires-Dist: meshio>=5.3; extra == "dfn"
Requires-Dist: matplotlib>=3.8; extra == "dfn"
Provides-Extra: sim
Requires-Dist: open-darts>=1.5; extra == "sim"
Provides-Extra: learn
Requires-Dist: scikit-learn>=1.4; extra == "learn"
Requires-Dist: scipy>=1.11; extra == "learn"
Requires-Dist: tslearn>=0.6; extra == "learn"
Requires-Dist: hdbscan>=0.8; extra == "learn"
Requires-Dist: umap-learn>=0.5; extra == "learn"
Requires-Dist: pycatch22>=0.4; extra == "learn"
Requires-Dist: dtaidistance>=2.3; extra == "learn"
Requires-Dist: shap>=0.45; extra == "learn"
Provides-Extra: deep
Requires-Dist: torch>=2.1; extra == "deep"
Requires-Dist: onnx>=1.16; extra == "deep"
Requires-Dist: onnxruntime>=1.17; extra == "deep"
Requires-Dist: onnxscript>=0.1; extra == "deep"
Provides-Extra: field
Requires-Dist: welltestpy>=1.2; extra == "field"
Requires-Dist: pandas>=2.0; extra == "field"
Requires-Dist: pyarrow>=15.0; extra == "field"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: ruff<0.17,>=0.6; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: GeoDFN>=2.0; extra == "dev"
Requires-Dist: networkx>=3.2; extra == "dev"
Requires-Dist: meshio>=5.3; extra == "dev"
Requires-Dist: matplotlib>=3.8; extra == "dev"
Requires-Dist: scikit-learn>=1.4; extra == "dev"
Requires-Dist: scipy>=1.11; extra == "dev"
Requires-Dist: tslearn>=0.6; extra == "dev"
Requires-Dist: hdbscan>=0.8; extra == "dev"
Requires-Dist: umap-learn>=0.5; extra == "dev"
Requires-Dist: dtaidistance>=2.3; extra == "dev"
Requires-Dist: shap>=0.45; extra == "dev"
Requires-Dist: welltestpy>=1.2; extra == "dev"
Requires-Dist: pandas>=2.0; extra == "dev"
Requires-Dist: pyarrow>=15.0; extra == "dev"
Dynamic: license-file

# fracpta

GeoType catalogues of fractured-reservoir pressure transients.

A GeoType is a recurring fluid-flow behaviour of a fractured reservoir, read off the shape of its
pressure-transient response. `fracpta` builds catalogues of them: it generates or ingests transients,
turns them into comparable shapes, clusters them into a catalogue whose prototypes are real member
curves, assigns new curves with a coverage guarantee, and attributes each behaviour to the
fracture-network properties that control it. The shape machinery underneath (derivative
preprocessing, banded DTW, PAM k-medoids, conformal assignment) is [`pygeotypes`](https://pypi.org/project/pygeotypes/);
this package keeps the domain.

It is the engine of the [Pulso](https://pulso.fasl-work.com) product and lived inside it as an internal
package until 0.01.000. A product declares no package of its own, so the engine is published here and
the product pins it.

## Install

```bash
pip install fracpta                 # core: numpy + pygeotypes; analytic ensembles, studies, the live entry
pip install "fracpta[dfn]"          # GeoDFN network generation, fracture descriptors, meshing
pip install "fracpta[learn]"        # the clustering ladder, representations, attribution (scikit-learn, tslearn, ...)
pip install "fracpta[field]"        # welltestpy field campaigns and the 4TU real-data readers
pip install "fracpta[sim]"          # open-DARTS discrete-fracture simulation (heavy, offline)
pip install "fracpta[deep]"         # the learned tier (torch, ONNX export)
```

Every heavy engine is imported inside the function that needs it: `import fracpta` stays light, and the
analytic generators and the live classifier run anywhere the core installs, Pyodide included.

## Quickstart

```python
from fracpta.io.schema import EnsembleSpec
from fracpta.study import run_study

spec = EnsembleSpec(case_id="demo", kind="warren_root", n_curves=120,
                    omega_range=(0.01, 0.5), lam_range=(1e-8, 1e-4), noise_sd=0.01,
                    k_min=2, k_max=6, alpha=0.1)
result = run_study(spec, seed=42)

trace = result["trace"]            # the full-ensemble study artifact (see docs/contracts.md)
trace["k"]                          # catalogue size chosen by the silhouette sweep
result["metrics"]["silhouette_train"], result["metrics"]["coverage"]
result["report"].flagged            # CONTRACT 1 flags on the generated curves
```

Classify a new curve against that catalogue, the way a browser does it:

```python
from fracpta.live import classify_curve_json, generate_curve_json

curve = generate_curve_json(omega=0.05, lam=1e-6, noise_sd=0.02, seed=9)
verdict = classify_curve_json(trace, curve["t"], curve["p"], alpha=0.1)
verdict["point_prediction"], verdict["prediction_set"], verdict["out_of_catalogue"]
```

Bring your own transients (time and pressure arrays plus optional physical descriptors per curve):

```python
from fracpta.stages.feature_extraction import arrays_from_curves
from fracpta.study import study_trace, train_infer_evaluate

arrays = arrays_from_curves("mine", t_list, p_list, features, feature_names,
                            n_points=spec.n_points, derivative_order=1, L=spec.L, norm=spec.norm)
result = train_infer_evaluate(arrays, spec, seed=42)
trace, schema = study_trace("mine", arrays, spec, result, seed=42)
```

A run is a pure function of (spec, seed): the same inputs produce an identical trace, byte for byte.

## What is in the package

| Module | What it holds |
|---|---|
| `fracpta.model.pta` | analytic pressure-transient ensembles (Warren-Root dual porosity, homogeneous radial, mixtures) on the `pygeotypes` generators |
| `fracpta.io.schema`, `fracpta.io.contract` | the input specs (`EnsembleSpec`, `DFNSpec`, `DartsWellTestSpec`, `DfmStudySpec`, `RealDataSpec`, `FieldDataSpec`, `BenchmarkSpec`) and CONTRACT 1: schema, ranges, an explicit outlier policy, flags with reasons |
| `fracpta.io.field_data`, `fracpta.io.real_data` | the welltestpy field campaigns and the 4TU fractured-reservoir corpus, read from the vault named by `FRACPTA_VAULT` |
| `fracpta.stages` | the study workflow: `preprocess` (generate under CONTRACT 1), `feature_extraction` (log grid, Bourdet derivative or p'', normalisation), `train` (DTW matrix, K selection, PAM, catalogue, conformal calibration, attribution), `infer`, `evaluate` |
| `fracpta.study` | `run_study`, `train_infer_evaluate`, `study_trace`: the stages strung together without a filesystem |
| `fracpta.core.trace` | the study trace builders (the v2 full-ensemble format and the compact v1), the DFN, DARTS and DFM traces |
| `fracpta.methods` | the clustering ladder against the DTW k-medoids reference (`clustering`), UMAP, t-SNE, functional PCA and catch22 (`representations`), predictability-vs-K, the ROM descriptor sweep and the dual-representation Mondrian conformal (`attribution_plus`) |
| `fracpta.dfn` | GeoDFN 2-D network ensembles and their fracture descriptors, meshing, open-DARTS well-test and discrete-fracture-matrix drawdowns, the fidelity gate against the source paper |
| `fracpta.deep` | the learned tier: InceptionTime, PatchTST-lite, a convolutional autoencoder and a TS2Vec-style encoder trained on catalogue curves and exported to ONNX under a parity gate |
| `fracpta.live` | `generate_curve_json`, `classify_curve_json`: the two primitives a browser lane runs against a baked trace |

The theory behind each method, equation by equation and with its sources, is in [`docs/`](docs/README.md).

## Data the readers expect

Two datasets are not shipped (size and licence) and are read from a vault directory named by
`FRACPTA_VAULT` (the former name `FLOWDNA_VAULT` is still accepted): `real-curves/` holds the 4TU
fractured-reservoir corpus, `field/` the welltestpy campaigns (Zenodo 4139374). `available()` on each
reader says whether the data is reachable, so a caller can skip those studies instead of failing.

## Determinism and provenance

Every random draw goes through one seeded generator (`fracpta.core.rng.make_rng`); no stage reads a
clock into an artifact. A trace records the catalogue, the conformal calibration, the attribution, the
full committed ensemble and the diagnostics of the run; the consuming product adds its own manifest
with the package version it used.

## Versioning

`VERSION` is the source of truth (`X.XX.XXX`); `pyproject.toml` carries the PEP 440 form; every release
is tagged `vX.XX.XXX` and published to PyPI by `publish-pypi.yml` through trusted publishing. History in
[`CHANGELOG.md`](CHANGELOG.md).

## Licence

MIT. Third-party engines keep their own licences: GeoDFN (MIT), open-DARTS (Apache-2.0), tslearn
(BSD-2), hdbscan (BSD-3), umap-learn (BSD-3), pycatch22 (GPL-3, optional, imported only when installed).
