Metadata-Version: 2.4
Name: pestpysuite
Version: 0.0.1
Summary: A pure-Python parameter estimation suite (GLM and iES) for environmental models.
Author: Martin Vonk
Author-email: Martin Vonk <vonk.mart@gmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Typing :: Typed
Requires-Dist: numpy>=2.0.0
Requires-Dist: scipy>=1.9.0
Requires-Dist: pytest ; extra == 'dev'
Requires-Dist: tox-uv ; extra == 'dev'
Requires-Dist: ruff ; extra == 'dev'
Requires-Dist: ty ; extra == 'dev'
Requires-Python: >=3.11
Provides-Extra: dev
Description-Content-Type: text/markdown

# PESTpy

A pure-Python parameter estimation and uncertainty analysis package for
environmental and geophysical models. **pestpy** bridges the gap between
`scipy.optimize.least_squares` and PEST++ by providing two core algorithms:

- **GLM (Gauss-Levenberg-Marquardt)** – A deterministic optimizer with an
  explicit multi-lambda testing loop and Marquardt scaling. Suitable for
  non-linear least-squares calibration problems.
- **iES (Iterative Ensemble Smoother)** – A stochastic ensemble-based smoother
  based on the mathematics from Fienen et al. (2025). It simultaneously upgrades
  an ensemble of parameter realizations while adding observation noise to
  prevent ensemble collapse.

## Installation

```bash
pip install pestpysuite
```

Or directly from source:

```bash
pip install .
```

## Quick Start

```python
import numpy as np
from pestpy import GLMOptimizer, IESOptimizer

# --- GLM example ---
def model(params):
    return params[0] * np.array([1.0, 2.0, 3.0]) + params[1]

obs = np.array([2.1, 4.0, 6.1])
p0 = np.array([1.0, 0.0])

glm = GLMOptimizer(model, obs)
result = glm.fit(p0)
print("GLM solution:", result)

# --- iES example ---
n_params, n_obs, n_real = 2, 3, 50
ensemble = np.random.randn(n_params, n_real)

ies = IESOptimizer(model, obs)
ensemble_updated = ies.fit(ensemble)
print("iES posterior mean:", ensemble_updated.mean(axis=1))
```
