Your first scenario¶
A scenario describes a plant and what happens to it. This one has a single flow control loop, a disturbance nobody measures, a setpoint change and a transmitter fault. Save it as first.yaml:
seed: 7
duration: 2h
dt: 1s
library: process@1
units:
- {id: FIC-101, template: flow_loop, sp: 50}
exogenous:
- target: FIC-101.pressure # upstream pressure: a disturbance nobody measures
unit: bar
source: {kind: ou, mean: 0, std: 0.2, tau: 5min}
interventions:
- {at: 30min, target: FIC-101.sp, value: 60}
- {at: 1h, target: FIC-101, fault: sensor_bias, magnitude: 2}
output:
every: 10s
What each part does:
seed,durationanddt: the random seed, the length of the run and the base step. Durations are seconds or strings such as30s,10min,2hor1d.library: process@1: the process-industry library, pinned to its major version 1. It provides the templates, sensor presets and faults.units: oneflow_loop, a flow controller with its valve, the flow's response to the valve, and a flowmeter. The idFIC-101gives the ISA tags: the flowmeter reading isFT-101and the valve isFV-101.exogenous: the loop'spressureport is driven by an Ornstein–Uhlenbeck process with a standard deviation of 0.2 bar and a correlation time of 5 minutes.interventions: at 30 minutes the setpoint steps to 60 m³/h; at 1 hour the flowmeter starts reading 2 m³/h too high.output: record every 10 seconds.
Run it¶
import pandas as pd
import homeostat
run = homeostat.simulate("first.yaml")
run.observed.columns # ['FT-101', 'FIC-101.sp', 'FIC-101.mode', 'FIC-101.out']
The run starts from a steady state at the initial setpoint, so there is no start-up transient. It takes well under a second.
run.observed is what a historian would record: the flowmeter reading and the controller's setpoint, mode and output. run.truth holds the true value of every signal, including the pressure disturbance and the true flow FIC-101.cv, which no instrument reports directly.
See the fault where it hides¶
A transmitter bias is a classic example of a fault that the control loop hides. The controller trusts its reading and holds it at the setpoint, so the reading looks perfect while the real flow drifts away:
before = (run.truth.index > pd.Timedelta("40min")) & (run.truth.index < pd.Timedelta("60min"))
after = run.truth.index > pd.Timedelta("90min")
run.measured["FT-101"][before].mean() # 60.1: on setpoint
run.truth["FIC-101.cv"][before].mean() # 60.0
run.measured["FT-101"][after].mean() # 60.0: still on setpoint
run.truth["FIC-101.cv"][after].mean() # 58.0: the real flow is 2 m³/h low
The fault is recorded in the event log, with the event it expands to:
| at | target | action | value | label | origin |
|---|---|---|---|---|---|
| 1800 | FIC-101.sp |
set | 60 | planned | |
| 3600 | FT-101.bias.offset |
shift | 2 | fault:sensor_bias | fault:sensor_bias@FIC-101 |
When something is wrong¶
Scenarios are checked before they run. Every problem comes with a code, the place in the file, the reason and a fix. For example, an intervention that targets the true flow instead of its setpoint:
[E_TARGET_REGULATED] interventions[0]: 'FIC-101.cv' is the regulated variable of loop FIC-101;
nobody can set it directly. Fix: Change its reference instead: target 'FIC-101.sp'.
All codes are listed in Error codes.
Next¶
- Reading a run: everything a run returns.
- Scenario files: templates, wiring, measurements and custom operators.
- Tutorial: a plant with a reactor, a mixer and three materials.