Python API
The novomodelo package is the Python binding of the Novomodelo solver (import novomodelo). This page lists the signature, behaviour, return shape and raised exception classes of each public name in the novomodelo package, novomodelo.run, novomodelo.io, novomodelo.results, novomodelo.errors and novomodelo.schema, and the fields of the data classes in novomodelo.model and the typed result dicts in novomodelo._types. To install the package, see Installation; for a guided first session, see the Python Quickstart.
Conventions used by every entry:
- Paths accept
strorpathlib.Path. - The package runs in a single process. It never initialises MPI, and it releases the GIL (Python’s global interpreter lock) while the solver computes.
- Warning diagnostics are written to file descriptor 2.
contextlib.redirect_stderrdoes not capture them. - Each heading is the qualified name of a symbol and is the anchor of that entry (
#novomodelostudytrainfornovomodelo.Study.train).
novomodelo
Section titled “novomodelo”novomodelo.__version__
Section titled “novomodelo.__version__”__version__: strThe package version string, for example 0.18.0.
novomodelo.version_info
Section titled “novomodelo.version_info”def version_info() -> dict[str, Any]: ...Describes the running build. The dict has the keys version (equal to novomodelo.__version__), solver (the LP backend and its version), comm (always local, because the package is single-process), zstd (enabled), arch (target architecture and operating system) and build (debug or release).
Returns: the build description dict.
import novomodelo
print(novomodelo.version_info()){'version': '0.18.0', 'solver': 'HiGHS 1.13.1', 'comm': 'local', 'zstd': 'enabled', 'arch': 'x86_64-linux', 'build': 'release'}novomodelo.Study
Section titled “novomodelo.Study”class Study: def __init__( self, case_dir: Union[str, Path], output_dir: Optional[Union[str, Path]] = None, threads: Optional[int] = None, config_overrides: Optional[Mapping[str, Any]] = None, ) -> None: ...A loaded, reusable study. Construction loads the case, resolves the configuration, runs the stochastic and hydro-model preprocessing, and writes training/scaling_report.json, training/model_provenance.json and training/hydro_models.json (plus the stochastic exports when enabled) under output_dir. Validation happens here: a case or configuration that fails validation raises from the constructor, so a constructed Study is valid.
Parameters
case_dir: the case directory.output_dir: where results are written. Defaults to<case_dir>/output.threads: worker threads for training and simulation, at least 1.Noneruns on one thread, not on every core. The value is stored fortrainandsimulateand is not used while loading.config_overrides: a flat mapping of dotted keys deep-merged intoconfig.json.{"training.enabled": False}sets that one key and keeps its sibling keys. A nested value under a single key replaces the whole object at that key, so{"training": {"enabled": False}}discards every othertrainingkey and fails validation. An unknown key fails validation. For the keys, see Configuration.
Raises
OSError(builtin) whencase_dirdoes not exist, before any work.ValueError(builtin) whenthreadsis0, or whenconfig_overrideshas a non-strkey or an unsupported value type.novomodelo.errors.ValidationErrorfor a configuration override, parse or read failure, for a missing or unreadable case file, and for a schema, parse or constraint failure in the case data, for a refusedpolicy.path, and for an invalid-input setup failure such as an unsupported stochastic model (no valid historical windows found).novomodelo.errors.CaseIoErrorwhen writing one of those files fails, such as under an unwritableoutput_dir.novomodelo.errors.SolverErrorfor a solver or internal failure during preprocessing or construction.
novomodelo.Study.output_dir
Section titled “novomodelo.Study.output_dir”@propertydef output_dir(self) -> str: ...The resolved output directory as a string.
novomodelo.Study.system
Section titled “novomodelo.Study.system”@propertydef system(self) -> System: ...The loaded novomodelo.model.System. The property shares the study’s system; it does not copy it.
novomodelo.Study.stochastic
Section titled “novomodelo.Study.stochastic”@propertydef stochastic(self) -> StochasticSummary: ...The structural stochastic summary, a dict fixed at construction. Its shape is novomodelo._types.StochasticSummary.
novomodelo.Study.hydro_models
Section titled “novomodelo.Study.hydro_models”@propertydef hydro_models(self) -> HydroModelsSummary: ...The structural hydro-model summary, a dict fixed at construction. Its shape is novomodelo._types.HydroModelsSummary; for the 1dtoy scaffold it reads {'n_constant': 1, 'n_fpha': 0, 'total_planes': 0, 'n_evaporation': 0, 'n_no_evaporation': 1}.
novomodelo.Study.provenance
Section titled “novomodelo.Study.provenance”@propertydef provenance(self) -> ProvenanceReport: ...The model-provenance report, a dict fixed at construction. Its shape is novomodelo._types.ProvenanceReport.
novomodelo.Study.validate
Section titled “novomodelo.Study.validate”def validate(self) -> dict[str, Any]: ...Reports the warnings captured during construction and checks the configured policy load against the study’s output_dir, without re-reading the case.
Returns: {"valid": True, "errors": [], "warnings": [...]}. The result has the same shape as novomodelo.io.validate. valid is False, with the error novomodelo.io.validate reports for that directory, when the configured policy load fails; policy-load warnings join warnings.
novomodelo.Study.train
Section titled “novomodelo.Study.train”def train( self, on_iteration: Optional[Callable[[dict[str, Any]], Any]] = None,) -> "Policy": ...Trains an SDDP policy against the study’s in-memory setup and returns a novomodelo.Policy. The call writes the policy to <output_dir>/<policy.path> (default policy/) and the training artifacts under training/. The GIL is released while the solver computes; the callback runs on a separate thread that takes the GIL only at iteration boundaries.
Parameters
on_iteration: called once per iteration boundary with a dict holdingkind,iteration,lower_bound,upper_bound,gapandwall_time_ms.gapis the raw relative gap, not multiplied by 100. A truthy return requests a cooperative stop at a later iteration boundary; the stop is asynchronous, so the iteration at which the run ends is not fixed. An exception raised by the callback is re-raised verbatim after the artifacts are written.
A stop requested by the callback, or an exception raised by it, ends the run with convergence.termination_reason set to "graceful_shutdown" and status set to "partial" in training/metadata.json unless the stopping rules or the iteration budget also ended it at that iteration; see training/metadata.json.
Returns: a novomodelo.Policy. With training.enabled set to false, train returns a zero-iteration policy that holds no cuts; simulate rejects it. Load a trained policy with load_policy instead.
A second train call on the same Study runs against a setup that already carries the first call’s cuts, so it is not a fresh training. On the 1dtoy scaffold the second call raises SolverError (basis row count mismatch).
Raises
novomodelo.errors.SolverErrorfor a solver initialisation failure or a training failure the solver classifies.novomodelo.errors.ValidationErrorfor a missing prior policy directory under warm start or resume (Policy directory not found: ...), and for a refused boundary or training input.novomodelo.errors.PolicyIncompatibleErrorwhen the warm-start or resume checkpoint atpolicy.pathfails the Policy Load Contract, apolicy.boundarycheckpoint was written by another software or version, or a checkpoint’smanifest.binis missing or cannot be decoded.novomodelo.errors.CaseIoErrorwhen writing the policy or a training artifact fails; a periodic-checkpoint write failure raisesFileNotFoundError,CaseIoError,novomodelo.errors.OutputErrororValidationErrorby its cause.novomodelo.errors.InternalErrorwhen the callback thread panics.- Any exception raised by the callback, and
KeyboardInterrupt, re-raised after the artifacts are written.
novomodelo.Study.load_policy
Section titled “novomodelo.Study.load_policy”def load_policy( self, output_dir: Optional[Union[str, Path]] = None,) -> "Policy": ...Reads a policy checkpoint from <output_dir>/<policy.path>/ and returns a novomodelo.Policy that simulate accepts in the same way as a trained one. The checkpoint is always checked against the study’s state dimension, stage count and terminal entity manifest.
Parameters
output_dir: the directory that holds the policy. Defaults to the study’soutput_dir.
Returns: the loaded novomodelo.Policy.
Raises
novomodelo.errors.ValidationErrorwhen the policy directory is missing (Policy directory not found: ...).novomodelo.errors.PolicyIncompatibleErrorwhen the checkpoint cannot be read, decoded or reconstructed, or policy validation rejects it.novomodelo.errors.CaseIoErrorwhen a checkpoint file cannot be opened.
novomodelo.Study.simulate
Section titled “novomodelo.Study.simulate”def simulate( self, policy: "Policy", output_dir: Optional[Union[str, Path]] = None,) -> dict[str, Any]: ...Runs the simulation phase with policy and writes the simulation/ artifacts. Each call writes a fresh output set, and one policy can feed repeated calls.
Parameters
policy: anovomodelo.Policyfromtrainorload_policy.output_dir: where thesimulation/directory is written. Defaults to the study’soutput_dir.
Returns: {"n_scenarios": int, "completed": int}, for example {'n_scenarios': 10, 'completed': 10}.
Raises
novomodelo.errors.SolverErrorwhen the policy holds no cuts (Policy has no cuts to simulate; when training is disabled, call Study.load_policy() to load a trained policy before simulate()), or on a simulation workspace initialisation failure.novomodelo.errors.SimulationErrorfor a simulation failure.novomodelo.errors.CaseIoErrorfor a writer failure.novomodelo.errors.InternalErrorwhen the worker thread panics.
novomodelo.Policy
Section titled “novomodelo.Policy”class Policy: ...A trained or loaded policy: the Benders cuts that approximate the future cost of each stage, together with the data novomodelo.Study.simulate needs. The class has no Python constructor; novomodelo.Policy() raises TypeError (cannot create 'builtins.Policy' instances). Obtain a handle from novomodelo.Study.train or novomodelo.Study.load_policy.
Every stage argument is 0-based: stage t of the 1-based numbering used in the methodology is stage = t - 1.
evaluate and cut_matrix read the in-memory cut pool, which holds intercepts and coefficients in the solver’s cost scale: the checkpoint’s currency-unit values divided by the pool’s cost_scale_factor (modeling.cost_scale_factor, 1000000.0 by default). Multiply by cost_scale_factor to get currency units. final_lower_bound, final_upper_bound and the values returned by novomodelo.results.load_policy are in currency units. For the conversion, see Cost-Scale Canonicalization.
novomodelo.Policy.iterations
Section titled “novomodelo.Policy.iterations”@propertydef iterations(self) -> int: ...The number of completed training iterations. A policy from load_policy reports the checkpoint’s completed_iterations; a training-disabled policy reports 0.
novomodelo.Policy.final_lower_bound
Section titled “novomodelo.Policy.final_lower_bound”@propertydef final_lower_bound(self) -> float: ...The final lower bound, in currency units. A policy from load_policy reports the checkpoint’s recorded final lower bound; a training-disabled policy reports 0.0.
novomodelo.Policy.final_upper_bound
Section titled “novomodelo.Policy.final_upper_bound”@propertydef final_upper_bound(self) -> float: ...The final mean upper bound, in currency units. A policy from load_policy reports the checkpoint’s recorded upper bound (the last training iteration’s value, not a running best), or inf when the checkpoint records none; a training-disabled policy reports inf.
novomodelo.Policy.evaluate
Section titled “novomodelo.Policy.evaluate”def evaluate(self, stage: int, state: Sequence[float]) -> float: ...Evaluates the future-cost function at state: the maximum over the stage’s active cuts of intercept + coeffs · state. The value is in the solver’s cost scale, not in currency (see novomodelo.Policy). A stage with no active cut returns float('-inf'); the last stage of the 1dtoy case holds none.
Parameters
stage: the 0-based stage index.state: the incoming state, one float per state dimension of the policy (1for the 1dtoy case).
Returns: the future-cost value as a float.
Raises
IndexError(builtin) whenstageis at or beyond the stage count (stage 4 out of range (policy has 4 stages)).ValueError(builtin) whenstatehas the wrong length (state has length 2, expected 1 (policy state dimension)).OverflowError(builtin) whenstageis negative.
novomodelo.Policy.cut_matrix
Section titled “novomodelo.Policy.cut_matrix”def cut_matrix(self, stage: int) -> tuple[Any, Any]: ...Returns the active cuts of one stage as two NumPy arrays, in ascending slot order. Read cut k as θ ≥ intercepts[k] + coeffs[k] · state. The coefficients are the stored values, not negated. The values are in the solver’s cost scale, not in currency (see novomodelo.Policy).
Parameters
stage: the 0-based stage index.
Returns: (intercepts, coeffs), two float64 arrays of shapes (n_cuts,) and (n_cuts, dim), where dim is the policy state dimension. A stage with no active cut returns shapes (0,) and (0, dim).
Raises
IndexError(builtin) whenstageis at or beyond the stage count.OverflowError(builtin) whenstageis negative.ImportError(builtin) when NumPy is not installed.
import novomodeloimport novomodelo.results
study = novomodelo.Study("case", output_dir="output")policy = study.train()
intercepts, coeffs = policy.cut_matrix(0)print(intercepts.shape, coeffs.shape)print(policy.evaluate(0, [50.0]))
saved = novomodelo.results.load_policy("output")stage0 = saved["stage_cuts"][0]print(intercepts[0], stage0["cuts"][0]["intercept"])print(stage0["cost_scale_factor"])print(round(stage0["cuts"][0]["intercept"] / intercepts[0]))(128,) (128, 1)21.65008409203045431.669512704831718 31669512.704831721000000.01000000The saved intercept is cost_scale_factor times the in-memory one. The evaluate result of about 21.65 is therefore about 21650084 in currency units.
novomodelo.write_policy_checkpoint
Section titled “novomodelo.write_policy_checkpoint”def write_policy_checkpoint( path: Union[str, Path], stage_cuts: Sequence[Mapping[str, Any]], metadata: Mapping[str, Any], stage_bases: Optional[Sequence[Mapping[str, Any]]] = None, stage_states: Optional[Sequence[Mapping[str, Any]]] = None, inflow_lag_depth: Optional[int] = None,) -> None: ...Writes a policy checkpoint to path from plain Python dicts. stage_cuts and metadata mirror the "stage_cuts" and "metadata" keys that novomodelo.results.load_policy returns, so a loaded checkpoint round-trips. stage_bases and stage_states default to empty. The checkpoint always records the running novomodelo version; software, software_version or novomodelo_version keys in metadata are ignored. For the dict shapes, see Write a checkpoint, Round-trip example and Input validation.
inflow_lag_depth = N with N > 0 reserves N inflow-lag state slots per storage hydro in every stage and places each cut’s inflow_lag_coefficients at their (hydro, depth) positions. Use it to author a boundary policy for a converted case with no PAR model to infer the depth from. Absent or 0, the checkpoint is identical to one written without the argument.
Returns: None.
Raises
ValueError(builtin) when a cut’scoefficientslength differs from its stage’sstate_dimension, when a stage’s state data length differs fromcount * state_dimension, when a cut carriesinflow_lag_coefficientswithout a positiveinflow_lag_depth, or, withinflow_lag_depthset, when a stage manifest lacks a leading storage block or an inflow-lag coefficient has no slot to be placed in.novomodelo.errors.ValidationError(aValueError) whenpath(for a link, its target) or its.stagingor.previoussibling holds an entry no checkpoint writer leaves there (refusing to write a checkpoint: ...); nothing on disk changes.- A
novomodelo.errorsclass mapped from the underlying write failure for any other error, such asnovomodelo.errors.OutputError.
novomodelo.run
Section titled “novomodelo.run”novomodelo.run.run
Section titled “novomodelo.run.run”def run( case_dir: Union[str, Path], output_dir: Optional[Union[str, Path]] = None, threads: Optional[int] = None, config_overrides: Optional[Mapping[str, Any]] = None, on_iteration: Optional[Callable[[dict[str, Any]], Optional[bool]]] = None,) -> RunResult: ...Loads a case, trains, simulates when the configuration enables it, and writes the results. One novomodelo.Study drives the call. The phases that run follow the configuration, in three branches.
Parameters
case_dir,output_dir,threads,config_overrides: as fornovomodelo.Study.on_iteration: as fornovomodelo.Study.train.
Returns: a dict of shape novomodelo._types.RunResult. All keys are present on every branch; the values below differ by branch.
| Branch | converged | iterations | lower_bound | upper_bound | gap_percent | simulation |
|---|---|---|---|---|---|---|
| Training enabled | True when the stopping rules ended training and a gap or bound_stalling rule triggered at that iteration; False for any other stopping reason | iterations run | final lower bound | final upper bound | gap times 100 | the summary dict, or None if simulation is off |
| Training disabled, simulation enabled (simulation-only) | False | 0 | the checkpoint’s lower bound | the checkpoint’s recorded upper bound (the last training iteration’s value), None if not finite | None | the summary dict |
| Both disabled | False | 0 | 0.0 | None | None | None |
gap_percent is the relative gap times 100; see Optimality gap. A simulation-only run loads the policy from <output_dir>/<policy.path>/ (default policy/); see Simulation-Only Mode. The stochastic, hydro_models and provenance keys hold the same summaries as the Study properties.
A Ctrl-C during the run is delivered as KeyboardInterrupt after the call returns to the interpreter, not at an iteration boundary. To end a run early, return a truthy value from on_iteration.
A stop requested by on_iteration records convergence.termination_reason as "graceful_shutdown" in training/metadata.json unless the stopping rules or the iteration budget also ended it at that iteration; see training/metadata.json.
Raises
OSError(builtin) whencase_dirdoes not exist.ValueError(builtin) whenthreadsis0, or whenconfig_overridesis malformed.- The classes listed for
novomodelo.Study,novomodelo.Study.trainandnovomodelo.Study.simulate, according to the phase that fails. - Any exception raised by the callback, re-raised verbatim after the artifacts are written.
import novomodelo.run
def on_iteration(info): print(info) return info["iteration"] >= 3 # a truthy return requests a stop
novomodelo.run.run("case", output_dir="output", on_iteration=on_iteration){'kind': 'iteration', 'iteration': 1, 'lower_bound': 5060208.389407688, 'upper_bound': 17125951.86504142, 'gap': 2.3844360838756016, 'wall_time_ms': 3}{'kind': 'iteration', 'iteration': 2, 'lower_bound': 6970347.641468819, 'upper_bound': 583065.7603054191, 'gap': -0.9163505480219415, 'wall_time_ms': 5}{'kind': 'iteration', 'iteration': 3, 'lower_bound': 7171550.050534338, 'upper_bound': 12565763.664663631, 'gap': 0.7521684400330416, 'wall_time_ms': 7}wall_time_ms varies between runs. The stop requested at iteration 3 takes effect at a later iteration boundary, so the run can finish after more than three iterations.
novomodelo.io
Section titled “novomodelo.io”novomodelo.io.load_case
Section titled “novomodelo.io.load_case”def load_case(path: Union[str, Path]) -> model.System: ...Loads and validates a case directory through the full validation pipeline and returns a novomodelo.model.System. A relative path resolves from the process working directory.
Parameters
path: the case directory.
Returns: the validated novomodelo.model.System.
Raises
OSError(builtin) when the directory does not exist.novomodelo.errors.ValidationErrorfor a missing or unreadable case file (reported as a[FileNotFound]constraint violation) and for a parse, schema or constraint failure.
novomodelo.io.validate
Section titled “novomodelo.io.validate”def validate( path: Union[str, Path], config_overrides: Optional[Mapping[str, Any]] = None, *, output_dir: Optional[Union[str, Path]] = None,) -> dict[str, Any]: ...Runs the full validation pipeline (case structure, schema, configuration, stochastic preparation, hydro models, generic constraints, study construction, boundary reconciliation when configured, and the configured warm-start, resume or simulation-only policy load from <output_dir>/<policy.path>) and stops at the first failure. validate returns failures as data; it does not raise for an invalid case.
Parameters
path: the case directory. A missing directory yields a failure entry of kindIoError.config_overrides: as fornovomodelo.Study.output_dir: keyword-only; the directory the policy load reads from. Defaults to<path>/output; a relative path resolves from the working directory. Nothing is written, and an absent directory is not created.
Returns: a dict with valid (bool), errors (a list of {"kind", "message"} dicts; empty when valid is True) and warnings (a list of {"kind", "message", "file", "entity"} dicts). Warnings do not change valid.
Raises
ValueError(builtin) only whenconfig_overridesis malformed.
import novomodelo.io
report = novomodelo.io.validate("case")print(report){'valid': True, 'errors': [], 'warnings': []}novomodelo.results
Section titled “novomodelo.results”Loaders for the artifacts a run writes under its output directory. Every function takes output_dir, the directory that holds training/, simulation/, policy/ and stochastic/. A relative path resolves from the process working directory, and a missing output_dir raises FileNotFoundError. NumPy and pyarrow are optional dependencies, imported only by the functions that return their types. To read the numbers, see Convergence & Diagnostics.
novomodelo.results.load_results
Section titled “novomodelo.results.load_results”def load_results(output_dir: Union[str, Path]) -> dict[str, Any]: ...Reads the run manifests.
Parameters
output_dir: the output directory.
Returns: a dict with two sub-dicts.
"training":"metadata", the parsedtraining/metadata.json;"manifest", an alias of"metadata"with equal contents;"convergence_path"and"timing_path", the absolute paths oftraining/convergence.parquetandtraining/timing/iterations.parquet; and"complete"."simulation":"manifest", the parsedsimulation/metadata.json, orNonewhen that file is absent (simulation did not run); and"complete".
Both "complete" values are _SUCCESS marker flags, not a verdict on the run. training["complete"] is always True in a returned dict, because the call raises when the training/_SUCCESS marker is absent. simulation["complete"] is True when simulation/_SUCCESS exists and False otherwise. To learn how a run ended, read the metadata dicts; see Metadata Files.
Raises
FileNotFoundError(builtin) whenoutput_dirortraining/_SUCCESSis missing.ValueError(builtin) when a metadata file is not valid JSON.OSError(builtin) for other read failures.
novomodelo.results.load_convergence
Section titled “novomodelo.results.load_convergence”def load_convergence(output_dir: Union[str, Path]) -> Any: ...Reads training/convergence.parquet; for the columns, see training/convergence.parquet.
Parameters
output_dir: the output directory.
Returns: a list[dict] with one dict per row (one per training iteration), keyed by the file’s own columns. The list is empty when the file has zero rows.
Raises
FileNotFoundError(builtin) whenoutput_dirortraining/convergence.parquetis missing.OSError(builtin) for other I/O failures and Parquet decoding failures.
import novomodelo.resultsimport novomodelo.run
novomodelo.run.run("case", output_dir="output")
rows = novomodelo.results.load_convergence("output")print(len(rows))print(sorted(rows[0]))128['cuts_active', 'cuts_added', 'cuts_removed', 'forward_passes', 'gap_percent', 'iteration', 'lower_bound', 'lp_solves', 'mean_rows_in_lp', 'time_backward_ms', 'time_forward_ms', 'time_total_ms', 'upper_bound', 'upper_bound_kind', 'upper_bound_std']novomodelo.results.load_convergence_arrow
Section titled “novomodelo.results.load_convergence_arrow”def load_convergence_arrow(output_dir: Union[str, Path]) -> Any: ...Reads the same file as novomodelo.results.load_convergence without building Python objects per value.
Parameters
output_dir: the output directory.
Returns: a pyarrow.Table with the schema of training/convergence.parquet.
Raises
FileNotFoundError(builtin) whenoutput_dirortraining/convergence.parquetis missing.OSError(builtin) for Parquet decoding failures or Arrow serialisation errors.ImportError(builtin) when pyarrow is not installed.
novomodelo.results.load_simulation
Section titled “novomodelo.results.load_simulation”def load_simulation( output_dir: Union[str, Path], entity_type: Optional[str] = None,) -> Any: ...Reads the Hive-partitioned Parquet files under simulation/; see Simulation Output and Hive Partitioning. Each row dict carries a scenario_id key, read from the file’s own scenario_id column or, when the file has none, from the partition path, and rows are ordered by scenario_id.
Parameters
output_dir: the output directory.entity_type: the name of asimulation/subdirectory, such as"costs","hydros"or"thermals".Noneloads every entity directory that exists.
Returns: with entity_type, a list[dict] of row dicts. Without it, a dict[str, list[dict]] keyed by entity type (for the 1dtoy case: buses, costs, hydro_bus_generation, hydros, inflow_lags and thermals).
Raises
FileNotFoundError(builtin) whenoutput_dir,simulation/or the named entity directory is missing.OSError(builtin) for corrupt Parquet files and other I/O failures.
novomodelo.results.load_simulation_arrow
Section titled “novomodelo.results.load_simulation_arrow”def load_simulation_arrow( output_dir: Union[str, Path], entity_type: Optional[str] = None,) -> Any: ...Reads the same files as novomodelo.results.load_simulation as Arrow tables, with all scenarios concatenated in ascending scenario_id order.
Parameters
output_dir,entity_type: as forload_simulation.
Returns: with entity_type, a pyarrow.Table. Without it, a dict[str, pyarrow.Table] keyed by entity type. The first column of each table is scenario_id (the first three columns of the 1dtoy costs table are scenario_id, stage_id and node_id).
Raises
FileNotFoundError(builtin) whenoutput_dir,simulation/or the named entity directory is missing.OSError(builtin) for corrupt Parquet files or Arrow serialisation errors.ImportError(builtin) when pyarrow is not installed.
novomodelo.results.load_policy
Section titled “novomodelo.results.load_policy”def load_policy( output_dir: Union[str, Path], policy_subdir: str = "policy",) -> Any: ...Reads the policy checkpoint from <output_dir>/<policy_subdir> into plain dicts; see Policy Checkpoint. The function does not check the novomodelo version that wrote the checkpoint. For the key tables, see Load a policy.
Parameters
output_dir: the output directory.policy_subdir: the checkpoint directory underoutput_dir. Pass the configuredpolicy.pathwhen it is notpolicy.
Returns: a dict with the keys metadata, stage_cuts and stage_bases. stage_cuts holds one entry per cut pool, one per stage on a stage chain (four for the 1dtoy case). Intercepts and coefficients are in currency units; see novomodelo.Policy for the scaled values of cut_matrix.
Raises
FileNotFoundError(builtin) whenoutput_diris missing, or the policy directory (for a link, its target) is missing and neither<policy>.stagingnor<policy>.previousholds amanifest.bin.novomodelo.errors.OutputError(anOSError) when a checkpoint buffer cannot be read.
novomodelo.results.load_stochastic
Section titled “novomodelo.results.load_stochastic”def load_stochastic(output_dir: Union[str, Path]) -> Stochastic: ...Reads the fitted stochastic model from stochastic/inflow_ar_coefficients.parquet and stochastic/noise_openings.parquet; see Stochastic Artifacts. Those files exist only when the run had exports.stochastic enabled. Constructing the handle does not need NumPy.
Parameters
output_dir: the output directory.
Returns: a novomodelo.results.Stochastic handle.
Raises
FileNotFoundError(builtin) whenoutput_diror a required file is missing. Without the exports the message reads.../stochastic/inflow_ar_coefficients.parquet not found — stochastic artifacts are written only when exports.stochastic is enabled in config.json.OSError(builtin) when a Parquet file cannot be decoded.
novomodelo.results.Stochastic
Section titled “novomodelo.results.Stochastic”class Stochastic: ...A read-only view of a run’s fitted stochastic model. The class has no Python constructor; novomodelo.results.Stochastic() raises TypeError. Obtain a handle from novomodelo.results.load_stochastic.
novomodelo.results.Stochastic.par_coefficients
Section titled “novomodelo.results.Stochastic.par_coefficients”def par_coefficients(self) -> Any: ...Returns the fitted PAR(p) coefficients, in the row order of stochastic/inflow_ar_coefficients.parquet.
Returns: a float64 NumPy array of shape (n_rows, 4) with the columns [hydro_id, stage_id, lag, coefficient]. The three integer columns are cast to float64, and lag is 1-based. The array has zero rows for a case with AR order 0, such as the 1dtoy case.
Raises
ImportError(builtin) when NumPy is not installed.
novomodelo.results.Stochastic.opening_tree
Section titled “novomodelo.results.Stochastic.opening_tree”def opening_tree(self, stage: int) -> Any: ...Returns the noise openings of one stage from stochastic/noise_openings.parquet. Row k is the noise vector of opening k.
Parameters
stage: the 0-based stage index.
Returns: a float64 NumPy array of shape (n_openings, dim), for example (10, 1) at any stage of the 1dtoy case.
Raises
IndexError(builtin) when the stage is absent from the file (stage 9 not present in the opening tree (valid stages are 0..=3)).OverflowError(builtin) whenstageis negative.ImportError(builtin) when NumPy is not installed.
novomodelo.model
Section titled “novomodelo.model”The classes in novomodelo.model are frozen, read-only views that novomodelo.io.load_case and novomodelo.Study.system return; a System cannot be constructed from Python, and assigning to a field raises AttributeError. Each entity list holds its items in canonical (operational_start_date, id) order, and the field meanings follow the case files, whose JSON Schemas are listed in JSON Schemas.
Each class reports __module__ as builtins; reach it as novomodelo.model.<Name>. Two reads of the same list can return different Python objects for the same entity, so compare entities by field values, not by identity (is).
novomodelo.model.System
Section titled “novomodelo.model.System”The loaded system: entity counts and one list per entity class.
| Field | Type | Description |
|---|---|---|
n_buses | int | The number of buses. |
n_hydros | int | The number of hydro plants. |
n_lines | int | The number of transmission lines. |
n_stages | int | The number of stages, study and pre-study. |
n_thermals | int | The number of thermal plants. |
buses | list[Bus] | The buses, in canonical order. |
hydros | list[Hydro] | The hydro plants, in canonical order. |
lines | list[Line] | The transmission lines, in canonical order. Empty when the case has none. |
thermals | list[Thermal] | The thermal plants, in canonical order. |
contracts | list[EnergyContract] | The energy contracts, in canonical order. Empty when the case has none. |
pumping_stations | list[PumpingStation] | The pumping stations, in canonical order. Empty when the case has none. |
non_controllable_sources | list[NonControllableSource] | The non-controllable sources, in canonical order. Empty when the case has none. |
import novomodelo.io
system = novomodelo.io.load_case("case")print(system.n_hydros)print([h.name for h in system.hydros])print(system.hydros[0].max_storage_hm3)1['UHE1']1000.0novomodelo.model.Bus
Section titled “novomodelo.model.Bus”An electrical network node where energy balance is maintained.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
deficit_segments | list[dict] | The piecewise-linear deficit cost segments, one dict per segment with the keys depth_mw (float, or None for the final unbounded segment) and cost_per_mwh (float, $/MWh). |
excess_cost | float | The cost of absorbing surplus generation, in $/MWh. |
novomodelo.model.Line
Section titled “novomodelo.model.Line”A transmission interconnection that carries power in both directions between two buses.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
source_bus_id | int | The id of the bus at the source end. |
target_bus_id | int | The id of the bus at the target end. |
direct_capacity_mw | float | The maximum flow from source to target, in MW. |
reverse_capacity_mw | float | The maximum flow from target to source, in MW. |
losses_percent | float | The transmission losses as a percentage; 2.5 means 2.5%. |
exchange_cost | float | The regularization cost per MWh exchanged, in $/MWh. |
novomodelo.model.Thermal
Section titled “novomodelo.model.Thermal”A thermal power plant with a scalar marginal cost.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
bus_id | int | The id of the bus the plant feeds. |
min_generation_mw | float | The minimum electrical generation (minimum stable load), in MW. |
max_generation_mw | float | The maximum electrical generation (installed capacity), in MW. |
cost_per_mwh | float | The marginal cost of generation, in $/MWh. |
novomodelo.model.Hydro
Section titled “novomodelo.model.Hydro”A hydroelectric plant with reservoir storage. Plants form a cascade through downstream_id.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
downstream_id | Optional[int] | The id of the next hydro in the cascade. None for a plant with no downstream plant. |
min_storage_hm3 | float | The minimum operational storage (dead volume), in hm³. |
max_storage_hm3 | float | The maximum operational storage (flood control level), in hm³. |
min_turbined_m3s | float | The minimum turbined flow, in m³/s. |
max_turbined_m3s | float | The maximum turbined flow (installed turbine capacity), in m³/s. |
productivity_mw_per_m3s | Optional[float] | Always None. The productivity is resolved per stage from the production-model files. |
novomodelo.model.EnergyContract
Section titled “novomodelo.model.EnergyContract”A bilateral energy contract with an external system.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
operational_start_date | str | The date the entity enters service, as an ISO 8601 YYYY-MM-DD string. |
bus_id | int | The id of the bus the contract connects to. |
contract_type | str | The direction of energy flow: "import" or "export". |
entry_stage_id | Optional[int] | The stage index at which the contract enters service. None when it is active from the first stage. |
exit_stage_id | Optional[int] | The stage index at which the contract expires. None when it never expires. |
price_per_mwh | float | The contract price, in $/MWh. A negative value represents export revenue. |
min_mw | float | The minimum contracted power, in MW. |
max_mw | float | The maximum contracted power, in MW. |
novomodelo.model.PumpingStation
Section titled “novomodelo.model.PumpingStation”A pumping station that transfers water between hydro reservoirs.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
operational_start_date | str | The date the entity enters service, as an ISO 8601 YYYY-MM-DD string. |
bus_id | int | The id of the bus the station draws power from. |
source_hydro_id | int | The id of the hydro the pumped water is taken from. |
destination_hydro_id | int | The id of the hydro that receives the pumped water. |
entry_stage_id | Optional[int] | The stage index at which the station enters service. None when it is active from the first stage. |
exit_stage_id | Optional[int] | The stage index at which the station is decommissioned. None when it is never decommissioned. |
consumption_mw_per_m3s | float | The power consumption per unit of pumped flow, in MW/(m³/s). |
min_flow_m3s | float | The minimum pumped flow, in m³/s. |
max_flow_m3s | float | The maximum pumped flow (installed pump capacity), in m³/s. |
novomodelo.model.NonControllableSource
Section titled “novomodelo.model.NonControllableSource”An intermittent generation source that is not dispatched.
| Field | Type | Description |
|---|---|---|
id | int | The entity id from the case file. |
name | str | The entity name from the case file. |
operational_start_date | str | The date the entity enters service, as an ISO 8601 YYYY-MM-DD string. |
bus_id | int | The id of the bus the source feeds. |
entry_stage_id | Optional[int] | The stage index at which the source enters service. None when it is active from the first stage. |
exit_stage_id | Optional[int] | The stage index at which the source is decommissioned. None when it is never decommissioned. |
max_generation_mw | float | The maximum generation (installed capacity), in MW. |
allow_curtailment | bool | Whether the LP may curtail the source. False is the must-run regime. |
curtailment_cost | float | The resolved cost of curtailed generation, in $/MWh. Unused when allow_curtailment is False. |
novomodelo.errors
Section titled “novomodelo.errors”Every leaf class subclasses novomodelo.errors.NovomodeloError and one builtin exception, and its __module__ is novomodelo.errors. NovomodeloError itself subclasses Exception directly and reports __module__ as errors. For the per-kind messages and exit codes, see Error Codes; for which policy-load path raises which class, see Policy-load errors.
novomodelo.errors.NovomodeloError
Section titled “novomodelo.errors.NovomodeloError”class NovomodeloError(Exception): ...The base class of every Novomodelo exception. Catch the typed class for one failure kind, the builtin base (ValueError, OSError, RuntimeError) to keep builtin-style handling, or NovomodeloError for all of them.
novomodelo.errors.ValidationError
Section titled “novomodelo.errors.ValidationError”class ValidationError(NovomodeloError, ValueError): ...Case data or configuration failed validation. Raised for an error of the invalid-input class, whatever its message prefix (a refused policy.path, a missing prior policy directory, and a checkpoint write refused for its manifest or a foreign entry included), and for message prefixes config override error, config parse error, config read error and setup validation error, and for parse, schema and constraint failures in the case data. It also covers a missing or unreadable case file.
novomodelo.errors.PolicyIncompatibleError
Section titled “novomodelo.errors.PolicyIncompatibleError”class PolicyIncompatibleError(NovomodeloError, ValueError): ...A policy is incompatible with the current system or build. Raised for an error of the incompatible-policy class: a warm-start, resume or simulation-only checkpoint refused for its software, version or a failed contract check, a boundary checkpoint refused for its software or version, a warm-start, resume or simulation-only checkpoint file that is missing or cannot be decoded, and an FCF construction failure.
novomodelo.errors.CaseIoError
Section titled “novomodelo.errors.CaseIoError”class CaseIoError(NovomodeloError, OSError): ...A filesystem read or write failure while loading or writing a case. Raised for message prefixes output write error and policy checkpoint error. It also covers a policy checkpoint read that the operating system refuses.
novomodelo.errors.OutputError
Section titled “novomodelo.errors.OutputError”class OutputError(NovomodeloError, OSError): ...An output serialization or schema failure, raised by novomodelo.write_policy_checkpoint, novomodelo.results.load_policy and a periodic checkpoint write during training. A not-found I/O error raises the builtin FileNotFoundError and other I/O errors raise CaseIoError. A manifest failure or a foreign entry raises ValidationError, not OutputError.
novomodelo.errors.SolverError
Section titled “novomodelo.errors.SolverError”class SolverError(NovomodeloError, RuntimeError): stage: int | None iteration: int | None scenario: int | NoneA training or solver failure. An error of the solver or internal class raises SolverError, and so does a message whose prefix no other class claims; a simulation failure raises SimulationError. The three attributes are always present. For an infeasible subproblem they hold the integer coordinates of the infeasibility; for every other failure they are None.
| Field | Type | Description |
|---|---|---|
stage | int | None | The stage of the infeasible subproblem. |
iteration | int | None | The training iteration of the infeasible subproblem. |
scenario | int | None | The scenario of the infeasible subproblem. |
novomodelo.errors.SimulationError
Section titled “novomodelo.errors.SimulationError”class SimulationError(NovomodeloError, RuntimeError): ...A simulation-phase failure. Raised for the message prefix simulation error.
novomodelo.errors.InternalError
Section titled “novomodelo.errors.InternalError”class InternalError(NovomodeloError, RuntimeError): ...An internal software or environment fault, such as a worker-thread panic. Raised for the message prefix internal error.
Some failures raise a builtin exception that is not a NovomodeloError: a missing case directory (OSError), threads=0 and a malformed config_overrides mapping (ValueError), and a callback’s own exception.
import novomodelofrom novomodelo.errors import ValidationError
try: novomodelo.Study("case", config_overrides={"trainning.enabled": False})except ValidationError as err: print(isinstance(err, ValueError)) print(err)Trueconfig override error: schema error in <config_overrides>, field trainning: unknown field `trainning`, expected one of `$schema`, `modeling`, `training`, `upper_bound_evaluation`, `policy`, `simulation`, `exports`, `estimation`novomodelo.schema
Section titled “novomodelo.schema”novomodelo.schema.export
Section titled “novomodelo.schema.export”def export(output_dir: Union[str, Path] = ".") -> int: ...Writes the JSON Schema file of every case-directory input type into output_dir, creating the directory if needed and overwriting existing files. For the schemas, see JSON Schemas.
Parameters
output_dir: the directory that receives the files. Defaults to the current directory.
Returns: the number of files written, as an int.
Raises
ValueError(builtin) when schema generation or serialisation fails.OSError(builtin) when the directory cannot be created or a file cannot be written.
import novomodelo.schema
print(novomodelo.schema.export("schemas"))18novomodelo._types
Section titled “novomodelo._types”The names in novomodelo._types are typing-only TypedDict classes for the dicts that novomodelo.run.run and the novomodelo.Study properties return; a type checker reads them from the package stubs. import novomodelo._types raises ModuleNotFoundError at runtime, and novomodelo.StochasticSummary, novomodelo.HydroModelsSummary, novomodelo.ProvenanceReport and novomodelo.System are likewise stub-only re-exports that are absent at runtime. Import the names under typing.TYPE_CHECKING and reach the runtime system class as novomodelo.model.System.
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING: from novomodelo._types import RunResult
def final_lower_bound(result: RunResult) -> float: return result["lower_bound"]StochasticSummary, HydroModelsSummary and ProvenanceReport are declared total=False; the dicts the package returns carry every key listed.
novomodelo._types.RunResult
Section titled “novomodelo._types.RunResult”The dict novomodelo.run.run returns.
| Key | Type | Description |
|---|---|---|
converged | bool | True when the stopping rules ended training and a gap or bound_stalling rule triggered at that iteration. False for any other stopping reason and for a run without training. |
iterations | int | The number of training iterations run. 0 when training did not run. |
lower_bound | float | The final lower bound, in currency units. |
upper_bound | Optional[float] | The final upper bound, in currency units. None when it is not available. |
gap_percent | Optional[float] | The relative optimality gap times 100. None when training did not run. |
total_time_ms | int | The training time in milliseconds. 0 when training did not run. |
output_dir | str | The output directory the run wrote to. |
simulation | Optional[SimulationSummary] | The simulation summary. None when simulation did not run. |
stochastic | Optional[StochasticSummary] | The same summary as novomodelo.Study.stochastic. novomodelo.run.run always sets it. |
hydro_models | Optional[HydroModelsSummary] | The same summary as novomodelo.Study.hydro_models. novomodelo.run.run always sets it. |
provenance | Optional[ProvenanceReport] | The same summary as novomodelo.Study.provenance. novomodelo.run.run always sets it. |
Every key is present on every branch. After training, upper_bound and gap_percent are set. On a simulation-only run (training disabled, simulation enabled), iterations is 0, gap_percent is None, and upper_bound is the loaded checkpoint’s recorded upper bound (the last training iteration’s value) when it is finite, otherwise None. With both phases disabled, lower_bound is 0.0 and upper_bound, gap_percent and simulation are None.
novomodelo._types.TrainingSummary
Section titled “novomodelo._types.TrainingSummary”The six training headline keys that RunResult carries at its top level. No function returns a TrainingSummary; use it to annotate those keys in isolation.
| Key | Type | Description |
|---|---|---|
converged | bool | True when the stopping rules ended training and a gap or bound_stalling rule triggered at that iteration. False for any other stopping reason and for a run without training. |
iterations | int | The number of training iterations run. 0 when training did not run. |
lower_bound | float | The final lower bound, in currency units. |
upper_bound | Optional[float] | The final upper bound, in currency units. None when it is not available. |
gap_percent | Optional[float] | The relative optimality gap times 100. None when training did not run. |
total_time_ms | int | The training time in milliseconds. 0 when training did not run. |
novomodelo._types.SimulationSummary
Section titled “novomodelo._types.SimulationSummary”The dict under RunResult["simulation"], and the return value of novomodelo.Study.simulate.
| Key | Type | Description |
|---|---|---|
n_scenarios | int | The number of simulation scenarios. |
completed | int | The number of scenarios that completed. |
novomodelo._types.StochasticSummary
Section titled “novomodelo._types.StochasticSummary”The dict novomodelo.Study.stochastic returns.
| Key | Type | Description |
|---|---|---|
inflow_source | Optional[str] | The source of the inflow seasonal statistics: one of "estimated" (computed from the historical records), "loaded" (read from the case files) or None (the component is not modelled). |
n_hydros | int | The number of hydro plants. |
n_seasons | int | The number of seasons in the PAR model. |
ar_order | Optional[dict] | The autoregressive (AR) order summary; None when the system has no hydros. The dict has the keys method (str, the order-selection method: "PACF" or "PACF_ANNUAL" for the estimation.order_selection that ran, or "fixed" when the orders come from the case files), min_order and max_order (int), n_hydros (int) and order_counts (list[int], the number of hydros at each order, indexed by order). |
correlation_source | Optional[str] | The source of the correlation data: one of "estimated" (computed from the historical records), "loaded" (read from the case files) or None (the component is not modelled). |
correlation_dim | Optional[str] | The dimension of the correlation matrix as a string, for example "1x1". None when there is no correlation matrix. |
opening_tree_source | Optional[str] | The source of the opening tree: one of "estimated" (computed from the historical records), "loaded" (read from the case files) or None (the component is not modelled). |
openings_per_stage | list[int] | The number of openings at each stage. |
n_stages | int | The number of stages in the stochastic context. |
n_load_buses | int | The number of buses with stochastic load noise. |
seed | int | The random seed used for noise generation. |
novomodelo._types.HydroModelsSummary
Section titled “novomodelo._types.HydroModelsSummary”The dict novomodelo.Study.hydro_models returns.
| Key | Type | Description |
|---|---|---|
n_constant | int | The number of hydros that use a constant productivity. |
n_fpha | int | The number of hydros that use the FPHA production model. |
total_planes | int | The total number of hyperplanes across all FPHA hydros. |
n_evaporation | int | The number of hydros with linearized evaporation. |
n_no_evaporation | int | The number of hydros with no evaporation model. |
novomodelo._types.ProvenanceReport
Section titled “novomodelo._types.ProvenanceReport”The dict novomodelo.Study.provenance returns.
| Key | Type | Description |
|---|---|---|
estimation_path | str | The estimation path taken, one of "deterministic", "user_stats_white_noise", "user_provided_no_history", "full_estimation", "user_ar_history_stats", "partial_estimation" or "user_provided_all". |
seasonal_stats_source | str | The origin of the seasonal mean and standard deviation: one of "estimated" (computed from the inflow history), "user_file" (read from a case file) or "n/a" (not applicable). |
ar_coefficients_source | str | The origin of the AR coefficients: one of "estimated" (computed from the inflow history), "user_file" (read from a case file) or "n/a" (not applicable). |
correlation_source | str | The origin of the spatial correlation: one of "estimated" (computed from the inflow history), "user_file" (read from a case file) or "n/a" (not applicable). |
opening_tree_source | str | The origin of the noise opening tree: one of "estimated" (computed from the inflow history), "user_file" (read from a case file) or "n/a" (not applicable). |
n_hydros | int | The number of hydro plants. |
ar_method | Optional[str] | The order-selection method used when the AR coefficients were estimated: "PACF" or "PACF_ANNUAL". None when they were not estimated. |
ar_max_order | Optional[int] | The maximum AR order across all hydros when the AR coefficients were estimated. None otherwise. |
white_noise_fallbacks | list[int] | The ids of the hydros that fell back to white noise. Empty unless the path is "partial_estimation". |
hydro_production | dict | The hydro-production source counts. The dict has the integer keys n_fpha_computed_from_geometry, n_fpha_precomputed_hyperplanes, n_evaporation_ref_user_supplied and n_evaporation_ref_default_midpoint. |