PyTracerLab.model.model module

Model container with a parameter registry.

class PyTracerLab.model.model.Model(dt: float, lambda_: Union[float, np.ndarray], input_series: np.ndarray, production: Optional[Union[bool, Sequence[bool]]] = False, target_series: Optional[np.ndarray] = None, steady_state_input: Optional[Union[float, Sequence[float]]] = None, n_warmup_half_lives: int = 2, n_warmup_steps: int = None, time_steps: Optional[Union[Sequence, np.ndarray]] = None, units: List[Unit] = <factory>, unit_fractions: List[float] = <factory>, param_uncert: Dict[str, List[float, float, float]] = None, param_map: Dict[str, float] = None)[source]

Bases: object

Forward model container with a parameter registry.

The model aggregates units, keeps their mixing fractions, and performs the convolution-based simulation. It also manages an explicit parameter registry that stores current values, initial values, optimizer bounds, and fixed flags per parameter.

Parameters:
  • dt (float) – Time step of the simulation (same units as mtt used by units).

  • lambda (float or ndarray) – Decay constant(s) in 1/time units. Provide a scalar for single-tracer runs or an array-like of length n_tracers for multi-tracer runs.

  • input_series (ndarray) – Forcing time series of shape (N,) for single tracer or (N, K) for K tracers.

  • production (bool or sequence of bool, optional) – If True, simulate production from decay. If a bool, this is a global setting. Use a sequence of bools for per-tracer specification. The order of the sequence must match the order of the columns in the input_series. The input should contain the parent tracer from which production is simulated.

  • target_series (ndarray, optional) – Observed output series of shape (N,) or (N, K); used only for calibration/loss and reporting.

  • steady_state_input (float or sequence of float, optional) – If provided, a warmup of constant input is prepended. Supply a scalar for single-tracer runs or one value per tracer for multi-tracer runs.

  • n_warmup_half_lives (int, optional) – Heuristic warmup scaling in half-lives (kept for compatibility).

  • n_warmup_steps (int, optional) – Number of warmup time steps prepended to the input series. If given, it overrides the warmup steps calculated from n_warmup_half_lives.

Notes

  • Units are added via add_unit(). The method also registers unit parameters into the model’s registry.

  • Bounds are optimization bounds only and can be provided at add time or later via set_bounds().

add_unit(unit: PyTracerLab.model.units.Unit, fraction: float, prefix: str | None = None, bounds: List[Tuple[float, float]] | None = None) None[source]

Add a unit, register its parameters, and set its mixture fraction.

Parameters:
  • unit (Unit) – The unit instance to add.

  • fraction (float) – Mixture fraction of this unit in the overall response. Fractions should sum to ~1 across all units.

  • prefix (str, optional) – Namespace prefix for the unit’s parameters (e.g., "epm"). If omitted, "u{index}" is used in insertion order.

  • bounds (list of (float, float), optional) – Optimizer bounds for the unit’s parameters in the same order as returned by unit.param_values(). If omitted, bounds are left as None and can be supplied later via set_bounds().

Raises:

ValueError – If bounds is provided and its length does not match the number of unit parameters.

dt: float
get_bounds(free_only: bool = False) List[Tuple[float, float]][source]

Return bounds for parameters in registry order.

Raises a ValueError if any addressed parameter has no bounds set.

get_ttds(n_steps: int | None = None) List[numpy.ndarray][source]

Return travel time distributions for all units using current registry values.

Returns:

Dict of unit fractions and travel time distributions for all units. Each travel time distribution is a numpy array with one element per time step. Time steps and time step units correspond to general model settings. Fractions can be used to determine a global model-impulse response from the unit-specific responses.

Return type:

Dict

get_vector(which: str = 'value', free_only: bool = False) List[float][source]

Export parameter values as a flat vector in registry order.

Parameters:
  • which ({"value", "initial"}) – Whether to export current values or initial guesses.

  • free_only (bool, optional) – If True, export only free parameters.

Returns:

Parameter vector following param_keys() order.

Return type:

list of float

input_series: np.ndarray
lambda_: float | np.ndarray
property n_warmup: int

Number of warmup steps prepended to the series.

n_warmup_half_lives: int = 2
n_warmup_steps: int = None
param_keys(free_only: bool = False) List[str][source]

Return parameter keys in a stable order.

Parameters:

free_only (bool, optional) – If True, return only parameters with fixed == False.

Returns:

Fully-qualified parameter keys (e.g., "epm.mtt").

Return type:

list of str

param_map: Dict[str, float] = None
param_uncert: Dict[str, List[float, float, float]] = None
params: Dict[str, ParamRecord]
production: bool | Sequence[bool] | None = False
set_bounds(key: str, bounds: Tuple[float, float]) None[source]

Set optimizer bounds for a single parameter.

Parameters:
  • key (str) – Fully-qualified parameter key (e.g., "epm.mtt").

  • bounds ((float, float)) – Lower and upper search bounds for the optimizer.

set_fixed(key: str, fixed: bool = True) None[source]

Mark a parameter as fixed (not optimized).

set_initial(key: str, value: float) None[source]

Set a single parameter’s initial value used for optimization seeding.

set_param(key: str, value: float) None[source]

Set a single parameter’s current value and update the unit.

This is a convenience wrapper around set_vector() for one value.

set_vector(vec: Sequence[float], which: str = 'value', free_only: bool = False) None[source]

Write a vector into the registry (and units) in registry order.

Parameters:
  • vec (sequence of float) – Values to assign (length must match the number of addressed params).

  • which ({"value", "initial"}) – Destination field to write ("value" also writes through to units).

  • free_only (bool, optional) – If True, write into free parameters only.

simulate() numpy.ndarray[source]

Run the forward model using current registry values.

Returns:

Simulated output aligned with target_series (warmup removed).

Return type:

ndarray

steady_state_input: float | Sequence[float] | None = None
target_series: np.ndarray | None = None
time_steps: Sequence | np.ndarray | None = None
unit_fractions: List[float]
units: List[Unit]
write_report(filename: str, frequency: str, tracer: str | None = None, sim: str | Sequence[str] | None = None, title: str = 'Model Report', include_initials: bool = True, include_bounds: bool = True, convert_mtt_to_years: bool = False) str[source]

Create a simple text report of the current model configuration and fit.

Parameters:
  • filename (str) – Path of the text file to write.

  • frequency (str) – Simulation frequency (e.g., "1h"). This is not checked internally and directly written to the report.

  • tracer (str or sequence of str, optional) – Name(s) of the tracer(s) in the report. If not given, decay constants are shown for all tracers instead of tracer names.

  • sim (ndarray, optional) – Simulated series corresponding to the current parameters. If not provided and target_series is present, the method will call simulate() to compute one.

  • title (str, optional) – Title shown at the top of the report.

  • include_initials (bool, optional) – Whether to include initial values in the parameter table.

  • include_bounds (bool, optional) – Whether to include optimizer bounds in the parameter table.

  • convert_mtt_to_years (bool, optional) – Whether to convert mean travel times to years from months.

Returns:

The full report text that was written to filename.

Return type:

str

Notes

  • Parameters are grouped by their namespace prefix (e.g., "epm" in keys like "epm.mtt").

  • If target_series is available, the report includes the mean squared error (MSE) between the simulation and observations using overlapping, non-NaN entries.