Scenario files¶
A scenario is a YAML file (or a Python dictionary with the same content) that describes a plant and its timeline. This guide goes through its parts; the scenario schema lists every key.
seed: 1
duration: 1h
dt: 1s
library: process@1
units:
- {id: FIC-101, template: flow_loop, sp: 20, span: [0, 40]}
- {id: TIC-102, template: heat_exchanger, sp: 70}
exogenous:
- {target: TIC-102.flow, from: FIC-101.cv} # the exchanger's flow is the loop's true flow
- target: TIC-102.inlet_temp
unit: degC
source: {kind: ou, mean: 25, std: 1.5, tau: 30min}
measured: {tag: TT-100, sensor: thermocouple, span: [0, 100]}
output:
every: 10s
Time¶
duration is the length of the run and dt the base step; duration must be a whole multiple of dt. Durations are seconds (90) or strings: 250ms, 30s, 10min, 1.5h, 2d, 3d2h. Parameters are always given in physical units (a time constant in seconds, not in steps), and every operator discretizes its equations exactly, so changing dt never requires retuning.
Choose dt for the fastest dynamics in the plant. Flow loops, with time constants of a few seconds, need dt: 1s; a plant of temperature loops runs well at dt: 20s. Slower parts of a plant can update less often: see Multi-rate plants.
Units and templates¶
library loads a domain library; process@1 pins its major version, and a mismatch is an error. Each entry of units is an instance of one of the library's templates, with an id and any of the template's parameters:
units:
- {id: TIC-101, template: temperature_loop, sp: 80, Kc: 3, Ti: 10min, sensor: thermocouple, span: [0, 150]}
The id sets the ISA tags: a loop TIC-101 has the transmitter reading TT-101, the valve TV-101, and the controller signals TIC-101.sp, TIC-101.mode, TIC-101.man_out and TIC-101.out. Parameters of the operators inside a unit are paths such as TIC-101.plant.K. The process library lists every template with its parameters and ports.
Loops take tuning: simc to compute the controller from the plant model instead of Kc and Ti, with detune above 1 for a slower, more robust controller.
Ports and wiring¶
A template's inputs from outside are its ports, for example a temperature loop's feed_temp. An exogenous entry drives a port in one of two ways:
source: a new signal from a source.ouis an Ornstein–Uhlenbeck process given by itsmean, stationary standard deviationstdand correlation timetau;aris an autoregressive process;constantis a constant.from: an existing signal, which connects units. In the example above, the heat exchanger's flow is the flow loop's true flowFIC-101.cv.
A port that nothing drives is a schedule at its default value, so regimes and interventions can change it, for example a production rate.
An exogenous entry with a source whose target is not a port creates a new signal with that name, which other entries and custom operators can then use. from always feeds a port.
Measuring a signal¶
A template measures its own regulated variable. To measure anything else, add measured to an exogenous entry. It builds an instrument from a sensor preset: a lag, a bias, noise, quantization and, for analyzers and labs, sampling and a reporting delay. Noise and quantization are percentages of the span, so choose a span around the expected values.
exogenous:
- target: TIC-101.feed_temp
source: {kind: ou, mean: 25, std: 2, tau: 6h}
measured: {tag: TT-100, sensor: thermocouple, span: [0, 100]}
The historian records every measured tag.
Custom operators¶
custom adds core operators directly, with their parameters as keys. The operator reference lists them. This ratio station makes a second flow loop follow the first:
custom:
- {id: FFC-102.ratio, op: schedule, value: 0.25, unit: "1"}
- {id: FFC-102.out, op: product, inputs: {in0: FT-101, in1: FFC-102.ratio}, unit: m3/h}
exogenous:
- {target: FIC-102.rsp, from: FFC-102.out} # FIC-102 follows it in mode CAS
The operator's output signal has the operator's id. A schedule holds a value that interventions and regimes can change.
The initial state¶
init sets where the run starts:
steady(the default): the plant is stepped with noise off, sources at their means and ageing held, until every state settles; then stochastic sources start from their stationary distributions. There is no start-up transient.none: every operator starts from its initial state.{burn_in: 2h}: the plant runs with noise for that long before t = 0, and that part is discarded.
If a plant cannot settle, for example because a loop saturates at its initial setpoint, the error names the states that are still moving.
Output¶
output.every records every n-th step (a multiple of dt). output.start sets the calendar time of t = 0, and the run's index becomes timestamps:
Loading from Python¶
simulate() and prepare() accept a file path, YAML text, a dictionary or a validated Scenario:
import homeostat
run = homeostat.simulate("plant.yaml")
prepared = homeostat.prepare({"duration": "1h", "library": "process", "units": [{"id": "FIC-101", "template": "flow_loop"}]})
prepared.compiled.loops # the loops found in the compiled graph
run = prepared.run()
prepare() validates and compiles without running. homeostat.catalog() describes every operator, template, preset, fault, degradation and task as data, for tools and AI assistants.