A greenhouse simulator whose physics you can trace, calibrated against measured data.
GreenFlux models greenhouse climate — air temperature, humidity, CO2 — together with tomato crop
growth, the equipment that drives them, and the economics of running the whole thing. Every state
is driven by a named heat, vapour, CO2 or radiation balance rather than by a fitted black box, so a
result can be traced back to the term that produced it. The import package and command-line
executable are both greenflux.
It exists for two connected goals:
- Build reusable greenhouse models that stay traceable to physical balances, equipment parameters and measured-data validation.
- Generate structured datasets for machine-learning research: unsupervised anomaly and fault detection, stochastic and optimal control, sensor observability and placement, synthetic benchmarks.
The implementation is inspired by greenhouse Modelica literature and reference models, including
queraltab/Greenhouses-Library, but the public package does not require cloned vendor
repositories.
📖 Full documentation → lirei.ca/greenflux
Both figures are tracked in this repository
(outputs/calibration/validation_figures/hps_physical/) and show the
measured_replay_physical profile, which projects select by default. Regenerating them needs the
measured GreenLight dataset, which the public repository does not ship — see
docs/data_access.md — and then:
uv run --extra plots python examples/plot_validation.py --installation HPSAccuracy across the whole season, out of sample. Each bar is one replay window; orange is the single window the calibration was fitted on, the dashed line is the mean over the windows that were not. Reported this way because a single in-sample window is not evidence — earlier one-day figures of ~2 °C degraded to 15–22 °C when replayed elsewhere in the season.
What a replay actually looks like, including where it is wrong: CO2 and vapour pressure track the measurement closely, air temperature runs roughly 2 °C cold and clips the daytime peaks, and relative humidity sits biased high enough to saturate. Those two biases are open, documented and under investigation — they are not smoothed out of the figure.
- Physics first: greenhouse states are driven by heat, vapour, CO2, crop, ventilation, radiation and equipment balances rather than black-box predictors.
- Project based: each greenhouse project carries site, geometry, equipment, crop, weather references, calibration profiles and named simulation cases.
- Experiment ready: the upper workflow is organized as
Project -> Problem -> Experiment -> Benchmarkso studies can be repeated, compared and exported. - ML ready outputs: simulations produce aligned time series, state variables, actuator traces, crop states, fluxes and validation diagnostics.
- Validation aware: measured replay, dynamic error layers, operating-regime diagnostics and crop calibration are part of the public package contract.
- reusable equations for psychrometrics, heat, vapour, CO2 and ventilation
- equipment models for lighting, screens, heating, humidity control, CO2, irrigation and envelope losses
- shared actuation commands and management policies for controllable greenhouse operation
- tomato crop and biomass models with calibration utilities
- project definitions for site, geometry, equipment, crop and simulation cases
- problem, experiment and benchmark wrappers for reproducible greenhouse studies
- calibrated GreenLight HPS/LED Bleiswijk tomato projects
- EPW/TMY weather adapters through optional
pvlib - layered validation metrics, including increment error and drift diagnostics
- hidden-residual attribution for heat, vapour and CO2 balance gaps
- optional SciPy, Optuna, pandas/xarray, plotting and performance extras
GreenFlux is a Python package managed with uv.
Required tools:
- Python 3.10 or newer. Python 3.11+ is recommended for the standard-library TOML parser and faster local development.
uvfor dependency resolution, virtual environments and running commands.gitfor cloning the repository.
Install uv if it is not already available:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv --versionClone and prepare the project:
git clone https://github.com/lirei-lab/greenflux.git
cd greenflux
uv syncRun the test suite and build the package:
uv run --extra dev pytest tests/
uv buildOptional features are installed through extras when needed:
uv run --extra plots python examples/tomato_plots.py
uv run --extra data --extra plots greenflux run examples/scenarios/tomato.toml --staged --plots --xarray
uv run --extra simulation python -m unittest tests.test_scipy_simulator
uv run --extra optimization python examples/calibrate_greenlight_replay.py --installation HPS --limit 289
uv run --extra solar python -m unittest tests.test_weatherReal measured-data calibration requires external datasets under data/raw/.
The public repository does not include raw datasets, generated outputs or
vendored reference repositories; see
docs/data_access.md for download instructions, licenses
and the expected local dataset layout.
To download and prepare the supported calibration datasets automatically:
uv run python scripts/download_calibration_data.py --dataset alluv run --extra plots greenflux project-run projects/greenlight_hps_bleiswijk/project.json --run-id baseline --plotsProject runs write outputs under the project itself, for example
projects/greenlight_hps_bleiswijk/results/measured_open_loop/baseline/. This
keeps reusable examples separate from project-specific data products. With
--plots, the same folder also receives climate.png, actuation.png and
crop.png.
Project runs default to deterministic fixed-step integration. To use the adaptive SciPy full-state IVP path:
uv run greenflux project-run projects/greenlight_hps_bleiswijk/project.json --solver ivp
uv run python examples/compare_project_solvers.py projects/greenlight_hps_bleiswijk/project.jsonEach project run also writes resources/resource_statistics.* with simulated
heat, electricity, CO2, irrigation and drainage totals. When an AGC forcing case
has a matching Resources.csv under data/raw/agc_2019/<team>/, GreenFlux also
writes resources/resource_comparison.* for measured-vs-simulated resource
bias checks.
For project cases backed by GreenLight or AGC raw data, project-run uses
measured actuator traces when available: roof vents, screens, lighting, CO2
dosing and a grow-pipe heating proxy. Pass --constant-controls to ignore those
traces and use the CLI control arguments instead.
A study is a declarative analysis over a project — studies/<id>/study.json names a project,
picks an analysis type (stochastic planning or anomaly detection) and supplies its parameters.
uv run --extra simulation --extra planning greenflux study run studies/plan_led_demo/study.json
uv run --extra simulation greenflux study run studies/anomaly_led_demo/study.jsonResults land in studies/<id>/results/<run_id>/result.json. See
docs/studies.md for the schema, both study types and how studies relate to
the Project -> Problem -> Experiment -> Benchmark framework.
Use projects, scenarios or experiments to generate clean baseline traces, perturbed actuator traces, synthetic weather stress tests and fault-injected variants. The resulting time series can be exported as CSV or xarray/NetCDF for downstream pipelines.
uv run --extra plots --extra data greenflux run examples/scenarios/tomato.toml --staged --plots --xarray
uv run python examples/validate_greenlight_long_crop_coupled.py --installation HPS --days 7uv run python examples/calibrate_greenlight_replay.py --installation HPS --limit 289 --scipy-max-evaluations 300
uv run python examples/validate_greenlight_period_sufficiency.py --installation HPS --project projects/greenlight_hps_bleiswijk/project.json --period-days 7 --stride-days 7
uv run python examples/replay_greenlight_with_humidity_schedule.py --installation HPS --parameters outputs/calibration/greenlight_hps_replay/parameters.json --schedule outputs/calibration/greenlight_hps_replay/validation/humidity_control_inference.csv --limit 289
uv run python examples/calibrate_agc_crop.py --team Reference --days 100
uv run --extra plots python examples/plot_validation.py --installation HPSplot_validation.py renders the validation figure set — simulated-vs-measured overlays, parity
scatter, error trajectories, per-period RMSE across the dataset and regime-split error. The two
shown at the top of this README come from that set.
Validation reports include conventional level errors and dynamic diagnostics:
increment error, increment-rate error, drift, horizon-window metrics and
physical residual attribution. GreenLight validation folders now include both
timeseries.csv, residuals.csv and humidity_control_inference.csv, so
calibration gaps can be inspected as equivalent hidden heat, vapour and CO2
source terms, plus diagnostic dehumidifier/fogging control schedules inferred
from vapour residuals.
Period-sufficiency validation screens non-overlapping replay windows for
minimum duration, sample count and operating-regime coverage before accepting a
candidate calibration.
The default GreenLight environment calibration also uses a residual-aware
objective, which penalizes large hidden heat, vapour and CO2 corrections in
addition to state RMSE.
The residual-aware objective is the default for new calibration runs; exported project JSON files
should be regenerated after accepting a new calibrated artifact.
The greenhouse unit also exposes optional dynamic cover-condensation states and
diagnostics (cover_condensation_kg_m2_s, cover_temperature_k and
cover_surface_vapour_pressure_pa). These parameters are serialized in
projects and calibration artifacts, but current GreenLight period validation
does not yet support making dynamic cover condensation the default baseline.
The greenflux.problems, greenflux.experiments and greenflux.benchmarks
layers provide deterministic simulation environments that can later be adapted
to stochastic control, optimal control, MPC, reinforcement learning,
observability analysis and sensor-placement experiments.
For declarative, CLI-runnable analyses over a project, the studies/ layer ships
three study types today — stochastic_planning (a two-stage convex program),
stochastic_mpc (receding-horizon closed loop), and
anomaly_detection (residual-diagnostic fault detection). See
docs/studies.md.
See docs/experiment_framework.md for the dataset-generation
philosophy and the Project -> Problem -> Experiment -> Benchmark workflow.
uv run python examples/run_examples.py
uv run --extra dev pytest tests/Examples:
uv run --extra plots python examples/tomato_plots.py
uv run --extra simulation python -m unittest tests.test_scipy_simulator
uv run python examples/tomato_sensitivity.py
uv run python examples/greenhouse_1_full.py
uv run greenflux run examples/scenarios/tomato.toml --output-dir outputs/scenario
uv run --extra plots --extra data greenflux run examples/scenarios/tomato.toml --staged --plots --xarray
uv run python examples/export_greenlight_calibrated_projects.py
uv run --extra plots greenflux project-run projects/greenlight_hps_bleiswijk/project.json --run-id baseline --plotsThis writes tomato example plots and CSV/NetCDF time series into outputs/.
examples/greenhouse_1_full.py writes a traceable Python approximation of
Greenhouses.Examples.Greenhouse_1, including headline Modelica variables,
component inventory and a full interval time series.
simulation: SciPy-backed adaptive ODE integration and bounded calibration.optimization: Optuna-backed bounded parameter search.planning: cvxpy convex solver for thestochastic_planningandstochastic_mpcstudy types.data: xarray/pandas result export.config: Pydantic settings for strict external configuration.scenarios: YAML scenario loading.solar: EPW/TMY weather loading viapvlib.edm: optionalenergydatamodelinheritance for energy asset objects.
Scenario files can be loaded as typed configs:
from greenflux import load_scenario_config
scenario = load_scenario_config("examples/scenarios/tomato.toml")Or executed from the command line:
greenflux run examples/scenarios/tomato.toml --output-dir outputs/scenarioThe example implementations use packaged reference tables under
greenflux.resources.modelica_examples, so no vendor/ checkout is needed to
run the simulator.
GreenFlux is validated through reproducible Python simulations and measured greenhouse datasets:
- GreenLight HPS/LED Bleiswijk measured comparisons.
- AGC 2019 tomato crop/yield calibration against measured production data.
- Packaged reference-table greenhouse examples.
- Unit and integration tests across equations, equipment, projects, weather, crop, calibration and simulation runners.
Accuracy is reported out of sample. The exported GreenLight projects use a staged Optuna
calibration plus floor/soil thermal-mass and moisture-buffer nodes, replayed across windows that
span the full ~112-day dataset. On measured_replay_physical — the profile projects select by
default — the tracked figure set gives:
| installation | air temperature | relative humidity | vapour pressure | CO2 |
|---|---|---|---|---|
| HPS | 2.19 °C | 7.85 % | 289 Pa | 54 ppm |
| LED | 2.00 °C | 7.34 % | 256 Pa | see below |
Both temperatures sit under the project's 2.5 °C acceptance target. The numbers are printed on the
figures themselves (outputs/calibration/validation_figures/{hps,led}_physical/period_rmse.png),
so the table and the plots cannot drift apart.
Three limits that belong next to those numbers:
- CO2 is a diagnostic, not a prediction. The dataset's injection signal is a binary status flag, not a flux, and it reads off 97 % of the time while CO2 is dosed to ~780 ppm. HPS lands at 54 ppm because its dosing gain is identifiable; LED's is not, and its CO2 RMSE is an order of magnitude worse. Do not use either as a CO2 accuracy claim.
- Two open biases, visible in the overlay figure above: air temperature runs ~2 °C cold and clips daytime peaks, and relative humidity is biased high. Both are traced to the lumped heat balance rather than to calibration.
- Short-window replay and data generation are what this is for. Long-horizon open-loop prediction is still improving.
Earlier single-window figures of ~2 °C were in-sample on one day and degraded to 15–22 °C / 35–48 pp when replayed elsewhere in the season; the thermal-mass node fixed a night-time temperature divergence and the moisture-buffer node fixed a day-wet/night-dry humidity swing. That history is why accuracy here is always quoted across windows.
The latest full local run completed 1060 unit/integration tests (1 skipped, 76 subtests) and
uv build successfully.
The implementation is intentionally modular: equations, flows, equipment, actuation, management, systems, projects, problems, experiments and validation tools live in separate files. This makes it easier to add new crops, equipment, control policies, fault injectors and dataset exporters without turning the simulator into a single monolithic script.
The published site is lirei.ca/greenflux, organised by what a
reader is trying to do. The same pages live under docs/ in this repository.
| I want to… | Page |
|---|---|
| define a greenhouse | project_management.md · weather_management.md · models_equipment_controls.md |
| run a simulation | project_run_forward.md · forward_heating_mode.md · forward_ventilation_mode.md · forward_co2_mode.md |
| choose a solver | numerical_solvers.md |
| ask a research question | studies.md · experiment_framework.md |
| understand the model | usable_physical_models.md · actuation_management.md · project_structure.md |
| get the datasets | data_access.md |
| contribute | CONTRIBUTING.md |
Working notes, audits and reviews live in docs/internal/, which is deliberately unpublished and
untracked: it is a log of how the work was done, not the work. The same rule keeps unverified
studies and generated outputs out of the public tree — a study in this repository reads as a
result, and a result should have been checked first.

