kipe.options module

Options of the kipe study file.

A study file is a YAML file with one section per concern: output, forward_solver, parameters, measurements, synthesis and estimation (StudyOptions). Each command reads the sections it needs. Relative paths are relative to the working directory kipe is run from.

The dataclasses of this module are the only definition of the study file: pydantic validates study files against them, unknown keys are an error, and the documentation’s study file reference is generated from them, with their attribute docstrings as the descriptions.

See examples/fitzhugh_nagumo/study.yaml for a complete study file.

exception kipe.options.StudyFileError[source]

Bases: Exception

The study file cannot be read, or is not a YAML mapping of sections.

class kipe.options.OutputOptions(path: str, log_level: Literal['debug', 'info', 'warning', 'error'] = 'info')[source]

Bases: object

Where and how kipe writes its results.

path: str

Directory for all results. Subcommands write into subdirectories.

log_level: Literal['debug', 'info', 'warning', 'error'] = 'info'

Minimum level of the log messages kipe writes.

class kipe.options.ForwardSolverOptions(factory: str, arguments: dict[str, ~typing.Any]=<factory>)[source]

Bases: object

How to construct the forward solver.

kipe imports factory and calls it with arguments as keyword arguments. The factory can be a class or a function; it must return an object that conforms to kipe.forward_solver.ForwardSolver.

factory: str

Import path of the factory, "module:name", e.g., "kipe.examples.fitzhugh_nagumo:Solver".

arguments: dict[str, Any]

Keyword arguments passed to the factory, as given (no path resolution).

type kipe.options.Reparameterization = Literal['log', 'multiplicative', 'additive']
class kipe.options.ParameterPrior(initial: float | None = None, relative_stddev: float | None = None, stddev: float | None = None, reparameterization: Reparameterization | None = None)[source]

Bases: object

Initial estimate of one estimated parameter and its uncertainty.

The two define the prior (Gaussian in the estimated parameter \(\theta\)) and the starting state of the filter: the standard deviation sets the initial size of the sigma-point stencil used for the derivative-free linearization, and the initial weight against the measurement noise.

log and multiplicative take a relative_stddev, additive an absolute stddev.

initial: float | None = None

Initial estimate (prior mean), as physical value. The solver’s nominal value if not given.

relative_stddev: float | None = None

Standard deviation relative to initial, e.g., 0.3 for 30%. For log and multiplicative.

Converted to the standard deviation of \(\theta\), to first order around initial: \(\sigma_\theta\) = relative_stddev / ln 2 for log, \(\sigma_\theta\) = relative_stddev for multiplicative. So both give the same prior as long as relative_stddev is small. For large values they differ: log keeps the parameter positive, at the price of an asymmetric distribution. kipe list-parameters shows the resulting 1σ range.

stddev: float | None = None

Absolute standard deviation, in the parameter’s physical unit. For additive.

reparameterization: Reparameterization | None = None

Overrides the section’s reparameterization for this parameter.

class kipe.options.ParametersOptions(reparameterization: Reparameterization, select: dict[str, ParameterPrior])[source]

Bases: object

Selection, initial estimate and reparameterization of the estimated parameters.

kipe list-parameters shows the names the forward solver accepts.

reparameterization: Reparameterization

How the estimated parameter \(\theta\) relates to the physical value \(\phi\), relative to the initial estimate \(\phi_0\). Default for all parameters in select, each can override it.

Available reparameterizations:

  • log: \(\phi = \phi_0 2^\theta\), starting at \(\theta = 0\), with a relative_stddev. Keeps \(\phi\) positive; the initial estimate must be positive.

  • multiplicative: \(\phi = \phi_0 \theta\), starting at \(\theta = 1\), with a relative_stddev. The initial estimate must not be zero; \(\phi\) can change sign.

  • additive: \(\phi = \phi_0 + \theta\), starting at \(\theta = 0\), with an absolute stddev. \(\theta\) is the deviation from the initial estimate, in the parameter’s physical unit. Accepts any initial estimate, including zero.

select: dict[str, ParameterPrior]

parameter name -> initial estimate and uncertainty. All other parameters keep their nominal values.

Type:

Estimated parameters

class kipe.options.TimeRange(start: float, stop: float, step: float)[source]

Bases: object

Equidistant times from start to stop, both included.

start: float

First time.

stop: float

Last time, included (up to round-off).

step: float

Distance between successive times.

class kipe.options.NumpyDataOptions(type: Literal['numpy'], path: str)[source]

Bases: object

Measurement data in a numpy .npz file.

The file holds the arrays times, shape (n,), and values, shape (n, m): m values at each of the n times.

type: Literal['numpy']

Data format.

path: str

Path to the .npz file. With several noise realizations (synthesis.realizations), it contains the placeholder {realization}, e.g., "data/r{realization}/u.npz" or "data/u_{realization:02d}.npz": each realization has its own file. In quotes, since YAML reads braces in {type: ..., path: ...} as a mapping.

class kipe.options.ArraySamplingOptions(type: Literal['array'] = 'array')[source]

