GreenFlux — Models, Equipment & Controls Overview¶
A single reference for what the simulator currently models: the physics, the equipment
library, and the control layer. It reflects the state after the equipment-realism milestone
(closed-loop forward heating + ventilation + CO2, runnable from project-run --mode forward).
The package is layered strictly (each layer only depends on those below it):
1. Physics models¶
Climate state (the greenhouse "unit")¶
systems/greenhouse_unit.py::GreenhouseUnit is the assembly point. Its immutable
GreenhouseUnitState carries up to nine climate + node states:
| State | Meaning |
|---|---|
air_temperature_k |
greenhouse air temperature |
vapour_pressure_pa |
air humidity (vapour pressure) |
co2_mg_m3 |
air CO2 concentration |
local_co2_mg_m3 |
near-canopy CO2 (local-mixing sensor blend) |
cover_temperature_k |
cover/glazing surface (condensation) |
screen_temperature_k |
thermal-screen surface (condensation) |
floor_temperature_k |
floor/soil surface |
thermal_mass_temperature_k |
lumped thermal-mass node |
moisture_buffer_vapour_pressure_pa |
moisture-buffer node |
Every step returns a rich GreenhouseUnitFluxes record — each physical term
(heating, ventilation, envelope loss, condensation, transpiration, CO2 exchange, …) is a
named field, enabling per-term balance decomposition and calibration.
Crop¶
crop/ models a staged tomato crop (photosynthesis, respiration, partitioning, fruit
development). In the climate loop the canopy is treated as a coupled node (transpiration,
CO2 exchange, sensible/latent split). Crop pools (buffer/leaf/stem/fruit/harvested) are
explicit states.
Scalar physics (equations/, flows/)¶
equations/: psychrometrics (saturated vapour pressure, RH, dew point; pluggable psychrolib/CoolProp backends), fluid flow, fluid heat transfer.flows/: coupled transfer wrappers composing the scalars —heat_and_vapour,mass_transfer(condensation/evaporation),canopy_transpiration,ventilation(Boulard-Baille natural-vent law + forced-fan rate),heat_transfer.
Integration paths (all share the same physics)¶
| Path | Entry point | Use |
|---|---|---|
| Explicit fixed-step (Euler) | simulators/core.py::simulate_fixed_step |
deterministic, dataset generation |
Adaptive IVP (SciPy solve_ivp) |
systems/greenhouse_unit_ode.py::simulate_greenhouse_unit_ivp |
recommended default; stiff/accurate |
| Forward (closed-loop) | simulators/forward.py::simulate_forward |
actuator-driven design studies |
The fixed-step and IVP paths reproduce the same physics (parity-verified). The forward path wraps either integrator with a control loop (below).
2. Equipment library (equipment/)¶
Each device is a frozen dataclass with a capacity-aware output(...)/step(...)/dose(...)
method returning a result record. Present-vs-absent realism constraints are noted.
| Device | Class(es) | Models | Capacity / limits |
|---|---|---|---|
| Boiler | Boiler, PipeCircuit, PipeHeatingNetwork, HeatingPipeSegment |
pipe heat delivery; boiler fuel→heat | rated nominal_heat_power_w, efficiency, reports unmet load; ε-NTU pipe effectiveness; free-convection pipe correlation |
| Ventilation | RoofVent, FanVentilator, InfiltrationLeakage |
natural roof buoyancy+wind exchange; forced-fan flow; envelope leakage | fan max_flow_m3_s + specific_power_w_per_m3_s (electric); discharge/wind/resistance coefficients |
| CO2 supply | CO2Tank, FlueGasCO2Source, CO2LocalMixing, CO2DosingMemory |
pure-gas dosing; flue-gas capture; near-canopy mixing; pulse memory | tank capacity_mg_m2_s+purity; flue max_fuel_power_w_m2, co2_yield, capture eff., contaminant split |
| Humidity | Dehumidifier, FoggingSystem, CondensationSurface |
condensing dehumidifier; fogging humidifier; cover/screen/floor condensation | removal capped at capacity_kg_m2_s and available excess; electric/pump power; first-order surface lag |
| Envelope | EnvelopeBoundary, EnvelopeAssembly |
per-boundary U·A·exposure conductance | validated U/exposure; screen-insulation credit |
| Lighting | LampFixture (HPS/LED presets) |
PAR/NIR/FIR/convective spectral split | dimming clamp; installed_power_w_m2 |
| Screens | MovableScreen, ScreenMaterial |
shortwave transmission, heat-loss reduction, light pollution | closure clamp; material presets (thermal/blackout/shade) |
| Irrigation | DripperLine, FertigationSolution, IrrigationEvent |
emitter flow, EC/pH/nutrients, drain fraction | emitter max_flow_kg_m2_s, uniformity |
| Heat pump | consoclim_heat_pump, ConsoClimHeatPumpParameters |
temperature-dependent COP (EIRFT/CAPFT), part-load, cycling | full-load cap, COP map — available but not yet wired into the greenhouse loop |
| Sizing | FloorAreaSizingBasis, capacity_per_floor_area |
rated-capacity ↔ per-area conversions | — |
Runtime wiring status. The measured-replay path drives climate from measured actuator
logs (pipe-temperature heating proxy, measured roof %, CO2 injection signal) and the passive
elements (envelope, condensation, local mixing). The forward path (below) wires the
capacity-aware Boiler, RoofVent+FanVentilator, and CO2Tank/FlueGasCO2Source through
closed-loop actuators. The heat pump and the lamp spectral split remain available but are not
yet consumed by either run path.
3. Control layer (control/) — the forward mode¶
New with the equipment-realism milestone. Actuators follow one pattern: setpoint → PI controller → capacity-limited equipment → flux + consumption + saturation.
Generic PI controller¶
PIControllerParameters / PIControllerState / pi_control_step — unit-neutral
(proportional_gain, integral_gain, output_max, output_min), with
conditional-integration anti-windup. Output units are actuator-specific.
Actuators¶
| Actuator | Wraps | Tracks | Notes |
|---|---|---|---|
HeatingActuator |
Boiler (per m²) |
temperature setpoint | modulating PI, capacity + efficiency + gas; optional ramp limit; under-capacity → saturated |
VentilationActuator |
RoofVent + FanVentilator |
max-temperature and max-humidity | two gated PI demands (cool only if outside cooler; dehumidify only if outside drier); max of the two; split-range command [0,1] roof → [1,2] fan; fan electricity |
CO2Actuator |
CO2Tank/FlueGasCO2Source |
CO2 ppm setpoint | dose reduced by roof opening (no wasting gas into open vents); CO2/fuel accounting |
Setpoint schedules¶
Per domain: Constant* and DayNight* providers for heating temperature, ventilation
temperature, humidity (constant only), and CO2 ppm. "Enrich by day only" falls out of a
day/night CO2 setpoint whose night value is ambient.
Composition (simulate_forward)¶
Each control interval evaluates the actuators in order heating → ventilation → CO2 (CO2
reads the vent's roof opening for its dose reduction), then advances the same GreenhouseUnit
physics with the resulting fluxes held constant (zero-order hold) via the fixed or IVP
integrator. Separate heating/ventilation setpoints create a natural deadband. The PI
integral state lives in the driver, never in the frozen physics state, so the mode composes
with both integrators without touching parity.
Consumption ledger (ForwardConsumptionLedger): heat_delivered_kwh_m2,
fuel_gas_kwh_m2, fan_electricity_kwh_m2, co2_dosed_kg_m2, co2_source_fuel_kwh_m2, plus
utilization/at-capacity hours per actuator.
Note: ventilation cooling is stiff (a fully open roof at large ΔT removes hundreds of W/m²); forward runs with active ventilation should use
solver="ivp"(the default) — a 300 s explicit-Euler step overshoots.
4. Run modes¶
Declarative forward control¶
A project may declare a project-level forward_control block (projects/schema.py):
heating / ventilation / co2 actuator definitions (PI gains, capacities, dose params) +
heating_setpoint / ventilation_setpoint / humidity_setpoint / co2_setpoint schedules
(constant or day_night). It resolves to the actuators + setpoints simulate_forward
consumes. Heating is mandatory; ventilation and CO2 are optional; the CO2 source comes from
equipment.co2. The block is optional — projects without it are unaffected.
CLI¶
greenflux project-run <project.json> --mode replay # measured forcing (default), validated
greenflux project-run <project.json> --mode forward # closed-loop actuators from forward_control
- replay: reproduces measured actuator behaviour; the calibrated, validated path; byte-stable (dataset generation depends on it).
- forward: runs the closed-loop actuators over weather-only forcing and writes the same
outputs (timeseries + recovered fluxes,
summary.jsonwith the consumption ledger, ledger-derivedresource_statistics). Both GreenLight projects ship aforward_controlblock, so--mode forwardworks out of the box.
Solver flags apply to both (--solver ivp|fixed, IVP tolerances). See
docs/project_run_forward.md.
5. Calibration & validation¶
- Validated against the GreenLight HPS/LED Bleiswijk measured-replay dataset.
- Calibration profiles per project (
measured_replay,measured_replay_physical,measured_replay_ivp).measured_replay_physicalis the recommended default for simulation studies: physical parameters (near-unity fudge multipliers), better balance closure, comparable climate accuracy.measured_replayis retained byte-stable. - A shared cover-condensation vapour-sink helper makes
project-runreproduce the replay's calibrated humidity/latent behaviour (previously the builder path silently dropped it). analysis/resource_statistics.pyaggregates macro consumptions (heat/electricity/CO2/water) and assesses them against plausibility bounds.
See docs/usable_physical_models.md, the calibration-forcing reconciliation notes (internal).
6. Realism status & known limitations (honest)¶
Realistic today
- Closed-loop heating / ventilation / CO2 with capacity limits, efficiency/fuel accounting,
actuator dynamics (anti-windup, ramp, split-range staging, dose-vs-vent coupling), and a
consumption ledger — driven by physical equipment, not replayed signals.
- Physical calibration (measured_replay_physical) with balances that close.
- Validated forward behaviour: on a warm day the controller holds temperature in the
heat/vent deadband ~92 % of the time and daytime CO2 within ±100 ppm of target ~95 %.
Known limitations / follow-ups
- Heat pump (COP-vs-temperature) is modelled but not wired into either run path.
- Lighting uses a single heat-fraction scalar in the runtime; the LampFixture spectral
split (PAR/NIR/FIR) is not consumed there.
- CO2 as a diagnostic in replay: the measured injection signal is uninformative (binary,
off ~97 % of the time) — forward setpoint dosing is meaningful, replay CO2 is not.
- Deferred CO2 signal-shaping reconciliation: the replay's stateful CO2 pulse shaping is
not yet reproduced by project-run (needs a stateful builder input schedule).
- Irrigation / water balance is an open-loop dosing calculator, not coupled to a root-zone
state; the forward resource ledger reports no water.
- Fuel/gas totals appear in the forward summary.json ledger but not in the flat
ResourceMacroStatistics (which has no fuel field).
- On/off-hysteresis control and a declarative case-level forward trigger are not implemented
(the --mode forward CLI flag is the trigger).
Pointers¶
- Actuator internals:
docs/forward_heating_mode.md,docs/forward_ventilation_mode.md,docs/forward_co2_mode.md. - Running forward:
docs/project_run_forward.md. - Calibration:
docs/usable_physical_models.md, the calibration-forcing reconciliation notes (internal).