Experiment Framework¶
GreenFlux keeps the physical simulator organized by greenhouse domain, but the upper workflow follows a problem-centric structure inspired by energy modelling frameworks such as enflow/emflow:
This is intentionally lighter than adopting a full external framework. The goal is to make greenhouse studies reproducible without hiding the physical models. The same workflow is also the basis for high-quality dataset generation: a project defines the physical asset, a problem defines the question, an experiment records one reproducible run and a benchmark compares families of runs.
Project¶
greenflux.projects defines the physical object:
- site and weather references
- greenhouse geometry
- envelope and equipment
- crop parameters
- named simulation cases
- calibration profiles
A project answers: what greenhouse are we modelling?
For ML and control datasets, projects are also the correct place to store calibration profiles, weather references and project-local result folders. Keeping these definitions attached to the greenhouse avoids undocumented dataset-generation scripts with hidden assumptions.
Problem¶
greenflux.problems defines the question being asked of a project. A
GreenhouseProblem combines:
- one
GreenhouseProject - one simulation case
- an objective
- default actuation commands
- initial climate conditions
- resolved weather forcing
The environment exposes a deterministic sequential interface:
from greenflux.actuation import GreenhouseActuation
from greenflux.problems import GreenhouseProblem
problem = GreenhouseProblem(
problem_id="winter_climate",
project=project,
case_name="winter_day",
default_action=GreenhouseActuation(heating_power_w_m2=60.0),
)
result = problem.environment(weather=weather).run()
Actions are greenhouse actuation commands such as vent opening, screen closure, heating, lighting, CO2 injection and vapour source/sink terms. Observations are compact records with temperature, RH, vapour pressure, CO2 and harvested dry mass.
For downstream algorithms, a problem is the natural boundary for deciding what is observed, what is controlled and what remains hidden as simulator truth. This distinction is important for anomaly detection, fault diagnosis, observability analysis and sensor-placement studies.
The action contract itself lives in greenflux.actuation, so controllers,
management policies and problem environments use the same physical command
language without mixing actuator definitions with decision logic.
See docs/actuation_management.md for the detailed boundary between equipment,
actuation, controls, management and problem evaluation.
Management policies live in greenflux.management and produce those
actuation commands:
from greenflux.actuation import GreenhouseActuation
from greenflux.management import DayNightManagementPolicy
policy = DayNightManagementPolicy(
day_command=GreenhouseActuation(lighting_power_w_m2=80.0),
night_command=GreenhouseActuation(screen_closure=1.0, heating_power_w_m2=40.0),
)
result = problem.environment(weather=weather).run(
action_schedule=lambda time_s, state: policy.action(time_s=time_s, state=state)
)
Experiment¶
greenflux.experiments packages a problem run into a reproducible unit:
from greenflux.experiments import GreenhouseExperiment
experiment = GreenhouseExperiment(
experiment_id="winter_climate_baseline",
problem=problem,
tags=("baseline", "synthetic-weather"),
)
result = experiment.run(weather=weather)
result.write_summary("outputs/experiments/winter_climate_baseline.json")
The summary stores the experiment id, project id, case name, model, GreenFlux
version, record count, objective metrics and final observation. Full time-series
export still belongs to greenflux.io and scenario/project runners.
Experiments should also become the preferred unit for generated datasets: normal baselines, perturbed weather runs, fault-injected runs, stochastic control rollouts and sensor-subset studies can all share the same provenance shape.
Benchmark¶
greenflux.benchmarks runs one or more experiments and checks metric
thresholds:
from greenflux.benchmarks import BenchmarkThreshold, GreenhouseBenchmark, GreenhouseBenchmarkCase
benchmark = GreenhouseBenchmark(
benchmark_id="public_smoke_benchmark",
cases=(
GreenhouseBenchmarkCase(
name="winter_day",
experiment=experiment,
thresholds=(BenchmarkThreshold("objective_score", maximum=5.0),),
),
),
)
benchmark_result = benchmark.run(weather_by_case={"winter_day": weather})
Benchmarks are the right place for public comparison contracts: real-data replay cases, crop calibration cases, smoke tests, anomaly-detection datasets, fault-diagnosis scenarios, controller comparisons and future multi-model comparisons.
Relationship to declarative studies¶
The Problem → Experiment → Benchmark layer is the programmatic workflow (Python objects,
step-able environments, provenance and metric thresholds). There is also a declarative
sibling — studies/<id>/study.json, run with greenflux study run — that packages a named
analysis pipeline (stochastic planning, anomaly detection) with its parameters and writes a
result file, without writing Python. The two express "a reproducible question over a project" at
different granularities (a study is a multi-run analysis; an experiment is one problem-run).
They share one run-record shape: greenflux.provenance.RunProvenance. An experiment summary
carries a provenance block; a study result is {"provenance": {...}, "result": {...}} with the
same block (kind, run_id, project_id, greenflux_version, created_at_utc, metrics). A
report or benchmark consumer sees the same provenance envelope from either layer. See
studies.md.
Dataset Generation Contract¶
When an experiment is used to generate ML-ready data, it should keep these streams separate:
- true simulator state
- sensor or measurement stream
- actuation command
- equipment response
- weather and other exogenous forcing
- crop state and biological fluxes
- fault or perturbation metadata
- validation metrics and physical residuals
Why Not Spaces Yet?¶
GreenFlux does not currently need a first-class greenflux.spaces package.
The state/action/exogenous/observation concepts are present inside the problem
layer, but extracting them too early would add abstraction before we need it.
Add a dedicated spaces layer only when GreenFlux needs formal compatibility with control, MPC, reinforcement-learning or multi-agent APIs. Until then, problem and experiment objects are enough to keep studies modular and readable.