Bases: object

Sample all entries of the observed fields, concatenated in the order of fields.

type: Literal['array'] = 'array'

Kind of spatial sampling.

class kipe.options.DifferenceModelOptions(type: Literal['difference'] = 'difference')[source]

Bases: object

Measured data are the sampled fields; innovation = measured - predicted.

type: Literal['difference'] = 'difference'

Kind of measurement model.

class kipe.options.NoiseOptions(stddev: float, seed: int | None = None)[source]

Bases: object

Measurement noise: assumed by estimation, added by synthesis.

synthesis.noise can set another noise for synthesis to add, e.g., none.

stddev: float

Standard deviation of the noise, in the unit of the data.

seed: int | None = None

Seed of the noise added by synthesis (required there, unless it adds none, see synthesis.noise). The noise of the k-th time of the measurement is drawn from numpy.random.default_rng([seed, k]), so it does not depend on the other times. Measurements must have different seeds.

class kipe.options.MeshSamplingOptions(type: Literal['fenicsx.mesh'], mesh: str, mesh_name: str = 'mesh', element: Literal['P1', 'DG0'] = 'P1', projection: list[float] | Literal['surface_normal'] | None = None, padding: float = 0.0)[source]

Bases: object

Interpolate the observed field onto a measurement mesh (FEniCSx backend).

The field must be a distributed field whose function space the forward solver provides (kipe.fenicsx.forward_solver.SupportsFunctionSpaces). The measurement mesh need not match the solver’s mesh. The data are in the order of the mesh file: by vertex for P1, by cell for DG0, independent of the number of ranks; for a vector field without projection, the components of each vertex or cell one after another.

type: Literal['fenicsx.mesh']

Kind of spatial sampling.

mesh: str

Path to the measurement mesh, an XDMF file.

mesh_name: str = 'mesh'

mesh as dolfinx writes it; meshio writes Grid.

Type:

Name of the mesh in the XDMF file (its grid)

element: Literal['P1', 'DG0'] = 'P1'

vertex values (P1) or cell values (DG0).

Type:

Finite element on the measurement mesh

projection: list[float] | Literal['surface_normal'] | None = None

What is measured of a vector field; all components if not given. A direction, e.g., [0, 0, 1] for the z component (normalized by kipe), or surface_normal: the component along the cell normal of a surface mesh (needs element: DG0, one normal per cell). The normal’s orientation follows the order of each cell’s vertices in the mesh file, as ufl.CellNormal: the right-hand rule for triangles in 3D, the tangent rotated by +90° for lines in 2D; it is outward only if the mesh’s cells are oriented that way.

padding: float = 0.0

Distance, in mesh units, by which a measurement point may lie outside the solver’s mesh, e.g., where a surface mesh cuts slightly across the solver’s boundary. Such a point takes the value of the field in the nearest cell, extrapolated; a point farther out is an error.

class kipe.options.PointSamplingOptions(type: Literal['fenicsx.points'], points: list[list[float]])[source]

Bases: object

Evaluate the observed field at given points (FEniCSx backend).

The field must be a distributed field whose function space the forward solver provides (kipe.fenicsx.forward_solver.SupportsFunctionSpaces). The data are in the order of points; for a vector field, the components of each point one after another.

type: Literal['fenicsx.points']

Kind of spatial sampling.

points: list[list[float]]

Coordinates of the measurement points, one list per point, e.g., [[0.5, 0.5], [0.2, 0.8]] in 2D. Each point must lie in the solver’s mesh.

type kipe.options.SpatialSamplingOptions = ArraySamplingOptions | MeshSamplingOptions | PointSamplingOptions
class kipe.options.MeasurementOptions(fields: list[str], data: ~kipe.options.NumpyDataOptions, noise: ~kipe.options.NoiseOptions, times: list[float] | ~kipe.options.TimeRange | None = None, spatial_sampling: ~kipe.options.SpatialSamplingOptions = <factory>, model: ~kipe.options.DifferenceModelOptions = <factory>)[source]

Bases: object

One measurement: which fields it observes, its data, and how to compare them.

fields: list[str]

State fields the measurement observes.

data: NumpyDataOptions

read by estimation, written by synthesis.

Type:

Where the measured data are

noise: NoiseOptions

Measurement noise.

times: list[float] | TimeRange | None = None

a list or a range. Required by synthesis (the times to generate); for estimation a selection from the times in the data, all if not given.

Type:

Measurement times

spatial_sampling: SpatialSamplingOptions

How the observed fields are brought to the measurement locations; the kind is selected by type, array by default.

model: DifferenceModelOptions

How predicted data are computed from the sampled fields and compared with the data.

class kipe.options.RunTruthOptions(type: Literal['run'] = 'run')[source]

Bases: object

Generate the data by running the forward solver with its nominal parameters.

type: Literal['run'] = 'run'

Source of the true state.

class kipe.options.CheckpointsTruthOptions(type: Literal['checkpoints'], path: str)[source]

Bases: object

