Metadata-Version: 2.5
Name: kipe
Version: 0.3.0
Summary: Parameter estimation with reduced-order unscented Kalman filtering, and identifiability analysis, for PDE models
Project-URL: Homepage, https://github.com/cardiomath/kipe
Project-URL: Issues, https://github.com/cardiomath/kipe/issues
Author-email: David Nolte <dajuno@users.noreply.github.com>
License-Expression: MIT
License-File: AUTHORS
License-File: LICENSE
Keywords: FEniCS,ROUKF,data-assimilation,fisher-information,identifiability,inverse-problems,parameter-estimation,unscented-kalman-filter
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.12
Requires-Dist: matplotlib>=3.8
Requires-Dist: mpi4py>=4
Requires-Dist: numpy>=1.26
Requires-Dist: plotext<7,>=6.1
Requires-Dist: pydantic>=2.7
Requires-Dist: ruamel-yaml>=0.18
Provides-Extra: dev
Requires-Dist: mypy==2.3.1; extra == 'dev'
Requires-Dist: pre-commit; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: ruff==0.16.9; extra == 'dev'
Requires-Dist: scipy>=1.12; extra == 'dev'
Provides-Extra: docs
Requires-Dist: myst-parser<6,>=4; extra == 'docs'
Requires-Dist: sphinx-rtd-theme<4,>=3; extra == 'docs'
Requires-Dist: sphinx<10,>=8; extra == 'docs'
Requires-Dist: sphinxcontrib-mermaid<3,>=1; extra == 'docs'
Description-Content-Type: text/markdown

# kipe

[![CI](https://github.com/cardiomath/kipe/actions/workflows/ci.yml/badge.svg)](https://github.com/cardiomath/kipe/actions/workflows/ci.yml)
![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

***K**alman-backed **p**arameter **e**stimation* and **i**dentifiability analysis for **p**artial differential **e**quations

## Key features

+ **Solver-agnostic**: Connect your model through a small forward-solver interface
+ **Derivative-free**: Optimizer is based on the reduced-order
unscented Kalman filter (ROUKF) and hence it needs only forward runs.
+ **Modular observation operators**: New observation operators can be implemented easily by extending the library.
+ **Twin experiments**: Generate synthetic measurements from a model run.
+ **Reproducible runs**: Keep results, logs, and run provenance together.

> **Status: early development.** The core works (ROUKF estimation, synthetic data, plotting, run provenance), as does a first FEniCSx backend for measurements on finite element meshes, but the study file format and the Python API may still change. Feedback is very welcome: please open an [issue](https://github.com/cardiomath/kipe/issues).

## Installation

`kipe` needs Python ≥ 3.12 and an MPI library (for `mpi4py`). Use your system's MPI, or install one
with pip:

```bash
pip install kipe
pip install mpich  # only if there is no MPI library on your system
```

The latest development version installs from GitHub:

```bash
pip install "kipe @ git+https://github.com/cardiomath/kipe"
```

For FEniCSx models, kipe also needs dolfinx (0.9), which pip does not install: use your FEniCSx
installation (e.g., Spack, conda or the `dolfinx/dolfinx` Docker image) and install kipe into its
Python environment. Its MPI is then the one kipe uses.

## First run: the FitzHugh–Nagumo example

The example estimates the three parameters of the FitzHugh–Nagumo model from noisy measurements
that `kipe` generated itself with the true parameters (a twin experiment):

```bash
git clone https://github.com/cardiomath/kipe
cd kipe/examples/fitzhugh_nagumo
kipe synthesis study.yaml     # generate the measurement data
kipe estimation study.yaml    # estimate a, b, c from a wrong initial guess
kipe plot study.yaml --truth  # estimates with their 1σ range; --terminal without a display
```

Everything is configured in the study file,
[`study.yaml`](https://github.com/cardiomath/kipe/blob/main/examples/fitzhugh_nagumo/study.yaml):
the forward solver, the estimated parameters with their initial guess and uncertainty, the
measurements and the filter. Results, a log and the provenance of the run (versions, git state,
environment) go to the `output.path` it names.

Other subcommands: `kipe list-parameters study.yaml` shows the parameters a solver accepts;
`kipe <subcommand> --help` shows the options.

## Your own model

`kipe` calls your model through a small interface, the `ForwardSolver` protocol in
[`kipe/forward_solver.py`](https://github.com/cardiomath/kipe/blob/main/src/kipe/forward_solver.py).

The study file names a factory that builds your solver:

```yaml
forward_solver:
  factory: "my_package.my_module:MySolver"   # module:name, imported by kipe
  arguments: {dt: 0.01}                      # arguments passed to MySolver
```

The `arguments` are those passed to the factory, i.e., `MySolver(dt=0.01)`.

The FitzHugh–Nagumo solver in
[`kipe/examples/fitzhugh_nagumo.py`](https://github.com/cardiomath/kipe/blob/main/src/kipe/examples/fitzhugh_nagumo.py)
is a complete example in about 150 lines, docstrings included.

## FEniCSx models

A FEniCSx solver exchanges the owned degrees of freedom of its fields as arrays, like any other
state, and additionally provides their function spaces, `function_space(field)` (see
[`kipe/fenicsx/forward_solver.py`](https://github.com/cardiomath/kipe/blob/main/src/kipe/fenicsx/forward_solver.py)).
A measurement can then interpolate a field onto a measurement mesh, which need not match the
solver's:

```yaml
measurements:
  u:
    fields: [u]
    spatial_sampling: {type: fenicsx.mesh, mesh: data/measurement_mesh.xdmf, element: P1}
    ...
```

A vector field is measured as a whole, as its component along a direction
(`projection: [0, 0, 1]`), or normal to a surface mesh (`projection: surface_normal`, with
`element: DG0`).

The measurement data are in the order of the measurement mesh file (one value per vertex for
`P1`, per cell for `DG0`), so synthesis and estimation can run on different numbers of MPI ranks;
they must use the same mesh file. The heat equation example shows a complete setup:

```bash
cd kipe/examples/heat
python measurement_mesh.py         # the measurement mesh
kipe synthesis study.yaml
mpirun -n 2 kipe estimation study.yaml
```

Its solver,
[`kipe/fenicsx/examples/heat.py`](https://github.com/cardiomath/kipe/blob/main/src/kipe/fenicsx/examples/heat.py),
is a template for your own.

## Development

```bash
git clone https://github.com/cardiomath/kipe
cd kipe
pip install -e ".[dev]"
pre-commit install --hook-type pre-commit --hook-type commit-msg
pytest
```

The pre-commit hooks run ruff, mypy and a check of the commit message, which follows
[Conventional Commits](https://www.conventionalcommits.org), e.g., `fix(roukf): ...`; the
allowed scopes are listed in `.pre-commit-config.yaml`.

## License

MIT, see [LICENSE](https://github.com/cardiomath/kipe/blob/main/LICENSE). If you use kipe, please
cite it as described in
[CITATION.cff](https://github.com/cardiomath/kipe/blob/main/CITATION.cff).