Generate the data from the checkpoints of an earlier run of the forward solver.

The solver reads the true states at the measurement times from the checkpoints at path; it needs kipe.forward_solver.SupportsCheckpoints. The run may differ from the model of the forward_solver section (e.g., in its time step or parameters), as long as the solver can read its checkpoints into the model’s state.

type: Literal['checkpoints']

Source of the true state.

path: str

Location of the checkpoints, as the solver understands it (e.g., the output directory of the run).

type kipe.options.TruthOptions = RunTruthOptions | CheckpointsTruthOptions
class kipe.options.SynthesisNoiseOptions(stddev: float)[source]

Bases: object

Noise that synthesis adds to the data of a measurement.

stddev: float

Standard deviation of the noise, in the unit of the data; 0 for noise-free data.

class kipe.options.SynthesisOptions(truth: TruthOptions = <factory>, noise: dict[str, ~kipe.options.SynthesisNoiseOptions]=<factory>, realizations: int = 1)[source]

Bases: object

Generation of synthetic measurement data.

truth: TruthOptions

a run of the forward solver, or its checkpoints.

Type:

Source of the true state the data are generated from

noise: dict[str, SynthesisNoiseOptions]

Noise synthesis adds to the data of the listed measurements (measurement name -> noise), instead of their noise.stddev, which estimation assumes: for noise-free data (stddev: 0) or studies with misspecified noise. Measurements not listed get their noise.stddev.

realizations: int = 1

Number of noise realizations, e.g., for the statistics of the estimates over the noise. Synthesis computes the true state once and writes the data of realizations \(r = 0, \dots, n - 1\), each to the data.path of the measurements with {realization} replaced by \(r\); kipe estimation --realization r estimates from one of them. Realization 0 has the noise of a study without realizations.

type kipe.options.SigmaPoints = Literal['simplex', 'canonical', 'unique']
class kipe.options.EstimationOptions(sigma_points: SigmaPoints = 'simplex', iterations: int = 1)[source]

Bases: object

Parameter estimation with the reduced-order unscented Kalman filter (ROUKF).

sigma_points: SigmaPoints = 'simplex'

Sigma-point stencil, the pattern along which the estimates are perturbed. Each sigma point costs one forward run per measurement time:

  • simplex: \(p + 1\) sigma points, the cheapest.

  • canonical: \(2p\) sigma points, \(\pm\) along each parameter.

  • unique: one sigma point at the estimate, no spread: no correction. A sanity check of the whole pipeline (with the true parameters, the innovations are pure noise).

iterations: int = 1

Number of passes over the measurements. Each pass after the first starts from the previous estimate, with the initial uncertainty.

class kipe.options.StudyOptions(output: OutputOptions, forward_solver: ForwardSolverOptions, parameters: ParametersOptions | None = None, measurements: dict[str, ~kipe.options.MeasurementOptions] | None=None, synthesis: SynthesisOptions = <factory>, estimation: EstimationOptions = <factory>)[source]

Bases: object

All sections of a study file.

output: OutputOptions

Output location and logging.

forward_solver: ForwardSolverOptions

Construction of the forward solver.

parameters: ParametersOptions | None = None

Estimated parameters.

measurements: dict[str, MeasurementOptions] | None = None

name -> measurement. The names appear in logs, output and errors.

Type:

Measurements

synthesis: SynthesisOptions

Generation of synthetic measurement data.

estimation: EstimationOptions

Parameter estimation.

kipe.options.data_path(measurement: MeasurementOptions, realization: int = 0) → str[source]

Return the path to a measurement’s data of one noise realization.

Parameters:
  • measurement – the measurement options

  • realization – number of the noise realization

Returns:

data.path, with {realization} replaced

kipe.options.estimation_directory(study: StudyOptions, realization: int = 0) → Path[source]

Return the output directory of an estimation.

Parameters:
  • study – the study options

  • realization – number of the noise realization the estimation uses

Returns:

<output.path>/estimation; with several realizations, its subdirectory of the realization, e.g., r07, numbered with as many digits as the largest number

Raises:

StudyFileError – if the study has no such realization

kipe.options.load_study(path: str | Path) → StudyOptions[source]

Read and validate a study file.

Parameters:

path – path to the YAML study file

Returns:

validated study options

Raises:
  • StudyFileError – if the file cannot be read, is not valid YAML or is not a mapping

  • pydantic.ValidationError – if the options are invalid

kipe.options.study_schema() → dict[str, Any][source]

Return the JSON schema of the study file, e.g., for editors (kipe schema).

It is pydantic’s schema of StudyOptions, in JSON Schema draft 2020-12, with the options’ docstrings as descriptions.

Returns:

the JSON schema, as a mapping that json.dumps() writes

kipe.options.dump_study(study: StudyOptions, path: str | Path) → None[source]

Write study options as a study file, which load_study() reads back.

All options are written, defaults included, null for those not set. Useful to generate study files from Python, e.g., for a sweep over noise seeds.

Parameters:
  • study – the study options

  • path – where to write the study file