From 8998ce970562454e2da35fc91c8cefaa9def0abd Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 4 Jun 2026 21:43:57 +0100 Subject: [PATCH 01/21] cli: create toy accounts with kW-scale units Signed-off-by: Mohamed Belhsan Hmida --- flexmeasures/cli/data_add.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flexmeasures/cli/data_add.py b/flexmeasures/cli/data_add.py index 112b21dd4c..fd36967d20 100755 --- a/flexmeasures/cli/data_add.py +++ b/flexmeasures/cli/data_add.py @@ -1730,7 +1730,7 @@ def add_toy_account(kind: str, name: str): Sensor, name="day-ahead prices", generic_asset=nl_zone, - unit="EUR/MWh", + unit="EUR/kWh", timezone="Europe/Amsterdam", event_resolution=timedelta(minutes=60), knowledge_horizon=( @@ -1750,7 +1750,7 @@ def create_asset_with_one_sensor( asset_name: str, asset_type: str, sensor_name: str, - unit: str = "MW", + unit: str = "kW", parent_asset_id: int | None = None, flex_context: dict | None = None, flex_model: dict | None = None, @@ -1797,7 +1797,7 @@ def create_asset_with_one_sensor( latitude=location[0], longitude=location[1], flex_context={ - "site-power-capacity": "500 kVA", + "site-power-capacity": "500 kW", "consumption-price": {"sensor": day_ahead_sensor.id}, }, ) @@ -1811,7 +1811,7 @@ def create_asset_with_one_sensor( "discharging", parent_asset_id=building_asset.id, flex_model={ - "power-capacity": "500 kVA", + "power-capacity": "500 kW", "roundtrip-efficiency": "90%", "soc-max": "450 kWh", }, @@ -1918,7 +1918,7 @@ def create_asset_with_one_sensor( generic_asset=building_asset, timezone="Europe/Amsterdam", event_resolution="P1Y", - unit="MW", + unit="kW", ) db.session.commit() @@ -1934,7 +1934,7 @@ def create_asset_with_one_sensor( belief = TimedBelief( event_start=start_year, belief_time=server_now(), - event_value=0.5, + event_value=500, source=db.session.get(DataSource, 1), sensor=grid_connection_capacity, ) From 74fafb4b7608074b68bd3e5313a80c92002958cb Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 4 Jun 2026 21:44:30 +0100 Subject: [PATCH 02/21] cli/tests: cover kW-scale toy account data Signed-off-by: Mohamed Belhsan Hmida --- .../cli/tests/test_data_add_fresh_db.py | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/flexmeasures/cli/tests/test_data_add_fresh_db.py b/flexmeasures/cli/tests/test_data_add_fresh_db.py index cfd50741b9..f55aa1009b 100644 --- a/flexmeasures/cli/tests/test_data_add_fresh_db.py +++ b/flexmeasures/cli/tests/test_data_add_fresh_db.py @@ -374,6 +374,60 @@ def test_add_account( assert result.exit_code == 1 +def test_add_toy_account_battery_uses_kw_scale_units(app, fresh_db): + from flexmeasures.cli.data_add import add_toy_account + + result = app.test_cli_runner().invoke(add_toy_account, ["--kind", "battery"]) + + check_command_ran_without_error(result) + + toy_account = fresh_db.session.execute( + select(Account).filter_by(name="Toy Account") + ).scalar_one() + building = fresh_db.session.execute( + select(Asset).filter_by(name="toy-building", owner=toy_account) + ).scalar_one() + battery = fresh_db.session.execute( + select(Asset).filter_by(name="toy-battery", owner=toy_account) + ).scalar_one() + solar = fresh_db.session.execute( + select(Asset).filter_by(name="toy-solar", owner=toy_account) + ).scalar_one() + day_ahead_sensor = fresh_db.session.execute( + select(Sensor).filter_by(name="day-ahead prices") + ).scalar_one() + + assert day_ahead_sensor.unit == "EUR/kWh" + assert building.flex_context["site-power-capacity"] == "500 kW" + assert battery.flex_model["power-capacity"] == "500 kW" + assert battery.flex_model["soc-max"] == "450 kWh" + assert battery.sensors[0].unit == "kW" + assert solar.sensors[0].unit == "kW" + + +def test_add_toy_account_reporter_uses_kw_scale_units(app, fresh_db): + from flexmeasures.cli.data_add import add_toy_account + + result = app.test_cli_runner().invoke(add_toy_account, ["--kind", "reporter"]) + + check_command_ran_without_error(result) + + day_ahead_sensor = fresh_db.session.execute( + select(Sensor).filter_by(name="day-ahead prices") + ).scalar_one() + grid_connection_capacity = fresh_db.session.execute( + select(Sensor).filter_by(name="grid connection capacity") + ).scalar_one() + headroom = fresh_db.session.execute( + select(Sensor).filter_by(name="headroom") + ).scalar_one() + + assert day_ahead_sensor.unit == "EUR/kWh" + assert grid_connection_capacity.unit == "kW" + assert headroom.unit == "kW" + assert grid_connection_capacity.search_beliefs().values.flatten().tolist() == [500] + + @pytest.mark.parametrize("storage_power_capacity", ["sensor", "quantity", None]) @pytest.mark.parametrize("storage_efficiency", ["sensor", "quantity", None]) def test_add_storage_schedule( @@ -462,7 +516,12 @@ def test_add_storage_schedule( result = runner.invoke(add_schedule, cli_input) check_command_ran_without_error(result) - assert len(power_sensor.search_beliefs()) == 48 + schedule = power_sensor.search_beliefs() + max_power = schedule.event_value.abs().max() + assert len(schedule) == 48 + assert power_sensor.unit == "kW" + assert max_power > 1 + assert max_power <= 700 def test_add_storage_schedule_uses_state_of_charge_sensor_for_soc_at_start( From 360bac53b7ede728ee01a2e1f0612134f4edd6b9 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 4 Jun 2026 21:45:04 +0100 Subject: [PATCH 03/21] ci: use kW-scale toy tutorial inputs Signed-off-by: Mohamed Belhsan Hmida --- .github/workflows/build.yml | 2 +- .github/workflows/generate-dummy-price.sh | 48 +++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b97ca4545..4f9480db55 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,4 +70,4 @@ jobs: run: | docker exec --env-file .env fm-container flexmeasures add schedule --sensor 5 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H --flex-context '{"consumption-price": {"sensor": 1}}' \ - --flex-model "{\"duration\": \"PT4H\", \"process-type\": \"BREAKABLE\", \"power\": 0.2, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}" + --flex-model "{\"duration\": \"PT4H\", \"process-type\": \"BREAKABLE\", \"power\": 200, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}" diff --git a/.github/workflows/generate-dummy-price.sh b/.github/workflows/generate-dummy-price.sh index 06ee670454..6b674649ab 100755 --- a/.github/workflows/generate-dummy-price.sh +++ b/.github/workflows/generate-dummy-price.sh @@ -6,27 +6,27 @@ set -x TOMORROW=$(date --date="next day" '+%Y-%m-%d') echo "Hour,Price -${TOMORROW}T00:00:00,10 -${TOMORROW}T01:00:00,11 -${TOMORROW}T02:00:00,12 -${TOMORROW}T03:00:00,15 -${TOMORROW}T04:00:00,18 -${TOMORROW}T05:00:00,17 -${TOMORROW}T06:00:00,10.5 -${TOMORROW}T07:00:00,9 -${TOMORROW}T08:00:00,9.5 -${TOMORROW}T09:00:00,9 -${TOMORROW}T10:00:00,8.5 -${TOMORROW}T11:00:00,10 -${TOMORROW}T12:00:00,8 -${TOMORROW}T13:00:00,5 -${TOMORROW}T14:00:00,4 -${TOMORROW}T15:00:00,4 -${TOMORROW}T16:00:00,5.5 -${TOMORROW}T17:00:00,8 -${TOMORROW}T18:00:00,12 -${TOMORROW}T19:00:00,13 -${TOMORROW}T20:00:00,14 -${TOMORROW}T21:00:00,12.5 -${TOMORROW}T22:00:00,10 -${TOMORROW}T23:00:00,7" > prices-tomorrow.csv +${TOMORROW}T00:00:00,0.010 +${TOMORROW}T01:00:00,0.011 +${TOMORROW}T02:00:00,0.012 +${TOMORROW}T03:00:00,0.015 +${TOMORROW}T04:00:00,0.018 +${TOMORROW}T05:00:00,0.017 +${TOMORROW}T06:00:00,0.0105 +${TOMORROW}T07:00:00,0.009 +${TOMORROW}T08:00:00,0.0095 +${TOMORROW}T09:00:00,0.009 +${TOMORROW}T10:00:00,0.0085 +${TOMORROW}T11:00:00,0.010 +${TOMORROW}T12:00:00,0.008 +${TOMORROW}T13:00:00,0.005 +${TOMORROW}T14:00:00,0.004 +${TOMORROW}T15:00:00,0.004 +${TOMORROW}T16:00:00,0.0055 +${TOMORROW}T17:00:00,0.008 +${TOMORROW}T18:00:00,0.012 +${TOMORROW}T19:00:00,0.013 +${TOMORROW}T20:00:00,0.014 +${TOMORROW}T21:00:00,0.0125 +${TOMORROW}T22:00:00,0.010 +${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv From f0be0ca04b85893daa280bdbac21e561aed51949 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 4 Jun 2026 21:45:41 +0100 Subject: [PATCH 04/21] docs/scripts: run toy tutorials with kW-scale data Signed-off-by: Mohamed Belhsan Hmida --- .../tut/scripts/run-tutorial-in-docker.sh | 48 +++++++++---------- .../tut/scripts/run-tutorial2-in-docker.sh | 48 +++++++++---------- .../tut/scripts/run-tutorial3-in-docker.sh | 8 ++-- .../tut/scripts/run-tutorial4-in-docker.sh | 6 +-- 4 files changed, 55 insertions(+), 55 deletions(-) diff --git a/documentation/tut/scripts/run-tutorial-in-docker.sh b/documentation/tut/scripts/run-tutorial-in-docker.sh index 624a91adc7..1b8d47eb78 100755 --- a/documentation/tut/scripts/run-tutorial-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial-in-docker.sh @@ -9,30 +9,30 @@ echo "-----------------------------------------------------------------" echo "[TUTORIAL-RUNNER] loading prices..." TOMORROW=$(date --date="next day" '+%Y-%m-%d') echo "Hour,Price -${TOMORROW}T00:00:00,10 -${TOMORROW}T01:00:00,11 -${TOMORROW}T02:00:00,12 -${TOMORROW}T03:00:00,15 -${TOMORROW}T04:00:00,18 -${TOMORROW}T05:00:00,17 -${TOMORROW}T06:00:00,10.5 -${TOMORROW}T07:00:00,9 -${TOMORROW}T08:00:00,9.5 -${TOMORROW}T09:00:00,9 -${TOMORROW}T10:00:00,8.5 -${TOMORROW}T11:00:00,10 -${TOMORROW}T12:00:00,8 -${TOMORROW}T13:00:00,5 -${TOMORROW}T14:00:00,4 -${TOMORROW}T15:00:00,4 -${TOMORROW}T16:00:00,5.5 -${TOMORROW}T17:00:00,8 -${TOMORROW}T18:00:00,12 -${TOMORROW}T19:00:00,13 -${TOMORROW}T20:00:00,14 -${TOMORROW}T21:00:00,12.5 -${TOMORROW}T22:00:00,10 -${TOMORROW}T23:00:00,7" > prices-tomorrow.csv +${TOMORROW}T00:00:00,0.010 +${TOMORROW}T01:00:00,0.011 +${TOMORROW}T02:00:00,0.012 +${TOMORROW}T03:00:00,0.015 +${TOMORROW}T04:00:00,0.018 +${TOMORROW}T05:00:00,0.017 +${TOMORROW}T06:00:00,0.0105 +${TOMORROW}T07:00:00,0.009 +${TOMORROW}T08:00:00,0.0095 +${TOMORROW}T09:00:00,0.009 +${TOMORROW}T10:00:00,0.0085 +${TOMORROW}T11:00:00,0.010 +${TOMORROW}T12:00:00,0.008 +${TOMORROW}T13:00:00,0.005 +${TOMORROW}T14:00:00,0.004 +${TOMORROW}T15:00:00,0.004 +${TOMORROW}T16:00:00,0.0055 +${TOMORROW}T17:00:00,0.008 +${TOMORROW}T18:00:00,0.012 +${TOMORROW}T19:00:00,0.013 +${TOMORROW}T20:00:00,0.014 +${TOMORROW}T21:00:00,0.0125 +${TOMORROW}T22:00:00,0.010 +${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv docker cp prices-tomorrow.csv $CONTAINER_NAME:/app diff --git a/documentation/tut/scripts/run-tutorial2-in-docker.sh b/documentation/tut/scripts/run-tutorial2-in-docker.sh index 006c0b0e9e..cb4412c216 100755 --- a/documentation/tut/scripts/run-tutorial2-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial2-in-docker.sh @@ -11,30 +11,30 @@ echo "[TUTORIAL-RUNNER] loading solar production data..." TOMORROW=$(date --date="next day" '+%Y-%m-%d') echo "Hour,Production -${TOMORROW}T00:00:00,0.0 -${TOMORROW}T01:00:00,0.0 -${TOMORROW}T02:00:00,0.0 -${TOMORROW}T03:00:00,0.0 -${TOMORROW}T04:00:00,0.01 -${TOMORROW}T05:00:00,0.03 -${TOMORROW}T06:00:00,0.06 -${TOMORROW}T07:00:00,0.1 -${TOMORROW}T08:00:00,0.14 -${TOMORROW}T09:00:00,0.17 -${TOMORROW}T10:00:00,0.19 -${TOMORROW}T11:00:00,0.21 -${TOMORROW}T12:00:00,0.22 -${TOMORROW}T13:00:00,0.21 -${TOMORROW}T14:00:00,0.19 -${TOMORROW}T15:00:00,0.17 -${TOMORROW}T16:00:00,0.14 -${TOMORROW}T17:00:00,0.1 -${TOMORROW}T18:00:00,0.06 -${TOMORROW}T19:00:00,0.03 -${TOMORROW}T20:00:00,0.01 -${TOMORROW}T21:00:00,0.0 -${TOMORROW}T22:00:00,0.0 -${TOMORROW}T23:00:00,0.0" > solar-tomorrow.csv +${TOMORROW}T00:00:00,0 +${TOMORROW}T01:00:00,0 +${TOMORROW}T02:00:00,0 +${TOMORROW}T03:00:00,0 +${TOMORROW}T04:00:00,10 +${TOMORROW}T05:00:00,30 +${TOMORROW}T06:00:00,60 +${TOMORROW}T07:00:00,100 +${TOMORROW}T08:00:00,140 +${TOMORROW}T09:00:00,170 +${TOMORROW}T10:00:00,190 +${TOMORROW}T11:00:00,210 +${TOMORROW}T12:00:00,220 +${TOMORROW}T13:00:00,210 +${TOMORROW}T14:00:00,190 +${TOMORROW}T15:00:00,170 +${TOMORROW}T16:00:00,140 +${TOMORROW}T17:00:00,100 +${TOMORROW}T18:00:00,60 +${TOMORROW}T19:00:00,30 +${TOMORROW}T20:00:00,10 +${TOMORROW}T21:00:00,0 +${TOMORROW}T22:00:00,0 +${TOMORROW}T23:00:00,0" > solar-tomorrow.csv docker cp solar-tomorrow.csv $CONTAINER_NAME:/app/ diff --git a/documentation/tut/scripts/run-tutorial3-in-docker.sh b/documentation/tut/scripts/run-tutorial3-in-docker.sh index 5d18e11002..85d856f56a 100755 --- a/documentation/tut/scripts/run-tutorial3-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial3-in-docker.sh @@ -14,12 +14,12 @@ echo "[TUTORIAL-RUNNER] Computing schedule for PV curtailment (using artificial echo '''{ "consumption-price": [ - {"start": "'${TOMORROW}'T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "'${TOMORROW}'T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "'${TOMORROW}'T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "'${TOMORROW}'T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "'${TOMORROW}'T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "'${TOMORROW}'T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "'${TOMORROW}'T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "'${TOMORROW}'T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] }''' > tutorial3-priceprofile-flex-context.json docker cp tutorial3-priceprofile-flex-context.json $CONTAINER_NAME:/app/ diff --git a/documentation/tut/scripts/run-tutorial4-in-docker.sh b/documentation/tut/scripts/run-tutorial4-in-docker.sh index 9d9058e709..d9f118e564 100755 --- a/documentation/tut/scripts/run-tutorial4-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial4-in-docker.sh @@ -15,16 +15,16 @@ echo "[TUTORIAL-RUNNER] Creating three process schedules ..." docker exec -it $CONTAINER_NAME flexmeasures add schedule --sensor 4 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{"consumption-price": {"sensor": 1}}' \ - --flex-model '{"duration": "PT4H", "process-type": "INFLEXIBLE", "power": 0.2, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' + --flex-model '{"duration": "PT4H", "process-type": "INFLEXIBLE", "power": 200, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' docker exec -it $CONTAINER_NAME flexmeasures add schedule --sensor 5 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{"consumption-price": {"sensor": 1}}' \ - --flex-model '{"duration": "PT4H", "process-type": "BREAKABLE", "power": 0.2, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' + --flex-model '{"duration": "PT4H", "process-type": "BREAKABLE", "power": 200, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' docker exec -it $CONTAINER_NAME flexmeasures add schedule --sensor 6 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{"consumption-price": {"sensor": 1}}' \ - --flex-model '{"duration": "PT4H", "process-type": "SHIFTABLE", "power": 0.2, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' + --flex-model '{"duration": "PT4H", "process-type": "SHIFTABLE", "power": 200, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' echo "Now visit http://localhost:5000/assets/6/graphs to see all three schedules." From 9fae10c622bf19fa6e38b47bab7cd586e95ddfd7 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 4 Jun 2026 21:47:37 +0100 Subject: [PATCH 05/21] docs/tutorial: show battery scheduling in kW units Signed-off-by: Mohamed Belhsan Hmida --- documentation/dev/docker-compose.rst | 6 +- documentation/tut/toy-example-expanded.rst | 52 +++++++------- .../tut/toy-example-from-scratch.rst | 6 +- documentation/tut/toy-example-setup.rst | 67 +++++++++---------- 4 files changed, 65 insertions(+), 66 deletions(-) diff --git a/documentation/dev/docker-compose.rst b/documentation/dev/docker-compose.rst index 759e33ec19..dac8cf9328 100644 --- a/documentation/dev/docker-compose.rst +++ b/documentation/dev/docker-compose.rst @@ -123,7 +123,7 @@ The charging/discharging schedule should be there: .. code-block:: bash ┌────────────────────────────────────────────────────────────┐ - │ ▐ ▐▀▀▌ ▛▀▀│ 0.5MW + │ ▐ ▐▀▀▌ ▛▀▀│ 500kW │ ▞▌ ▌ ▌ ▌ │ │ ▌▌ ▌ ▐ ▗▘ │ │ ▌▌ ▌ ▐ ▐ │ @@ -131,7 +131,7 @@ The charging/discharging schedule should be there: │ ▐ ▐ ▐ ▝▖ ▞ │ │ ▌ ▐ ▐ ▌ ▌ │ │ ▐ ▝▖ ▌ ▌ ▌ │ - │▀▘───▀▀▀▀▖─────▌────▀▀▀▀▀▀▀▀▀▌─────▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘───│ 0.0MW + │▀▘───▀▀▀▀▖─────▌────▀▀▀▀▀▀▀▀▀▌─────▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▘───│ 0kW │ ▌ ▐ ▚ ▌ │ │ ▌ ▞ ▐ ▗▘ │ │ ▌ ▌ ▐ ▞ │ @@ -139,7 +139,7 @@ The charging/discharging schedule should be there: │ ▐ ▐ ▌ ▗▘ │ │ ▐ ▌ ▌ ▐ │ │ ▝▖ ▌ ▌ ▞ │ - │ ▙▄▟ ▐▄▄▌ │ -0.5MW + │ ▙▄▟ ▐▄▄▌ │ -500kW └────────────────────────────────────────────────────────────┘ 10 20 30 40 ██ discharging diff --git a/documentation/tut/toy-example-expanded.rst b/documentation/tut/toy-example-expanded.rst index 3922ad9a3e..ade5c3fff5 100644 --- a/documentation/tut/toy-example-expanded.rst +++ b/documentation/tut/toy-example-expanded.rst @@ -6,7 +6,7 @@ Toy example II: Adding solar production, and a limit on the grid connection ============================================================================ -So far we haven't taken into account any other devices that consume or produce electricity. The battery was free to use all available capacity (which was 500 kVA, both its own maximum charge/discharge rate, and the maximum grid capacity). +So far we haven't taken into account any other devices that consume or produce electricity. The battery was free to use all available capacity (which was 500 kW, both its own maximum charge/discharge rate, and the maximum grid capacity). What if other devices will be using some of that capacity? Our schedules need to reflect that, so we stay within given limits. @@ -24,36 +24,36 @@ How does it work? Adding PV production forecasts ------------------------------ -First, we'll create a new CSV file with solar forecasts (MW, see the setup for sensor 3 in part I of this tutorial) for tomorrow. +First, we'll create a new CSV file with solar forecasts (kW, see the setup for sensor 3 in part I of this tutorial) for tomorrow. .. code-block:: bash $ TOMORROW=$(date --date="next day" '+%Y-%m-%d') $ echo "Hour,Price - $ ${TOMORROW}T00:00:00,0.0 - $ ${TOMORROW}T01:00:00,0.0 - $ ${TOMORROW}T02:00:00,0.0 - $ ${TOMORROW}T03:00:00,0.0 - $ ${TOMORROW}T04:00:00,0.01 - $ ${TOMORROW}T05:00:00,0.03 - $ ${TOMORROW}T06:00:00,0.06 - $ ${TOMORROW}T07:00:00,0.1 - $ ${TOMORROW}T08:00:00,0.14 - $ ${TOMORROW}T09:00:00,0.17 - $ ${TOMORROW}T10:00:00,0.19 - $ ${TOMORROW}T11:00:00,0.21 - $ ${TOMORROW}T12:00:00,0.22 - $ ${TOMORROW}T13:00:00,0.21 - $ ${TOMORROW}T14:00:00,0.19 - $ ${TOMORROW}T15:00:00,0.17 - $ ${TOMORROW}T16:00:00,0.14 - $ ${TOMORROW}T17:00:00,0.1 - $ ${TOMORROW}T18:00:00,0.06 - $ ${TOMORROW}T19:00:00,0.03 - $ ${TOMORROW}T20:00:00,0.01 - $ ${TOMORROW}T21:00:00,0.0 - $ ${TOMORROW}T22:00:00,0.0 - $ ${TOMORROW}T23:00:00,0.0" > solar-tomorrow.csv + $ ${TOMORROW}T00:00:00,0 + $ ${TOMORROW}T01:00:00,0 + $ ${TOMORROW}T02:00:00,0 + $ ${TOMORROW}T03:00:00,0 + $ ${TOMORROW}T04:00:00,10 + $ ${TOMORROW}T05:00:00,30 + $ ${TOMORROW}T06:00:00,60 + $ ${TOMORROW}T07:00:00,100 + $ ${TOMORROW}T08:00:00,140 + $ ${TOMORROW}T09:00:00,170 + $ ${TOMORROW}T10:00:00,190 + $ ${TOMORROW}T11:00:00,210 + $ ${TOMORROW}T12:00:00,220 + $ ${TOMORROW}T13:00:00,210 + $ ${TOMORROW}T14:00:00,190 + $ ${TOMORROW}T15:00:00,170 + $ ${TOMORROW}T16:00:00,140 + $ ${TOMORROW}T17:00:00,100 + $ ${TOMORROW}T18:00:00,60 + $ ${TOMORROW}T19:00:00,30 + $ ${TOMORROW}T20:00:00,10 + $ ${TOMORROW}T21:00:00,0 + $ ${TOMORROW}T22:00:00,0 + $ ${TOMORROW}T23:00:00,0" > solar-tomorrow.csv Then, we read in the created CSV file as beliefs data. This time, different to above, we want to use a new data source (not the user) ― it represents whoever is making these solar production forecasts. diff --git a/documentation/tut/toy-example-from-scratch.rst b/documentation/tut/toy-example-from-scratch.rst index e4a9d40d04..bb254f088a 100644 --- a/documentation/tut/toy-example-from-scratch.rst +++ b/documentation/tut/toy-example-from-scratch.rst @@ -139,7 +139,7 @@ Great. Let's see what we made: Data spans 12 hours and starts at 2025-11-29 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ - │ ▛▀▜ ▞▀▀▌ ▐▀▀▚ │ 0.5MW + │ ▛▀▜ ▞▀▀▌ ▐▀▀▚ │ 500kW │ ▌ ▌ ▌ ▌ ▐ ▐ │ │ ▗▘ ▌ ▌ ▌ ▐ ▐ │ │ ▐ ▌ ▌ ▐ ▌ ▐ │ @@ -147,7 +147,7 @@ Great. Let's see what we made: │▌ ▐ ▐ ▐ ▐ ▌ ▌│ │▐ ▌ ▐ ▐ ▌ ▐ ▌│ │ ▌ ▌ ▌ ▐ ▌ ▐ ▐│ - │─▚▄▄▌────▀▙▄▄▄▖────▐────▀▚▄▄▄▄▄▄▄▄▖─────▗▄▄▄▄▄▄▄▄▄▄▄▄▄▟────▝│ 0.0MW + │─▚▄▄▌────▀▙▄▄▄▖────▐────▀▚▄▄▄▄▄▄▄▄▖─────▗▄▄▄▄▄▄▄▄▄▄▄▄▄▟────▝│ 0kW │ ▌ ▞ ▐ ▌ │ │ ▚ ▌ ▐ ▗▘ │ │ ▐ ▌ ▐ ▞ │ @@ -155,7 +155,7 @@ Great. Let's see what we made: │ ▝▖ ▐ ▌ ▗▘ │ │ ▌ ▞ ▌ ▐ │ │ ▌ ▌ ▚ ▞ │ - │ ▙▄▄▘ ▐▄▄▌ │ -0.5MW + │ ▙▄▄▘ ▐▄▄▌ │ -500kW └────────────────────────────────────────────────────────────┘ 06:00 09:00 12:00 15:00 ██ discharging (toy-battery) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index ddcee09284..82a6964e66 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -213,7 +213,7 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI Flex-Context Flex-Model -------------------------------- ------------ - site-power-capacity: 500 kVA + site-power-capacity: 500 kW consumption-price: {'sensor': 1} ==================================== @@ -230,7 +230,7 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI You can see that this building asset has some meta information about how FlexMeasures needs to schedule: - Within :ref:`flex_context`, we noted where to find the relevant optimization signal for electricity consumption (Sensor 1, which stores day-ahead prices). -- Also, the building has a grid connection capacity of 500 kVA, meaning that the total power flowing into or out of the building cannot exceed this value. +- Also, the building has a grid connection capacity of 500 kW, meaning that the total power flowing into or out of the building cannot exceed this value. Now let's look at the battery asset, as well: @@ -251,7 +251,7 @@ Now let's look at the battery asset, as well: Flex-Context Flex-Model -------------- ------------------------- - power-capacity: 500 kVA + power-capacity: 500 kW roundtrip-efficiency: 90% soc-max: 450 kWh @@ -265,14 +265,14 @@ Now let's look at the battery asset, as well: ID Name Unit Resolution Timezone Attributes ---- ----------- ------ ------------ ---------------- ------------ - 2 discharging MW 15 minutes Europe/Amsterdam + 2 discharging kW 15 minutes Europe/Amsterdam Yes, that is quite a large battery :) You can also see that the asset has some meta information about its scheduling. -- Within :ref:`flex_model`, we noted that the battery's power capacity is the same as the building's grid connection capacity (500 kVA), meaning the battery can charge or discharge at full power without overloading the connection, but no other devices can (we will come back to this limitation). +- Within :ref:`flex_model`, we noted that the battery's power capacity is the same as the building's grid connection capacity (500 kW), meaning the battery can charge or discharge at full power without overloading the connection, but no other devices can (we will come back to this limitation). - Also noted is the battery's roundtrip efficiency (90%) and maximum state of charge (450 kWh). .. note:: Obviously, you can use the ``flexmeasures`` command to create your own, custom account and assets. See :ref:`cli`. And to create, edit or read asset data via the API, see :ref:`v3_0`. @@ -313,36 +313,36 @@ And on the flex-model of the battery can be seen on its properties page (and is Add some price data --------------------------------------- -Now to add price data. First, we'll create the CSV file with prices (EUR/MWh, see the setup for sensor 1 above) for tomorrow. +Now to add price data. First, we'll create the CSV file with prices (EUR/kWh, see the setup for sensor 1 above) for tomorrow. .. code-block:: bash $ TOMORROW=$(date --date="next day" '+%Y-%m-%d') $ echo "Hour,Price - $ ${TOMORROW}T00:00:00,10 - $ ${TOMORROW}T01:00:00,11 - $ ${TOMORROW}T02:00:00,12 - $ ${TOMORROW}T03:00:00,15 - $ ${TOMORROW}T04:00:00,18 - $ ${TOMORROW}T05:00:00,17 - $ ${TOMORROW}T06:00:00,10.5 - $ ${TOMORROW}T07:00:00,9 - $ ${TOMORROW}T08:00:00,9.5 - $ ${TOMORROW}T09:00:00,9 - $ ${TOMORROW}T10:00:00,8.5 - $ ${TOMORROW}T11:00:00,10 - $ ${TOMORROW}T12:00:00,8 - $ ${TOMORROW}T13:00:00,5 - $ ${TOMORROW}T14:00:00,4 - $ ${TOMORROW}T15:00:00,4 - $ ${TOMORROW}T16:00:00,5.5 - $ ${TOMORROW}T17:00:00,8 - $ ${TOMORROW}T18:00:00,12 - $ ${TOMORROW}T19:00:00,13 - $ ${TOMORROW}T20:00:00,14 - $ ${TOMORROW}T21:00:00,12.5 - $ ${TOMORROW}T22:00:00,10 - $ ${TOMORROW}T23:00:00,7" > prices-tomorrow.csv + $ ${TOMORROW}T00:00:00,0.010 + $ ${TOMORROW}T01:00:00,0.011 + $ ${TOMORROW}T02:00:00,0.012 + $ ${TOMORROW}T03:00:00,0.015 + $ ${TOMORROW}T04:00:00,0.018 + $ ${TOMORROW}T05:00:00,0.017 + $ ${TOMORROW}T06:00:00,0.0105 + $ ${TOMORROW}T07:00:00,0.009 + $ ${TOMORROW}T08:00:00,0.0095 + $ ${TOMORROW}T09:00:00,0.009 + $ ${TOMORROW}T10:00:00,0.0085 + $ ${TOMORROW}T11:00:00,0.010 + $ ${TOMORROW}T12:00:00,0.008 + $ ${TOMORROW}T13:00:00,0.005 + $ ${TOMORROW}T14:00:00,0.004 + $ ${TOMORROW}T15:00:00,0.004 + $ ${TOMORROW}T16:00:00,0.0055 + $ ${TOMORROW}T17:00:00,0.008 + $ ${TOMORROW}T18:00:00,0.012 + $ ${TOMORROW}T19:00:00,0.013 + $ ${TOMORROW}T20:00:00,0.014 + $ ${TOMORROW}T21:00:00,0.0125 + $ ${TOMORROW}T22:00:00,0.010 + $ ${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv This is time series data, in FlexMeasures we call *"beliefs"*. Beliefs can also be sent to FlexMeasures via API or imported from open data hubs like `ENTSO-E `_ or `Weather Forecast APIs `_. However, in this tutorial we'll show how you can read data in from a CSV file. Sometimes that's just what you need :) @@ -368,19 +368,19 @@ Let's look at the price data we just loaded: │ ▗▀▚▖ │ │ ▗▘ ▝▖ │ │ ▞ ▌ │ - │ ▟ ▐ │ 15EUR/MWh + │ ▟ ▐ │ 0.015EUR/kWh │ ▗▘ ▝▖ ▗ │ │ ▗▘ ▚ ▄▞▘▚▖ │ │ ▞ ▐ ▄▀▘ ▝▄ │ │ ▄▞ ▌ ▛ ▖ │ │▀ ▚ ▐ ▝▖ │ - │ ▝▚ ▖ ▗▘ ▝▖ │ 10EUR/MWh + │ ▝▚ ▖ ▗▘ ▝▖ │ 0.010EUR/kWh │ ▀▄▄▞▀▄▄ ▗▀▝▖ ▞ ▐ │ │ ▀▀▜▘ ▝▚ ▗▘ ▚ │ │ ▌ ▞ ▌│ │ ▝▖ ▞ ▝│ │ ▐ ▞ │ - │ ▚ ▗▞ │ 5EUR/MWh + │ ▚ ▗▞ │ 0.005EUR/kWh │ ▀▚▄▄▄▄▘ │ └────────────────────────────────────────────────────────────┘ 5 10 15 20 @@ -396,4 +396,3 @@ Again, we can also view these prices in the `FlexMeasures UI Date: Thu, 4 Jun 2026 21:48:12 +0100 Subject: [PATCH 06/21] docs/tutorial: align follow-on examples with kW units Signed-off-by: Mohamed Belhsan Hmida --- .../toy-example-multiasset-curtailment.rst | 58 +++++++++---------- documentation/tut/toy-example-process.rst | 26 ++++----- documentation/tut/toy-example-reporter.rst | 8 +-- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/documentation/tut/toy-example-multiasset-curtailment.rst b/documentation/tut/toy-example-multiasset-curtailment.rst index b9b0e3ff99..7ffdc35f57 100644 --- a/documentation/tut/toy-example-multiasset-curtailment.rst +++ b/documentation/tut/toy-example-multiasset-curtailment.rst @@ -37,12 +37,12 @@ Also, we want to create a situation with negative prices, so curtailment makes s $ # this flex context has negative prices between 12:00 and 14:00 $ echo '''{ "consumption-price": [ - {"start": "'${TOMORROW}'T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "'${TOMORROW}'T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "'${TOMORROW}'T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "'${TOMORROW}'T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "'${TOMORROW}'T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "'${TOMORROW}'T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "'${TOMORROW}'T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "'${TOMORROW}'T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] }''' > tutorial3-priceprofile-flex-context.json $ docker cp tutorial3-priceprofile-flex-context.json flexmeasures-server-1:/app/ @@ -71,12 +71,12 @@ Also, we want to create a situation with negative prices, so curtailment makes s ], "flex-context": { "consumption-price": [ - {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] } } @@ -109,12 +109,12 @@ Also, we want to create a situation with negative prices, so curtailment makes s ], flex_context={ "consumption-price": [ - {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] }, ) @@ -132,7 +132,7 @@ Great. Let's see what we made: Data spans 12 hours and starts at 2025-11-29 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ - │ ▞▀▀▀▌ │ 0.2MW + │ ▞▀▀▀▌ │ 200kW │ ▄▄▄▄▞ ▌ ▗▄▄▄▖ │ │ ▗▘ ▚ ▐ ▝▖ │ │ ▛▀▀▀▘ ▐ ▐ ▝▀▀▀▜ │ @@ -140,7 +140,7 @@ Great. Let's see what we made: │ ▄▄▄▄▌ ▐ ▌ ▐▄▄▄▄ │ │ ▗▘ ▐ ▌ ▐ │ │ ▐ ▐ ▌ ▌ │ - │▄▄▄▄▌ ▐ ▌ ▐▄▄▄▄ │ 0.1MW + │▄▄▄▄▌ ▐ ▌ ▐▄▄▄▄ │ 100kW │ ▌ ▌ ▐ │ │ ▌ ▐ ▌ │ │ ▌ ▐ ▐ │ @@ -148,7 +148,7 @@ Great. Let's see what we made: │ ▌ ▐ │ │ ▌ ▐ │ │ ▚ ▐ │ - │▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▐▄▄▄▄▄▄▄▄▌▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁│ -0.0MW + │▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▐▄▄▄▄▄▄▄▄▌▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁│ 0kW └────────────────────────────────────────────────────────────┘ 06:00 09:00 12:00 15:00 ██ production (toy-solar) @@ -205,12 +205,12 @@ Note that we are still passing in the flex-context with block price profiles her ], "flex-context": { "consumption-price": [ - {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] } } @@ -244,12 +244,12 @@ Note that we are still passing in the flex-context with block price profiles her ], flex_context={ "consumption-price": [ - {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "10 EUR/MWh"} + {"start": "2025-11-18T00:00+01", "duration": "PT24H", "value": "0.010 EUR/kWh"} ], "production-price": [ - {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "4 EUR/MWh"}, - {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-10 EUR/MWh"}, - {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "4 EUR/MWh"} + {"start": "2025-11-18T05:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"}, + {"start": "2025-11-18T12:00+01", "duration": "PT2H", "value": "-0.010 EUR/kWh"}, + {"start": "2025-11-18T14:00+01", "duration": "PT7H", "value": "0.004 EUR/kWh"} ] }, ) @@ -276,22 +276,22 @@ And here is the CLI version: The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ │ ▛▀▀│ - │ ▐ │ 0.4MW + │ ▐ │ 400kW │ ▐ │ │ ▞ │ │ ▐▀▌ ▌ │ │ ▐ ▌▄▄▄▄▄▖ ▌ │ - │ ▄▄▄▄▄▞▀▀▞▀▐ ▝▀▚▞▀▚▄▄▄▄▖ ▐ │ 0.2MW + │ ▄▄▄▄▄▞▀▀▞▀▐ ▝▀▚▞▀▚▄▄▄▄▖ ▐ │ 200kW │ ▄▄▄▄▞▀▀▀▀▘ ▌ ▐ ▝▀▀▀▀▚▄▄▄▄ ▐ │ │▄▄▄▄▞ ▗▘ ▐ ▚▄▄▄▄▌ │ │ ▞ ▐ ▌▄▄▄▄│ │ ▗▘ ▐ ▐ │ - │▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▔▔▔▔▌▔▔▔▔▔▔▔▔▔▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▔▔▔▔▔│ -0.0MW + │▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▔▔▔▔▌▔▔▔▔▔▔▔▔▔▐▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▔▔▔▔▔│ 0kW │ ▌ ▌ │ │ ▌ ▌ │ │ ▌ ▐ │ │ ▌ ▗▖▐ │ - │ ▐▄▄▄▄▞▀▘▝▘ │ -0.2MW + │ ▐▄▄▄▄▞▀▘▝▘ │ -200kW └────────────────────────────────────────────────────────────┘ 06:00 09:00 12:00 15:00 ██ production (toy-solar) ██ discharging (toy-battery) @@ -325,7 +325,7 @@ We see the battery cycling twice, as before, but now it also soaks up solar prod │▄▄▌▄▄▀▀▀▀▀▀▀▀▀▐ ▐ ▝▀▀▀▀▀▀▀▀▀▄▄▄▄▄ ▌ ▙▘ │ │ ▌ ▐ ▌ ▚▄▄▄▐▖ █ │ │ ▐ ▌ ▌ ▐▝▀▀▀▐▚▄▄▄▄│ - │▔▔▝▀▀▀▀▀▀▌▔▔▔▔▌▔▔▔▔▔▝▀▀▀▀▀▀▀▀▌▔▔▔▔▔▔▔▔▔▔▞▀▀▀▀▀▀▀▀▀▔▔▔▔▔▔▔▔▔▔│ 0.0MW + │▔▔▝▀▀▀▀▀▀▌▔▔▔▔▌▔▔▔▔▔▝▀▀▀▀▀▀▀▀▌▔▔▔▔▔▔▔▔▔▔▞▀▀▀▀▀▀▀▀▀▔▔▔▔▔▔▔▔▔▔│ 0kW │ ▌ ▐ ▐ ▞ │ │ ▚ ▐ ▐ ▌ │ │ ▐ ▌ ▐ ▌ │ @@ -333,7 +333,7 @@ We see the battery cycling twice, as before, but now it also soaks up solar prod │ ▐ ▐ ▌ ▐ │ │ ▌ ▞ ▌ ▐ │ │ ▌ ▌ ▌ ▌ │ - │ ▙▟ ▐▄▄▄▄▄▄▄▌ │ -0.5MW + │ ▙▟ ▐▄▄▄▄▄▄▄▌ │ -500kW └────────────────────────────────────────────────────────────┘ 09:00 12:00 15:00 18:00 ██ production (toy-solar) ██ discharging (toy-battery) diff --git a/documentation/tut/toy-example-process.rst b/documentation/tut/toy-example-process.rst index 0a2b2b01d7..9f32dfe14b 100644 --- a/documentation/tut/toy-example-process.rst +++ b/documentation/tut/toy-example-process.rst @@ -8,7 +8,7 @@ Until this point we've been using a static battery, one of the most flexible ene However, in some settings, we can reduce electricity bills by **just** smartly timing the necessary work that we know we have to do. We call this work a "process". In other words, if the process can be displaced, by breaking it into smaller consumption periods or shifting its start time, the process run can match the lower price hours better. -For example, we could have a load that consumes energy at a constant rate (e.g. 200kW) for a fixed duration (e.g. 4h), but there's some flexibility in the start time. In that case, we could find the optimal start time in order to minimize the energy cost. +For example, we could have a load that consumes energy at a constant rate (e.g. 200 kW) for a fixed duration (e.g. 4h), but there's some flexibility in the start time. In that case, we could find the optimal start time in order to minimize the energy cost. Examples of flexible processes are: - Water irrigation in agriculture @@ -39,9 +39,9 @@ Before moving forward, we'll add the `process` asset and three sensors to store User with email toy-user@flexmeasures.io already exists in account Docker Toy Account. The sensor recording day-ahead prices is day-ahead prices (ID: 1). Created - Created - Created - Created + Created + Created + Created The sensor recording the power of the inflexible load is Power (Inflexible) (ID: 4). The sensor recording the power of the breakable load is Power (Breakable) (ID: 5). The sensor recording the power of the shiftable load is Power (Shiftable) (ID: 6). @@ -50,7 +50,7 @@ Before moving forward, we'll add the `process` asset and three sensors to store Trigger an updated schedule ---------------------------- -In this example, we are planning to consume at a 200kW constant power for a period of 4h. +In this example, we are planning to consume at a 200 kW constant power for a period of 4h. This load is to be scheduled for tomorrow, except from the period from 3pm to 4pm (imposed using the ``time-restrictions`` parameter). @@ -62,7 +62,7 @@ Now we are ready to schedule a process. Let's start with the INFLEXIBLE policy, $ flexmeasures add schedule --sensor 4 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{\"consumption-price\": {\"sensor\": 1}}' \ - --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"INFLEXIBLE\", \"power\": 0.2, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ + --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"INFLEXIBLE\", \"power\": 200, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ Under the INFLEXIBLE policy, the process starts as soon as possible, in this case, coinciding with the start of the planning window. @@ -73,7 +73,7 @@ Following the INFLEXIBLE policy, we'll schedule the same 4h consumption requirem $ flexmeasures add schedule --sensor 5 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{\"consumption-price\": {\"sensor\": 1}}' \ - --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"BREAKABLE\", \"power\": 0.2, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ + --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"BREAKABLE\", \"power\": 200, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ The BREAKABLE policy splits or breaks the process into blocks that can be scheduled discontinuously. The smallest possible unit is (currently) determined by the sensor's resolution. @@ -84,7 +84,7 @@ Finally, we'll schedule the process using the SHIFTABLE policy. The 4h block can $ flexmeasures add schedule --sensor 6 --scheduler ProcessScheduler \ --start ${TOMORROW}T00:00:00+02:00 --duration PT24H \ --flex-context '{\"consumption-price\": {\"sensor\": 1}}' \ - --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"SHIFTABLE\", \"power\": 0.2, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ + --flex-model '{\"duration\": \"PT4H\", \"process-type\": \"SHIFTABLE\", \"power\": 200, \"time-restrictions\": [{\"start\": \"${TOMORROW}T15:00:00+02:00\", \"duration\": \"PT1H\"}]}' \ Results @@ -111,15 +111,15 @@ Let's list the power costs which the policies achieved for each of the four bloc +-------------------------+------------+-----------+-----------+ | Block | INFLEXIBLE | BREAKABLE | SHIFTABLE | +=========================+============+===========+===========+ -| 1 | 10.00 | 5.00 | 10.00 | +| 1 | 0.010 | 0.005 | 0.010 | +-------------------------+------------+-----------+-----------+ -| 2 | 11.00 | 4.00 | 8.00 | +| 2 | 0.011 | 0.004 | 0.008 | +-------------------------+------------+-----------+-----------+ -| 3 | 12.00 | 5.50 | 5.00 | +| 3 | 0.012 | 0.0055 | 0.005 | +-------------------------+------------+-----------+-----------+ -| 4 | 15.00 | 7.00 | 4.00 | +| 4 | 0.015 | 0.007 | 0.004 | +-------------------------+------------+-----------+-----------+ -| Average Price (EUR/MWh) | 12.00 | 5.37 | 6.75 | +| Average Price (EUR/kWh) | 0.012 | 0.00537 | 0.00675 | +-------------------------+------------+-----------+-----------+ | Total Cost (EUR) | 9.60 | 4.29 | 5.40 | +-------------------------+------------+-----------+-----------+ diff --git a/documentation/tut/toy-example-reporter.rst b/documentation/tut/toy-example-reporter.rst index 7457c200f3..1ea8377f10 100644 --- a/documentation/tut/toy-example-reporter.rst +++ b/documentation/tut/toy-example-reporter.rst @@ -55,19 +55,19 @@ Run the command below to show the values for our newly-created `grid connection │ │ │ │ │ │ - │ │ 1.0MW + │ │ 500.5kW │ │ │ │ │ │ - │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ 0.5MW + │▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄│ 500.0kW │ │ │ │ │ │ - │▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁│ 0.0MW + │ │ 499.5kW │ │ │ │ │ │ - │ │ -0.5MW + │ │ 499.0kW └────────────────────────────────────────────────────────────┘ 06:00 12:00 18:00 ██ grid connection capacity (toy-building) From 1aa042653efdca8f8d529e156a39009abf33e585 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Fri, 5 Jun 2026 03:57:01 +0100 Subject: [PATCH 07/21] docs: add changelog entry Signed-off-by: Mohamed Belhsan Hmida --- documentation/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index c2d79a3685..b55c5ec94f 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -11,6 +11,7 @@ New features ------------- * Floor off-clock API datetimes to a non-instantaneous sensor's resolution by default when ingesting sensor data, uploading sensor data, and handling scheduler flex-model timed events; configurable with the ``floor_datetimes_to_resolution`` sensor attribute [see `PR #2146 `_] * Sensor references in flex-model and flex-context support various ways of filtering by source [see `PR #2209 `_] +* Create toy tutorial accounts with kW-scale power sensors and ``EUR/kWh`` day-ahead prices [see `PR #2223 `_] Infrastructure / Support From 03bc4da64de5bb509c16ac1ada1007e719b379ec Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Fri, 5 Jun 2026 13:25:43 +0100 Subject: [PATCH 08/21] docs/tutorial: import toy prices as EUR per MWh Signed-off-by: Mohamed Belhsan Hmida --- .../tut/scripts/run-tutorial-in-docker.sh | 50 ++++++++--------- documentation/tut/toy-example-setup.rst | 55 +++++++++---------- 2 files changed, 52 insertions(+), 53 deletions(-) diff --git a/documentation/tut/scripts/run-tutorial-in-docker.sh b/documentation/tut/scripts/run-tutorial-in-docker.sh index 1b8d47eb78..f1d2c3a575 100755 --- a/documentation/tut/scripts/run-tutorial-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial-in-docker.sh @@ -9,35 +9,35 @@ echo "-----------------------------------------------------------------" echo "[TUTORIAL-RUNNER] loading prices..." TOMORROW=$(date --date="next day" '+%Y-%m-%d') echo "Hour,Price -${TOMORROW}T00:00:00,0.010 -${TOMORROW}T01:00:00,0.011 -${TOMORROW}T02:00:00,0.012 -${TOMORROW}T03:00:00,0.015 -${TOMORROW}T04:00:00,0.018 -${TOMORROW}T05:00:00,0.017 -${TOMORROW}T06:00:00,0.0105 -${TOMORROW}T07:00:00,0.009 -${TOMORROW}T08:00:00,0.0095 -${TOMORROW}T09:00:00,0.009 -${TOMORROW}T10:00:00,0.0085 -${TOMORROW}T11:00:00,0.010 -${TOMORROW}T12:00:00,0.008 -${TOMORROW}T13:00:00,0.005 -${TOMORROW}T14:00:00,0.004 -${TOMORROW}T15:00:00,0.004 -${TOMORROW}T16:00:00,0.0055 -${TOMORROW}T17:00:00,0.008 -${TOMORROW}T18:00:00,0.012 -${TOMORROW}T19:00:00,0.013 -${TOMORROW}T20:00:00,0.014 -${TOMORROW}T21:00:00,0.0125 -${TOMORROW}T22:00:00,0.010 -${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv +${TOMORROW}T00:00:00,10 +${TOMORROW}T01:00:00,11 +${TOMORROW}T02:00:00,12 +${TOMORROW}T03:00:00,15 +${TOMORROW}T04:00:00,18 +${TOMORROW}T05:00:00,17 +${TOMORROW}T06:00:00,10.5 +${TOMORROW}T07:00:00,9 +${TOMORROW}T08:00:00,9.5 +${TOMORROW}T09:00:00,9 +${TOMORROW}T10:00:00,8.5 +${TOMORROW}T11:00:00,10 +${TOMORROW}T12:00:00,8 +${TOMORROW}T13:00:00,5 +${TOMORROW}T14:00:00,4 +${TOMORROW}T15:00:00,4 +${TOMORROW}T16:00:00,5.5 +${TOMORROW}T17:00:00,8 +${TOMORROW}T18:00:00,12 +${TOMORROW}T19:00:00,13 +${TOMORROW}T20:00:00,14 +${TOMORROW}T21:00:00,12.5 +${TOMORROW}T22:00:00,10 +${TOMORROW}T23:00:00,7" > prices-tomorrow.csv docker cp prices-tomorrow.csv $CONTAINER_NAME:/app docker exec -it $CONTAINER_NAME flexmeasures add beliefs \ - --sensor 1 --source toy-user /app/prices-tomorrow.csv --timezone Europe/Amsterdam + --sensor 1 --source toy-user /app/prices-tomorrow.csv --timezone Europe/Amsterdam --unit EUR/MWh echo "[TUTORIAL-RUNNER] creating schedule ..." docker exec -it $CONTAINER_NAME flexmeasures add schedule \ diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 82a6964e66..df7fefd745 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -20,7 +20,7 @@ Below are the ``flexmeasures`` CLI commands we'll run, and which we'll explain s # setup an account with a user, assets for battery & solar and an energy market (ID 1) $ flexmeasures add toy-account # load prices to optimize schedules against - $ flexmeasures add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam + $ flexmeasures add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam --unit EUR/MWh Okay, let's get started! @@ -313,42 +313,42 @@ And on the flex-model of the battery can be seen on its properties page (and is Add some price data --------------------------------------- -Now to add price data. First, we'll create the CSV file with prices (EUR/kWh, see the setup for sensor 1 above) for tomorrow. +Now to add price data. First, we'll create the CSV file with prices in EUR/MWh for tomorrow. The price sensor stores data in EUR/kWh, and we'll ask FlexMeasures to convert the CSV values while loading them. .. code-block:: bash $ TOMORROW=$(date --date="next day" '+%Y-%m-%d') $ echo "Hour,Price - $ ${TOMORROW}T00:00:00,0.010 - $ ${TOMORROW}T01:00:00,0.011 - $ ${TOMORROW}T02:00:00,0.012 - $ ${TOMORROW}T03:00:00,0.015 - $ ${TOMORROW}T04:00:00,0.018 - $ ${TOMORROW}T05:00:00,0.017 - $ ${TOMORROW}T06:00:00,0.0105 - $ ${TOMORROW}T07:00:00,0.009 - $ ${TOMORROW}T08:00:00,0.0095 - $ ${TOMORROW}T09:00:00,0.009 - $ ${TOMORROW}T10:00:00,0.0085 - $ ${TOMORROW}T11:00:00,0.010 - $ ${TOMORROW}T12:00:00,0.008 - $ ${TOMORROW}T13:00:00,0.005 - $ ${TOMORROW}T14:00:00,0.004 - $ ${TOMORROW}T15:00:00,0.004 - $ ${TOMORROW}T16:00:00,0.0055 - $ ${TOMORROW}T17:00:00,0.008 - $ ${TOMORROW}T18:00:00,0.012 - $ ${TOMORROW}T19:00:00,0.013 - $ ${TOMORROW}T20:00:00,0.014 - $ ${TOMORROW}T21:00:00,0.0125 - $ ${TOMORROW}T22:00:00,0.010 - $ ${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv + $ ${TOMORROW}T00:00:00,10 + $ ${TOMORROW}T01:00:00,11 + $ ${TOMORROW}T02:00:00,12 + $ ${TOMORROW}T03:00:00,15 + $ ${TOMORROW}T04:00:00,18 + $ ${TOMORROW}T05:00:00,17 + $ ${TOMORROW}T06:00:00,10.5 + $ ${TOMORROW}T07:00:00,9 + $ ${TOMORROW}T08:00:00,9.5 + $ ${TOMORROW}T09:00:00,9 + $ ${TOMORROW}T10:00:00,8.5 + $ ${TOMORROW}T11:00:00,10 + $ ${TOMORROW}T12:00:00,8 + $ ${TOMORROW}T13:00:00,5 + $ ${TOMORROW}T14:00:00,4 + $ ${TOMORROW}T15:00:00,4 + $ ${TOMORROW}T16:00:00,5.5 + $ ${TOMORROW}T17:00:00,8 + $ ${TOMORROW}T18:00:00,12 + $ ${TOMORROW}T19:00:00,13 + $ ${TOMORROW}T20:00:00,14 + $ ${TOMORROW}T21:00:00,12.5 + $ ${TOMORROW}T22:00:00,10 + $ ${TOMORROW}T23:00:00,7" > prices-tomorrow.csv This is time series data, in FlexMeasures we call *"beliefs"*. Beliefs can also be sent to FlexMeasures via API or imported from open data hubs like `ENTSO-E `_ or `Weather Forecast APIs `_. However, in this tutorial we'll show how you can read data in from a CSV file. Sometimes that's just what you need :) .. code-block:: bash - $ flexmeasures add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam + $ flexmeasures add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam --unit EUR/MWh Successfully created beliefs In FlexMeasures, all beliefs have a data source. Here, we use the username of the user we created earlier. We could also pass a user ID, or the name of a new data source we want to use for CLI scripts. @@ -395,4 +395,3 @@ Again, we can also view these prices in the `FlexMeasures UI Date: Fri, 5 Jun 2026 13:26:16 +0100 Subject: [PATCH 09/21] ci: import toy prices as EUR per MWh Signed-off-by: Mohamed Belhsan Hmida --- .github/workflows/build.yml | 2 +- .github/workflows/generate-dummy-price.sh | 48 +++++++++++------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f9480db55..bdff6d25cb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: - name: Add beliefs run: | docker exec --env-file .env fm-container flexmeasures \ - add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam + add beliefs --sensor 1 --source toy-user prices-tomorrow.csv --timezone Europe/Amsterdam --unit EUR/MWh - name: Export TOMORROW run: echo "TOMORROW=$(date --date="next day" '+%Y-%m-%d')" >> $GITHUB_ENV diff --git a/.github/workflows/generate-dummy-price.sh b/.github/workflows/generate-dummy-price.sh index 6b674649ab..06ee670454 100755 --- a/.github/workflows/generate-dummy-price.sh +++ b/.github/workflows/generate-dummy-price.sh @@ -6,27 +6,27 @@ set -x TOMORROW=$(date --date="next day" '+%Y-%m-%d') echo "Hour,Price -${TOMORROW}T00:00:00,0.010 -${TOMORROW}T01:00:00,0.011 -${TOMORROW}T02:00:00,0.012 -${TOMORROW}T03:00:00,0.015 -${TOMORROW}T04:00:00,0.018 -${TOMORROW}T05:00:00,0.017 -${TOMORROW}T06:00:00,0.0105 -${TOMORROW}T07:00:00,0.009 -${TOMORROW}T08:00:00,0.0095 -${TOMORROW}T09:00:00,0.009 -${TOMORROW}T10:00:00,0.0085 -${TOMORROW}T11:00:00,0.010 -${TOMORROW}T12:00:00,0.008 -${TOMORROW}T13:00:00,0.005 -${TOMORROW}T14:00:00,0.004 -${TOMORROW}T15:00:00,0.004 -${TOMORROW}T16:00:00,0.0055 -${TOMORROW}T17:00:00,0.008 -${TOMORROW}T18:00:00,0.012 -${TOMORROW}T19:00:00,0.013 -${TOMORROW}T20:00:00,0.014 -${TOMORROW}T21:00:00,0.0125 -${TOMORROW}T22:00:00,0.010 -${TOMORROW}T23:00:00,0.007" > prices-tomorrow.csv +${TOMORROW}T00:00:00,10 +${TOMORROW}T01:00:00,11 +${TOMORROW}T02:00:00,12 +${TOMORROW}T03:00:00,15 +${TOMORROW}T04:00:00,18 +${TOMORROW}T05:00:00,17 +${TOMORROW}T06:00:00,10.5 +${TOMORROW}T07:00:00,9 +${TOMORROW}T08:00:00,9.5 +${TOMORROW}T09:00:00,9 +${TOMORROW}T10:00:00,8.5 +${TOMORROW}T11:00:00,10 +${TOMORROW}T12:00:00,8 +${TOMORROW}T13:00:00,5 +${TOMORROW}T14:00:00,4 +${TOMORROW}T15:00:00,4 +${TOMORROW}T16:00:00,5.5 +${TOMORROW}T17:00:00,8 +${TOMORROW}T18:00:00,12 +${TOMORROW}T19:00:00,13 +${TOMORROW}T20:00:00,14 +${TOMORROW}T21:00:00,12.5 +${TOMORROW}T22:00:00,10 +${TOMORROW}T23:00:00,7" > prices-tomorrow.csv From d3c2d82adeeaad2df8e31092aca1205679415948 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Fri, 5 Jun 2026 13:27:21 +0100 Subject: [PATCH 10/21] docs/tests: keep toy site capacity in kVA Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-setup.rst | 4 ++-- flexmeasures/cli/data_add.py | 2 +- flexmeasures/cli/tests/test_data_add_fresh_db.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index df7fefd745..774d5db3ca 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -213,7 +213,7 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI Flex-Context Flex-Model -------------------------------- ------------ - site-power-capacity: 500 kW + site-power-capacity: 500 kVA consumption-price: {'sensor': 1} ==================================== @@ -230,7 +230,7 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI You can see that this building asset has some meta information about how FlexMeasures needs to schedule: - Within :ref:`flex_context`, we noted where to find the relevant optimization signal for electricity consumption (Sensor 1, which stores day-ahead prices). -- Also, the building has a grid connection capacity of 500 kW, meaning that the total power flowing into or out of the building cannot exceed this value. +- Also, the building has a grid connection capacity of 500 kVA, meaning that the total power flowing into or out of the building cannot exceed this physical limit. Now let's look at the battery asset, as well: diff --git a/flexmeasures/cli/data_add.py b/flexmeasures/cli/data_add.py index fd36967d20..b4e0508e7f 100755 --- a/flexmeasures/cli/data_add.py +++ b/flexmeasures/cli/data_add.py @@ -1797,7 +1797,7 @@ def create_asset_with_one_sensor( latitude=location[0], longitude=location[1], flex_context={ - "site-power-capacity": "500 kW", + "site-power-capacity": "500 kVA", "consumption-price": {"sensor": day_ahead_sensor.id}, }, ) diff --git a/flexmeasures/cli/tests/test_data_add_fresh_db.py b/flexmeasures/cli/tests/test_data_add_fresh_db.py index f55aa1009b..a3327f13e5 100644 --- a/flexmeasures/cli/tests/test_data_add_fresh_db.py +++ b/flexmeasures/cli/tests/test_data_add_fresh_db.py @@ -398,7 +398,7 @@ def test_add_toy_account_battery_uses_kw_scale_units(app, fresh_db): ).scalar_one() assert day_ahead_sensor.unit == "EUR/kWh" - assert building.flex_context["site-power-capacity"] == "500 kW" + assert building.flex_context["site-power-capacity"] == "500 kVA" assert battery.flex_model["power-capacity"] == "500 kW" assert battery.flex_model["soc-max"] == "450 kWh" assert battery.sensors[0].unit == "kW" From 1be8deafe68cea14dc6218a014a136a8b5b5a85a Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Fri, 5 Jun 2026 13:28:18 +0100 Subject: [PATCH 11/21] docs/tests: keep toy battery capacity in kVA Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-expanded.rst | 2 +- documentation/tut/toy-example-setup.rst | 4 ++-- flexmeasures/cli/data_add.py | 2 +- flexmeasures/cli/tests/test_data_add_fresh_db.py | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/documentation/tut/toy-example-expanded.rst b/documentation/tut/toy-example-expanded.rst index ade5c3fff5..e86be37f48 100644 --- a/documentation/tut/toy-example-expanded.rst +++ b/documentation/tut/toy-example-expanded.rst @@ -6,7 +6,7 @@ Toy example II: Adding solar production, and a limit on the grid connection ============================================================================ -So far we haven't taken into account any other devices that consume or produce electricity. The battery was free to use all available capacity (which was 500 kW, both its own maximum charge/discharge rate, and the maximum grid capacity). +So far we haven't taken into account any other devices that consume or produce electricity. The battery was free to use all available capacity (which was 500 kVA, both its own maximum charge/discharge rate, and the maximum grid capacity). What if other devices will be using some of that capacity? Our schedules need to reflect that, so we stay within given limits. diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 774d5db3ca..c47dc351a5 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -251,7 +251,7 @@ Now let's look at the battery asset, as well: Flex-Context Flex-Model -------------- ------------------------- - power-capacity: 500 kW + power-capacity: 500 kVA roundtrip-efficiency: 90% soc-max: 450 kWh @@ -272,7 +272,7 @@ Now let's look at the battery asset, as well: Yes, that is quite a large battery :) You can also see that the asset has some meta information about its scheduling. -- Within :ref:`flex_model`, we noted that the battery's power capacity is the same as the building's grid connection capacity (500 kW), meaning the battery can charge or discharge at full power without overloading the connection, but no other devices can (we will come back to this limitation). +- Within :ref:`flex_model`, we noted that the battery's power capacity is the same as the building's grid connection capacity (500 kVA), meaning the battery can charge or discharge at full power without overloading the connection, but no other devices can (we will come back to this limitation). - Also noted is the battery's roundtrip efficiency (90%) and maximum state of charge (450 kWh). .. note:: Obviously, you can use the ``flexmeasures`` command to create your own, custom account and assets. See :ref:`cli`. And to create, edit or read asset data via the API, see :ref:`v3_0`. diff --git a/flexmeasures/cli/data_add.py b/flexmeasures/cli/data_add.py index b4e0508e7f..f6f1e31c73 100755 --- a/flexmeasures/cli/data_add.py +++ b/flexmeasures/cli/data_add.py @@ -1811,7 +1811,7 @@ def create_asset_with_one_sensor( "discharging", parent_asset_id=building_asset.id, flex_model={ - "power-capacity": "500 kW", + "power-capacity": "500 kVA", "roundtrip-efficiency": "90%", "soc-max": "450 kWh", }, diff --git a/flexmeasures/cli/tests/test_data_add_fresh_db.py b/flexmeasures/cli/tests/test_data_add_fresh_db.py index a3327f13e5..ff9f72c38c 100644 --- a/flexmeasures/cli/tests/test_data_add_fresh_db.py +++ b/flexmeasures/cli/tests/test_data_add_fresh_db.py @@ -374,7 +374,7 @@ def test_add_account( assert result.exit_code == 1 -def test_add_toy_account_battery_uses_kw_scale_units(app, fresh_db): +def test_add_toy_account_battery_uses_kw_sensors_and_kva_capacities(app, fresh_db): from flexmeasures.cli.data_add import add_toy_account result = app.test_cli_runner().invoke(add_toy_account, ["--kind", "battery"]) @@ -399,7 +399,7 @@ def test_add_toy_account_battery_uses_kw_scale_units(app, fresh_db): assert day_ahead_sensor.unit == "EUR/kWh" assert building.flex_context["site-power-capacity"] == "500 kVA" - assert battery.flex_model["power-capacity"] == "500 kW" + assert battery.flex_model["power-capacity"] == "500 kVA" assert battery.flex_model["soc-max"] == "450 kWh" assert battery.sensors[0].unit == "kW" assert solar.sensors[0].unit == "kW" From d90941a2d1792e95fdb5226c76f3bf371f8c6869 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Fri, 5 Jun 2026 13:28:57 +0100 Subject: [PATCH 12/21] tests: explain toy schedule power bounds Signed-off-by: Mohamed Belhsan Hmida --- flexmeasures/cli/tests/test_data_add_fresh_db.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flexmeasures/cli/tests/test_data_add_fresh_db.py b/flexmeasures/cli/tests/test_data_add_fresh_db.py index ff9f72c38c..83cbbca947 100644 --- a/flexmeasures/cli/tests/test_data_add_fresh_db.py +++ b/flexmeasures/cli/tests/test_data_add_fresh_db.py @@ -520,6 +520,8 @@ def test_add_storage_schedule( max_power = schedule.event_value.abs().max() assert len(schedule) == 48 assert power_sensor.unit == "kW" + # The 700 kW quantity override is the highest cap used by this parametrized test. + # The lower bound catches accidentally storing MW-scale values on the kW sensor. assert max_power > 1 assert max_power <= 700 From c7717244890c54511422f8dcd103ed99141637d1 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Sun, 5 Jul 2026 15:14:09 +0100 Subject: [PATCH 13/21] docs: restore main whitespace after merge Signed-off-by: Mohamed Belhsan Hmida --- documentation/dev/connection-secrets.rst | 4 ++-- documentation/features/scheduling.rst | 4 ++-- documentation/host/deployment.rst | 6 +++--- documentation/plugin/introduction.rst | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/documentation/dev/connection-secrets.rst b/documentation/dev/connection-secrets.rst index 4fdbfb7500..7a15f4edab 100644 --- a/documentation/dev/connection-secrets.rst +++ b/documentation/dev/connection-secrets.rst @@ -10,10 +10,10 @@ key ID and timestamps. Developers normally do not need to read or modify this JSON structure directly. For implementation examples, token lifecycle strategies and manually seeding a -credential through the CLI, see :ref:`storing_connection_secrets`. +credential through the CLI, see :ref:`storing_connection_secrets`. The encrypted values are protected by -``FLEXMEASURES_SECRETS_ENCRYPTION_KEYS``, see :ref:`flexmeasures_secrets_encryption_keys` +``FLEXMEASURES_SECRETS_ENCRYPTION_KEYS``, see :ref:`flexmeasures_secrets_encryption_keys` This setting accepts arbitrary non-empty strings, which FlexMeasures derives into Fernet-compatible keys. Hosts must configure this keyring before secrets can be stored - FlexMeasures will print a warning if it is not set and hints how to initialize it. diff --git a/documentation/features/scheduling.rst b/documentation/features/scheduling.rst index 770f7b0129..6399d867af 100644 --- a/documentation/features/scheduling.rst +++ b/documentation/features/scheduling.rst @@ -386,8 +386,8 @@ Clicking the "Info" button will give you a lot more insights into the jobs' conf The RQ-dashboard: complete overview ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Internally, jobs are queued with the python-rq library. For this, a job dashboard is available, which -users with the ``admin`` role can access via the menu. This gives a complete overview over all jobs +Internally, jobs are queued with the python-rq library. For this, a job dashboard is available, which +users with the ``admin`` role can access via the menu. This gives a complete overview over all jobs running in FlexMeasures. You find your jobs via the queues, see screenshot below. diff --git a/documentation/host/deployment.rst b/documentation/host/deployment.rst index 31d0196a36..db4549cfd4 100644 --- a/documentation/host/deployment.rst +++ b/documentation/host/deployment.rst @@ -13,10 +13,10 @@ Cloud setup using containers ------------------------------ The recommended way to run FlexMeasures is indeed via docker containers. -There are cloud infrastructures offered by the big providers, but there are many others, too. -You could use a container orchestration platform like Kubernetes, but also simpler options like Ansible/Terraform are great. +There are cloud infrastructures offered by the big providers, but there are many others, too. +You could use a container orchestration platform like Kubernetes, but also simpler options like Ansible/Terraform are great. -We recommend that several FlexMeasures containers are run, with different main tasks. +We recommend that several FlexMeasures containers are run, with different main tasks. The obvious tasks would be to serve web & API requests and to perform heavier background tasks (like computing schedules and forecasting). The image below shows an example architecture, and you can see that this allows you to scale each task on its own (by adding more containers to its stack). diff --git a/documentation/plugin/introduction.rst b/documentation/plugin/introduction.rst index 5ef9575776..c453746981 100644 --- a/documentation/plugin/introduction.rst +++ b/documentation/plugin/introduction.rst @@ -34,3 +34,4 @@ It also includes a few Blueprint examples and best practices. Continue reading the :ref:`plugin_showcase`. Continue to see more possibilities to do :ref:`plugin_customization`. And if your plugin connects to a 3rd-party platform, read about support for :ref:`storing_connection_secrets`. + From fd829eb8226ee5e6876237393cc41857de77e19f Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 18:36:32 +0100 Subject: [PATCH 14/21] docs: refresh toy setup CLI examples Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-setup.rst | 78 +++++++++---------------- 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 1f1bbeab33..68a7e3233b 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -147,26 +147,6 @@ FlexMeasures offers a command to create a toy account with a battery and expose $ eval "$(flexmeasures add toy-account --kind battery --shell-vars | grep '^FM_TOY_')" - Generic asset type `solar` created successfully. - Generic asset type `wind` created successfully. - Generic asset type `one-way_evse` created successfully. - Generic asset type `two-way_evse` created successfully. - Generic asset type `battery` created successfully. - Generic asset type `building` created successfully. - Generic asset type `process` created successfully. - Creating account Toy Account ... - Toy account Toy Account with user toy-user@flexmeasures.io created successfully. You might want to run `flexmeasures show account --id 1` - Adding transmission zone type ... - Adding NL transmission zone ... - Created day-ahead prices - The sensor recording day-ahead prices is day-ahead prices (ID: 1). - Created - Created discharging - Created - Created production - The sensor recording battery discharging is discharging (ID: 2). - The sensor recording solar forecasts is production (ID: 3). - This sets variables such as ``FM_TOY_PRICE_SENSOR_ID``, ``FM_TOY_BATTERY_SENSOR_ID``, ``FM_TOY_SOLAR_SENSOR_ID`` and ``FM_TOY_BUILDING_ASSET_ID`` in your current shell. @@ -195,43 +175,43 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI ID Name Type Parent ID Location ---- ------------ -------- ----------- ----------------- - 2 toy-building building 2 (52.374, 4.88969) - 3 toy-battery battery 2 (52.374, 4.88969) - 4 toy-solar solar 2 (52.374, 4.88969) + 6 toy-battery battery 5 (52.374, 4.88969) + 5 toy-building building (52.374, 4.88969) + 7 toy-solar solar 5 (52.374, 4.88969) .. code-block:: bash :emphasize-lines: 9-10 - $ flexmeasures show asset --id 2 + $ flexmeasures show asset --id 5 - ========================= - Asset toy-building (ID: 2) - ========================= + ========================== + Asset toy-building (ID: 5) + ========================== - Type Location Sensors to show Attributes - -------- ----------------- ------------------- ------------ - building (52.374, 4.88969) Prices: [1] - Power flows: [3, 2] + Type Location Sensors to show Attributes External ID + -------- ----------------- ------------------------------------------------- ------------ ------------- + building (52.374, 4.88969) Prices: asset=5 (flex-context=consumption-price) + Power flows: [9, 8] Flex-Context Flex-Model -------------------------------- ------------ site-power-capacity: 500 kVA - consumption-price: {'sensor': 1} + consumption-price: {'sensor': 7} - ==================================== - Child assets of toy-building (ID: 2) - ==================================== + ===================================== + Child assets of toy-building (ID: 5) + ===================================== - ID Name Type - ------- ----------------- ---------------------------- - 3 toy-battery battery - 4 toy-solar solar + ID Name Type + ---- ----------- ------- + 7 toy-solar solar + 6 toy-battery battery No sensors in asset ... You can see that this building asset has some meta information about how FlexMeasures needs to schedule: -- Within :ref:`flex_context`, we noted where to find the relevant optimization signal for electricity consumption (Sensor 1, which stores day-ahead prices). +- Within :ref:`flex_context`, we noted where to find the relevant optimization signal for electricity consumption (Sensor 7, which stores day-ahead prices). - Also, the building has a grid connection capacity of 500 kVA, meaning that the total power flowing into or out of the building cannot exceed this physical limit. Now let's look at the battery asset, as well: @@ -239,17 +219,17 @@ Now let's look at the battery asset, as well: .. code-block:: bash :emphasize-lines: 10-12 - $ flexmeasures show asset --id 3 + $ flexmeasures show asset --id 6 =================================== - Asset toy-battery (ID: 3) - Child of asset toy-building (ID: 2) + Asset toy-battery (ID: 6) + Child of asset toy-building (ID: 5) =================================== - Type Location Sensors to show Attributes - ------- ----------------- ------------------- ------------ - battery (52.374, 4.88969) Prices: [1] - Power flows: [3, 2] + Type Location Sensors to show Attributes External ID + ------- ----------------- ------------------------------------------------- ------------ ------------- + battery (52.374, 4.88969) Prices: asset=5 (flex-context=consumption-price) + Power flows: [9, 8] Flex-Context Flex-Model -------------- ------------------------- @@ -258,7 +238,7 @@ Now let's look at the battery asset, as well: soc-max: 450 kWh ==================================== - Child assets of toy-battery (ID: 3) + Child assets of toy-battery (ID: 6) ==================================== No child assets ... @@ -267,7 +247,7 @@ Now let's look at the battery asset, as well: ID Name Unit Resolution Timezone Attributes ---- ----------- ------ ------------ ---------------- ------------ - 2 discharging kW 15 minutes Europe/Amsterdam + 8 discharging kW 15 minutes Europe/Amsterdam From e05bec3bb0c990d5c02077773c206f8c88886b25 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 18:37:17 +0100 Subject: [PATCH 15/21] docs: refresh toy setup price references Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-setup.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 68a7e3233b..a9f24769c9 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -343,7 +343,7 @@ Let's look at the price data we just loaded: $ flexmeasures show beliefs --sensor ${FM_TOY_PRICE_SENSOR_ID} --start ${TOMORROW}T00:00:00+01:00 --duration PT24H - Beliefs for Sensor 'day-ahead prices' (ID 1). + Beliefs for Sensor 'day-ahead prices' (ID 7). Data spans a day and starts at 2025-11-11 00:00:00+01:00. The time resolution (x-axis) is an hour. ┌────────────────────────────────────────────────────────────┐ @@ -370,7 +370,7 @@ Let's look at the price data we just loaded: -Again, we can also view these prices in the `FlexMeasures UI `_: +Again, we can also view these prices in the `FlexMeasures UI `_: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-prices.png :align: center From 7d5d8d73a4e1c8dee0bf448b1656d98cfe7f43dc Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 18:38:21 +0100 Subject: [PATCH 16/21] docs: refresh toy tutorial UI examples Signed-off-by: Mohamed Belhsan Hmida --- .../tut/scripts/run-tutorial4-in-docker.sh | 2 +- documentation/tut/toy-example-expanded.rst | 8 ++++---- documentation/tut/toy-example-from-scratch.rst | 6 +++--- .../tut/toy-example-multiasset-curtailment.rst | 4 ++-- documentation/tut/toy-example-process.rst | 17 ++++++++--------- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/documentation/tut/scripts/run-tutorial4-in-docker.sh b/documentation/tut/scripts/run-tutorial4-in-docker.sh index 22593e6618..c3eba206c6 100755 --- a/documentation/tut/scripts/run-tutorial4-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial4-in-docker.sh @@ -27,4 +27,4 @@ docker exec -it $CONTAINER_NAME flexmeasures add schedule --sensor ${FM_TOY_PROC --flex-context '{"consumption-price": {"sensor": '"${FM_TOY_PRICE_SENSOR_ID}"'}}' \ --flex-model '{"duration": "PT4H", "process-type": "SHIFTABLE", "power": 200, "time-restrictions": [{"start": "'"${TOMORROW}"'T15:00:00+02:00", "duration": "PT1H"}]}' -echo "Now visit http://localhost:5000/assets/6/graphs to see all three schedules." +echo "Now visit http://localhost:5000/assets/8/graphs to see all three schedules." diff --git a/documentation/tut/toy-example-expanded.rst b/documentation/tut/toy-example-expanded.rst index 19a39f366f..12be992014 100644 --- a/documentation/tut/toy-example-expanded.rst +++ b/documentation/tut/toy-example-expanded.rst @@ -69,7 +69,7 @@ Setting the data source type to "forecaster" helps FlexMeasures to visually dist $ flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 4 solar-tomorrow.csv --timezone Europe/Amsterdam Successfully created beliefs -The one-hour CSV data is automatically resampled to the 15-minute resolution of the sensor that is recording solar production. We can see solar production in the `FlexMeasures UI `_: +The one-hour CSV data is automatically resampled to the 15-minute resolution of the sensor that is recording solar production. We can see solar production in the `FlexMeasures UI `_: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-production.png :align: center @@ -158,13 +158,13 @@ This will have an effect on the available headroom for the battery, given the `` asyncio.run(client_script()) -We can see the updated scheduling in the `FlexMeasures UI `_: +We can see the updated scheduling in the `FlexMeasures UI `_: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-charging-with-solar.png :align: center | -The `graphs page for the battery `_ now shows the solar data, too: +The `graphs page for the battery `_ now shows the solar data, too: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/asset-view-with-solar.png :align: center @@ -190,7 +190,7 @@ In the case of the scheduler that we ran in the previous tutorial, which did not .. note:: You can add arbitrary sensors to a chart using the asset UI or the attribute ``sensors_to_show``. See :ref:`view_asset-data` for more. -A nice feature is that you can check the data connectivity status of your building asset. Now that we have made the schedule, both lamps are green. You can also view it in `FlexMeasures UI `_: +A nice feature is that you can check the data connectivity status of your building asset. Now that we have made the schedule, both lamps are green. You can also view it in `FlexMeasures UI `_: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/screenshot_building_status.png :align: center diff --git a/documentation/tut/toy-example-from-scratch.rst b/documentation/tut/toy-example-from-scratch.rst index 5d19eaeb92..d69ef68000 100644 --- a/documentation/tut/toy-example-from-scratch.rst +++ b/documentation/tut/toy-example-from-scratch.rst @@ -135,7 +135,7 @@ Great. Let's see what we made: .. code-block:: bash - Beliefs for Sensor 'discharging' (ID 2). + Beliefs for Sensor 'discharging' (ID 8). Data spans 12 hours and starts at 2025-11-29 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ @@ -163,7 +163,7 @@ Great. Let's see what we made: Here, negative values denote output from the grid, so that's when the battery gets charged. -We can also look at the charging schedule in the `FlexMeasures UI `_ (reachable via the asset page for the battery): +We can also look at the charging schedule in the `FlexMeasures UI `_ (reachable via the asset page for the battery): .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-charging.png :align: center @@ -173,7 +173,7 @@ Recall that we only asked for a 12 hour schedule here. We started our schedule * Our scheduler didn't have many opportunities to optimize, but it found some. This battery can fully charge in around an hour, and therefore, it runs two cycles. For instance, in the second cycle it buys at the lowest price (at 2pm) and sells it off at the highest price within the given 12 hours (at 6pm). -The `battery's graph dashboard `_ shows both prices and the schedule. +The `battery's graph dashboard `_ shows both prices and the schedule. .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/asset-view-without-solar.png :align: center diff --git a/documentation/tut/toy-example-multiasset-curtailment.rst b/documentation/tut/toy-example-multiasset-curtailment.rst index 49f710b41a..7b12f18d9b 100644 --- a/documentation/tut/toy-example-multiasset-curtailment.rst +++ b/documentation/tut/toy-example-multiasset-curtailment.rst @@ -133,7 +133,7 @@ Great. Let's see what we made: echo "[TUTORIAL-RUNNER] showing PV schedule ..." docker exec -it flexmeasures-server-1 flexmeasures show beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --start ${TOMORROW}T07:00:00+01:00 --duration PT12H - Beliefs for Sensor 'production' (ID 3). + Beliefs for Sensor 'production' (ID 9). Data spans 12 hours and starts at 2025-11-29 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ @@ -264,7 +264,7 @@ The PV inverter should then not need to curtail any production, as the battery c And the battery should get rid of this energy again when prices go up later in the day. -We can confirm this is the case on the updated scheduling in the `FlexMeasures UI `_: +We can confirm this is the case on the updated scheduling in the `FlexMeasures UI `_: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-multiasset-negativeprices.png :align: center diff --git a/documentation/tut/toy-example-process.rst b/documentation/tut/toy-example-process.rst index f5f9e82e19..b00cdad52e 100644 --- a/documentation/tut/toy-example-process.rst +++ b/documentation/tut/toy-example-process.rst @@ -37,14 +37,13 @@ Before moving forward, we'll add the `process` asset and three sensors to store $ eval "$(flexmeasures add toy-account --kind process --shell-vars | grep '^FM_TOY_')" User with email toy-user@flexmeasures.io already exists in account Docker Toy Account. - The sensor recording day-ahead prices is day-ahead prices (ID: 1). - Created - Created - Created - Created - The sensor recording the power of the inflexible load is Power (Inflexible) (ID: 4). - The sensor recording the power of the breakable load is Power (Breakable) (ID: 5). - The sensor recording the power of the shiftable load is Power (Shiftable) (ID: 6). + FM_TOY_ACCOUNT_ID=1 + FM_TOY_PRICE_SENSOR_ID=7 + FM_TOY_BUILDING_ASSET_ID=5 + FM_TOY_PROCESS_ASSET_ID=8 + FM_TOY_PROCESS_INFLEXIBLE_SENSOR_ID=10 + FM_TOY_PROCESS_BREAKABLE_SENSOR_ID=11 + FM_TOY_PROCESS_SHIFTABLE_SENSOR_ID=12 Trigger an updated schedule @@ -91,7 +90,7 @@ Results --------- The image below shows the resulting schedules following each of the three policies. -You will see similar results in your `FlexMeasures UI `_. +You will see similar results in your `FlexMeasures UI `_. .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/asset-view-process.png From 2a40d2a94d603d3ebbefd0370ccd5dc49c296395 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 18:39:32 +0100 Subject: [PATCH 17/21] docs: refresh toy tutorial API examples Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-expanded.rst | 4 ++-- .../tut/toy-example-from-scratch.rst | 6 ++--- .../toy-example-multiasset-curtailment.rst | 22 +++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/documentation/tut/toy-example-expanded.rst b/documentation/tut/toy-example-expanded.rst index 12be992014..de42969e15 100644 --- a/documentation/tut/toy-example-expanded.rst +++ b/documentation/tut/toy-example-expanded.rst @@ -102,7 +102,7 @@ This will have an effect on the available headroom for the battery, given the `` .. tab:: API - Example call: `[POST] http://localhost:5000/api/v3_0/sensors/2/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): + Example call: `[POST] http://localhost:5000/api/v3_0/sensors/8/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): .. code-block:: json :emphasize-lines: 8-10 @@ -141,7 +141,7 @@ This will have an effect on the available headroom for the battery, given the `` host="localhost:5000", ) schedule = await client.trigger_and_get_schedule( - sensor_id=2, # Battery power (sensor ID) + sensor_id=8, # Battery power (sensor ID) start=f"{(date.today() + timedelta(days=1)).isoformat()}T07:00+01:00", duration="PT12H", flex_model={ diff --git a/documentation/tut/toy-example-from-scratch.rst b/documentation/tut/toy-example-from-scratch.rst index d69ef68000..81b27e546e 100644 --- a/documentation/tut/toy-example-from-scratch.rst +++ b/documentation/tut/toy-example-from-scratch.rst @@ -75,7 +75,7 @@ There is more information being used by the scheduler, such as the battery's cap .. tab:: API - Example call: `[POST] http://localhost:5000/api/v3_0/sensors/3/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): + Example call: `[POST] http://localhost:5000/api/v3_0/sensors/8/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): .. code-block:: json @@ -83,7 +83,7 @@ There is more information being used by the scheduler, such as the battery's cap "start": "2025-11-11T07:00+01:00", "duration": "PT12H", "flex-model": [ - "sensor": 2, + "sensor": 8, "soc-at-start": "225kWh", "soc-min": "50 kWh" ] @@ -113,7 +113,7 @@ There is more information being used by the scheduler, such as the battery's cap host="localhost:5000", ) schedule = await client.trigger_and_get_schedule( - sensor_id=2, # battery discharging power sensor + sensor_id=8, # battery discharging power sensor start=f"{(date.today() + timedelta(days=1)).isoformat()}T07:00+01:00", duration="PT12H", flex_model={ diff --git a/documentation/tut/toy-example-multiasset-curtailment.rst b/documentation/tut/toy-example-multiasset-curtailment.rst index 7b12f18d9b..efda6f88ad 100644 --- a/documentation/tut/toy-example-multiasset-curtailment.rst +++ b/documentation/tut/toy-example-multiasset-curtailment.rst @@ -60,7 +60,7 @@ Also, we want to create a situation with negative prices, so curtailment makes s .. tab:: API - Example call: `[POST] http://localhost:5000/api/v3_0/sensors/3/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): + Example call: `[POST] http://localhost:5000/api/v3_0/sensors/9/schedules/trigger <../api/v3_0.html#post--api-v3_0-sensors-id-schedules-trigger>`_ (update the start date to tomorrow): .. code-block:: json :emphasize-lines: 14-18 @@ -71,7 +71,7 @@ Also, we want to create a situation with negative prices, so curtailment makes s "flex-model": [ { "consumption-capacity": "0 kW", - "production-capacity": {"sensor": 3, "source-types": ["forecaster"]}, + "production-capacity": {"sensor": 9, "source-types": ["forecaster"]}, } ], "flex-context": { @@ -103,13 +103,13 @@ Also, we want to create a situation with negative prices, so curtailment makes s async def client_script(): schedule = await client.trigger_and_get_schedule( - sensor_id=3, # PV production sensor + sensor_id=9, # PV production sensor start=f"{date.today().isoformat()}T07:00+01:00", duration="PT12H", flex_model=[ { "consumption-capacity": "0 kW", - "production-capacity": {"sensor": 3, "source-types": ["forecaster"]}, + "production-capacity": {"sensor": 9, "source-types": ["forecaster"]}, } ], flex_context={ @@ -189,7 +189,7 @@ Note that we are still passing in the flex-context with block price profiles her .. tab:: API - Example call: `[POST] http://localhost:5000/api/v3_0/assets/2/schedules/trigger <../api/v3_0.html#post--api-v3_0-assets-id-schedules-trigger>`_ (update the start date to tomorrow): + Example call: `[POST] http://localhost:5000/api/v3_0/assets/5/schedules/trigger <../api/v3_0.html#post--api-v3_0-assets-id-schedules-trigger>`_ (update the start date to tomorrow): .. code-block:: json @@ -198,12 +198,12 @@ Note that we are still passing in the flex-context with block price profiles her "duration": "PT12H", "flex-model": [ { - "sensor": 3, + "sensor": 9, "consumption-capacity": "0 kW", - "production-capacity": {"sensor": 3, "source-types": ["forecaster"]}, + "production-capacity": {"sensor": 9, "source-types": ["forecaster"]}, } { - "sensor": 2, + "sensor": 8, "soc-at-start": "225 kWh", "soc-min": "50 kWh" }, @@ -237,12 +237,12 @@ Note that we are still passing in the flex-context with block price profiles her duration="PT12H", flex_model=[ { - "sensor": 3, # solar production (sensor ID) + "sensor": 9, # solar production (sensor ID) "consumption-capacity": "0 kW", - "production-capacity": {"sensor": 3, "source-types": ["forecaster"]}, + "production-capacity": {"sensor": 9, "source-types": ["forecaster"]}, }, { - "sensor": 2, # battery power (sensor ID) + "sensor": 8, # battery power (sensor ID) "soc-at-start": "225 kWh", "soc-min": "50 kWh", }, From 3d31955819b39e67d890081ab6b18dd5f96f9c17 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 19:10:47 +0100 Subject: [PATCH 18/21] docs: align toy tutorial outputs Signed-off-by: Mohamed Belhsan Hmida --- .../tut/scripts/run-tutorial2-in-docker.sh | 2 +- .../tut/scripts/run-tutorial3-in-docker.sh | 4 +-- .../tut/scripts/run-tutorial5-in-docker.sh | 22 ++++++------ documentation/tut/toy-example-expanded.rst | 10 +++--- .../toy-example-multiasset-curtailment.rst | 4 +-- documentation/tut/toy-example-process.rst | 9 ----- documentation/tut/toy-example-reporter.rst | 36 +++++++++---------- documentation/tut/toy-example-setup.rst | 10 +++--- 8 files changed, 44 insertions(+), 53 deletions(-) diff --git a/documentation/tut/scripts/run-tutorial2-in-docker.sh b/documentation/tut/scripts/run-tutorial2-in-docker.sh index 014b7e2e92..a9fdf7c02c 100755 --- a/documentation/tut/scripts/run-tutorial2-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial2-in-docker.sh @@ -44,7 +44,7 @@ echo "[TUTORIAL-RUNNER] adding source ..." docker exec -it $CONTAINER_NAME flexmeasures add source --name "toy-forecaster" --type forecaster echo "[TUTORIAL-RUNNER] adding beliefs ..." -docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 4 /app/solar-tomorrow.csv --timezone Europe/Amsterdam +docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 2 /app/solar-tomorrow.csv --timezone Europe/Amsterdam echo "[TUTORIAL-RUNNER] showing beliefs ..." docker exec -it $CONTAINER_NAME flexmeasures show beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --start ${TOMORROW}T07:00:00+01:00 --duration PT12H diff --git a/documentation/tut/scripts/run-tutorial3-in-docker.sh b/documentation/tut/scripts/run-tutorial3-in-docker.sh index 5ece6f83fd..e0b319a026 100755 --- a/documentation/tut/scripts/run-tutorial3-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial3-in-docker.sh @@ -36,7 +36,7 @@ docker exec -it $CONTAINER_NAME flexmeasures show beliefs --sensor ${FM_TOY_SOLA echo "[TUTORIAL-RUNNER] Cleaning solar data for the next steps ..." # remove all previous beliefs on PV sensor so we don't have schedules mixed in the next run (issue 1807 can help with this, so selection by source works) docker exec -it $CONTAINER_NAME flexmeasures delete beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --force -docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 4 /app/solar-tomorrow.csv --timezone Europe/Amsterdam +docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 2 /app/solar-tomorrow.csv --timezone Europe/Amsterdam echo "[TUTORIAL-RUNNER] Now running both battery and PV together, still using block price profiles ..." docker exec -it $CONTAINER_NAME flexmeasures add schedule --asset ${FM_TOY_BUILDING_ASSET_ID} \ @@ -48,7 +48,7 @@ echo "[TUTORIAL-RUNNER] showing PV and battery schedule ..." docker exec -it $CONTAINER_NAME flexmeasures show beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --sensor ${FM_TOY_BATTERY_SENSOR_ID} --start ${TOMORROW}T07:00:00+01:00 --duration PT12H docker exec -it $CONTAINER_NAME flexmeasures delete beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --force -docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 4 /app/solar-tomorrow.csv --timezone Europe/Amsterdam +docker exec -it $CONTAINER_NAME flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 2 /app/solar-tomorrow.csv --timezone Europe/Amsterdam echo "[TUTORIAL-RUNNER] Now running both battery and PV together, with realistic DA prices and larger battery ..." docker exec -it $CONTAINER_NAME flexmeasures add schedule --asset ${FM_TOY_BUILDING_ASSET_ID} \ diff --git a/documentation/tut/scripts/run-tutorial5-in-docker.sh b/documentation/tut/scripts/run-tutorial5-in-docker.sh index 6f34c0ff13..5b43090a4d 100755 --- a/documentation/tut/scripts/run-tutorial5-in-docker.sh +++ b/documentation/tut/scripts/run-tutorial5-in-docker.sh @@ -17,7 +17,7 @@ echo "[TUTORIAL-RUNNER] Setting up toy account with reporters..." echo "[TUTORIAL-RUNNER] Show grid connection capacity ..." docker exec -it $CONTAINER_NAME flexmeasures show beliefs --sensor ${FM_TOY_GRID_CAPACITY_SENSOR_ID} --start ${TOMORROW}T00:00:00+02:00 --duration PT24H --resolution PT1H -docker exec -it $CONTAINER_NAME flexmeasures show data-sources --show-attributes --id 6 +docker exec -it $CONTAINER_NAME flexmeasures show data-sources --show-attributes --id 3 echo "[TUTORIAL-RUNNER] Configure headroom reporter ..." @@ -33,7 +33,7 @@ docker cp headroom-config.json $CONTAINER_NAME:/app echo " { 'input': [{'name': 'grid connection capacity', 'sensor': ${FM_TOY_GRID_CAPACITY_SENSOR_ID}}, - {'name': 'PV', 'sensor': ${FM_TOY_SOLAR_SENSOR_ID}, 'sources': [4]}], + {'name': 'PV', 'sensor': ${FM_TOY_SOLAR_SENSOR_ID}, 'sources': [2]}], 'output': [{'sensor': ${FM_TOY_HEADROOM_SENSOR_ID}}] }" > headroom-parameters.json docker cp headroom-parameters.json $CONTAINER_NAME:/app @@ -56,17 +56,17 @@ echo "[TUTORIAL-RUNNER] now the inflexible process ..." echo " { 'input': [{'sensor': ${FM_TOY_PROCESS_INFLEXIBLE_SENSOR_ID}}], - 'output': [{'sensor': 9}] + 'output': [{'sensor': 15}] }" > inflexible-parameters.json docker cp inflexible-parameters.json $CONTAINER_NAME:/app -docker exec -it $CONTAINER_NAME flexmeasures add report --source 6 \ +docker exec -it $CONTAINER_NAME flexmeasures add report --source 3 \ --parameters inflexible-parameters.json \ --start-offset DB,1D --end-offset DB,2D echo "[TUTORIAL-RUNNER] showing reported data ..." -docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 9 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" +docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 15 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" echo "[TUTORIAL-RUNNER] now the breakable process ..." @@ -74,17 +74,17 @@ echo "[TUTORIAL-RUNNER] now the breakable process ..." echo " { 'input': [{'sensor': ${FM_TOY_PROCESS_BREAKABLE_SENSOR_ID}}], - 'output': [{'sensor': 10}] + 'output': [{'sensor': 16}] }" > breakable-parameters.json docker cp breakable-parameters.json $CONTAINER_NAME:/app -docker exec -it $CONTAINER_NAME flexmeasures add report --source 6 \ +docker exec -it $CONTAINER_NAME flexmeasures add report --source 3 \ --parameters breakable-parameters.json \ --start-offset DB,1D --end-offset DB,2D echo "[TUTORIAL-RUNNER] showing reported data ..." -docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 10 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" +docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 16 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" @@ -93,14 +93,14 @@ echo "[TUTORIAL-RUNNER] now the breakable process ..." echo " { 'input' : [{'sensor': ${FM_TOY_PROCESS_SHIFTABLE_SENSOR_ID}}], - 'output' : [{'sensor': 11}] + 'output' : [{'sensor': 17}] }" > shiftable-parameters.json docker cp shiftable-parameters.json $CONTAINER_NAME:/app -docker exec -it $CONTAINER_NAME flexmeasures add report --source 6 \ +docker exec -it $CONTAINER_NAME flexmeasures add report --source 3 \ --parameters shiftable-parameters.json \ --start-offset DB,1D --end-offset DB,2D echo "[TUTORIAL-RUNNER] showing reported data ..." -docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 11 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" +docker exec -it $CONTAINER_NAME bash -c "flexmeasures show beliefs --sensor 17 --start ${TOMORROW}T00:00:00+01:00 --duration PT24H" diff --git a/documentation/tut/toy-example-expanded.rst b/documentation/tut/toy-example-expanded.rst index de42969e15..1e0d9abb0e 100644 --- a/documentation/tut/toy-example-expanded.rst +++ b/documentation/tut/toy-example-expanded.rst @@ -24,7 +24,7 @@ How does it work? Adding PV production forecasts ------------------------------ -First, we'll create a new CSV file with solar forecasts (kW, see the setup for sensor 3 in part I of this tutorial) for tomorrow. +First, we'll create a new CSV file with solar forecasts (kW, see the setup for sensor 9 in part I of this tutorial) for tomorrow. .. code-block:: bash @@ -65,8 +65,8 @@ Setting the data source type to "forecaster" helps FlexMeasures to visually dist .. code-block:: bash $ flexmeasures add source --name "toy-forecaster" --type forecaster - Added source - $ flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 4 solar-tomorrow.csv --timezone Europe/Amsterdam + Added source + $ flexmeasures add beliefs --sensor ${FM_TOY_SOLAR_SENSOR_ID} --source 2 solar-tomorrow.csv --timezone Europe/Amsterdam Successfully created beliefs The one-hour CSV data is automatically resampled to the 15-minute resolution of the sensor that is recording solar production. We can see solar production in the `FlexMeasures UI `_: @@ -96,7 +96,7 @@ This will have an effect on the available headroom for the battery, given the `` --start ${TOMORROW}T07:00+01:00 \ --duration PT12H \ --soc-at-start 50% \ - --flex-context '{"inflexible-device-sensors": [3]}' + --flex-context '{"inflexible-device-sensors": [9]}' --flex-model '{"soc-min": "50 kWh"}' \ New schedule is stored. @@ -149,7 +149,7 @@ This will have an effect on the available headroom for the battery, given the `` "soc-min": "50 kWh", }, flex_context={ - "inflexible-device-sensors": [3], # solar production (sensor ID) + "inflexible-device-sensors": [9], # solar production (sensor ID) }, ) print(schedule) diff --git a/documentation/tut/toy-example-multiasset-curtailment.rst b/documentation/tut/toy-example-multiasset-curtailment.rst index efda6f88ad..7da82ee0cb 100644 --- a/documentation/tut/toy-example-multiasset-curtailment.rst +++ b/documentation/tut/toy-example-multiasset-curtailment.rst @@ -276,7 +276,7 @@ And here is the CLI version: .. code-block:: bash - Beliefs for Sensors production (ID 3) and discharging (ID 2). + Beliefs for Sensors production (ID 9) and discharging (ID 8). Data spans 12 hours and starts at 2025-11-29 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. ┌────────────────────────────────────────────────────────────┐ @@ -317,7 +317,7 @@ We see the battery cycling twice, as before, but now it also soaks up solar prod .. code-block:: bash - Beliefs for Sensors production (ID 3) and discharging (ID 2). + Beliefs for Sensors production (ID 9) and discharging (ID 8). Data spans 12 hours and starts at 2025-11-19 07:00:00+01:00. The time resolution (x-axis) is 15 minutes. diff --git a/documentation/tut/toy-example-process.rst b/documentation/tut/toy-example-process.rst index b00cdad52e..b77ece39a7 100644 --- a/documentation/tut/toy-example-process.rst +++ b/documentation/tut/toy-example-process.rst @@ -35,15 +35,6 @@ Before moving forward, we'll add the `process` asset and three sensors to store .. code-block:: bash $ eval "$(flexmeasures add toy-account --kind process --shell-vars | grep '^FM_TOY_')" - - User with email toy-user@flexmeasures.io already exists in account Docker Toy Account. - FM_TOY_ACCOUNT_ID=1 - FM_TOY_PRICE_SENSOR_ID=7 - FM_TOY_BUILDING_ASSET_ID=5 - FM_TOY_PROCESS_ASSET_ID=8 - FM_TOY_PROCESS_INFLEXIBLE_SENSOR_ID=10 - FM_TOY_PROCESS_BREAKABLE_SENSOR_ID=11 - FM_TOY_PROCESS_SHIFTABLE_SENSOR_ID=12 Trigger an updated schedule diff --git a/documentation/tut/toy-example-reporter.rst b/documentation/tut/toy-example-reporter.rst index 58f87bb3f9..e2cc105087 100644 --- a/documentation/tut/toy-example-reporter.rst +++ b/documentation/tut/toy-example-reporter.rst @@ -47,7 +47,7 @@ Run the command below to show the values for our newly-created `grid connection $ TOMORROW=$(date --date="next day" '+%Y-%m-%d') $ flexmeasures show beliefs --sensor ${FM_TOY_GRID_CAPACITY_SENSOR_ID} --start ${TOMORROW}T00:00:00+02:00 --duration PT24H --resolution PT1H - Beliefs for Sensor 'grid connection capacity' (ID 7). + Beliefs for Sensor 'grid connection capacity' (ID 13). Data spans a day and starts at 2025-06-13 00:00:00+02:00. The time resolution (x-axis) is an hour. ┌────────────────────────────────────────────────────────────┐ @@ -73,23 +73,23 @@ Run the command below to show the values for our newly-created `grid connection ██ grid connection capacity (toy-building) -Moreover, we can check the freshly created source ``, which defines the `ProfitOrLossReporter` with the required configuration. +Moreover, we can check the freshly created source ``, which defines the `ProfitOrLossReporter` with the required configuration. You'll notice that the `config` is under the `data_generator` field. That's because reporters belong to a bigger category of classes that also contains the `Schedulers` and `Forecasters`. .. code-block:: bash - $ flexmeasures show data-sources --show-attributes --id 6 + $ flexmeasures show data-sources --show-attributes --id 3 type: reporter ======== ID Name User ID Model Version Attributes ---- ------------ --------- -------------------- --------- ------------------------------------------ - 6 FlexMeasures ProfitOrLossReporter { + 3 FlexMeasures ProfitOrLossReporter { "data_generator": { "config": { - "consumption_price_sensor": 1, + "consumption_price_sensor": 7, "loss_is_positive": true } } @@ -120,13 +120,13 @@ In practice, we need to create the `config` and `parameters`: $ echo " $ { $ 'input': [{'name': 'grid connection capacity', 'sensor': ${FM_TOY_GRID_CAPACITY_SENSOR_ID}}, - $ {'name': 'PV', 'sensor': ${FM_TOY_SOLAR_SENSOR_ID}, 'sources': [4]}], + $ {'name': 'PV', 'sensor': ${FM_TOY_SOLAR_SENSOR_ID}, 'sources': [2]}], $ 'output': [{'sensor': ${FM_TOY_HEADROOM_SENSOR_ID}}] $ }" > headroom-parameters.json -The output sensor (ID: 8) is actually the one created just to store that information - the headroom our battery has when considering solar production. +The output sensor (ID: 14) is actually the one created just to store that information - the headroom our battery has when considering solar production. -We limit the PV input to data with source 4, which is the one created when we added the toy account and is associated with our solar forecasts. This way, we ensure that only the forecasted values are used in the report, and not (also) schedules from the 3rd tutorial. +We limit the PV input to data with source 2, which is the one created for our solar forecasts. This way, we ensure that only the forecasted values are used in the report, and not (also) schedules from the 3rd tutorial. Finally, we can create the report with the following command: @@ -137,7 +137,7 @@ Finally, we can create the report with the following command: --start-offset DB,1D --end-offset DB,2D \ --resolution PT15M -Now we can visualize the diminished headroom in the following `link `_, which should resemble the following image: +Now we can visualize the diminished headroom in the following `link `_, which should resemble the following image: .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-headroom.png :align: center @@ -180,19 +180,19 @@ Define parameters in a JSON file: $ echo " $ { $ 'input': [{'sensor': ${FM_TOY_PROCESS_INFLEXIBLE_SENSOR_ID}}], - $ 'output': [{'sensor': 9}] + $ 'output': [{'sensor': 15}] $ }" > inflexible-parameters.json Create report: .. code-block:: bash - $ flexmeasures add report --source 6 \ + $ flexmeasures add report --source 3 \ --parameters inflexible-parameters.json \ --start-offset DB,1D --end-offset DB,2D -Check the results `here `_. The image should be similar to the one below. +Check the results `here `_. The image should be similar to the one below. .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-inflexible.png :align: center @@ -208,18 +208,18 @@ Define parameters in a JSON file: $ echo " $ { $ 'input': [{'sensor': ${FM_TOY_PROCESS_BREAKABLE_SENSOR_ID}}], - $ 'output': [{'sensor': 10}] + $ 'output': [{'sensor': 16}] $ }" > breakable-parameters.json Create report: .. code-block:: bash - $ flexmeasures add report --source 6 \ + $ flexmeasures add report --source 3 \ --parameters breakable-parameters.json \ --start-offset DB,1D --end-offset DB,2D -Check the results `here `_. The image should be similar to the one below. +Check the results `here `_. The image should be similar to the one below. .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-breakable.png @@ -236,18 +236,18 @@ Define parameters in a JSON file: $ echo " $ { $ 'input': [{'sensor': ${FM_TOY_PROCESS_SHIFTABLE_SENSOR_ID}}], - $ 'output': [{'sensor': 11}] + $ 'output': [{'sensor': 17}] $ }" > shiftable-parameters.json Create report: .. code-block:: bash - $ flexmeasures add report --source 6 \ + $ flexmeasures add report --source 3 \ --parameters shiftable-parameters.json \ --start-offset DB,1D --end-offset DB,2D -Check the results `here `_. The image should be similar to the one below. +Check the results `here `_. The image should be similar to the one below. .. image:: https://github.com/FlexMeasures/screenshots/raw/main/tut/toy-schedule/sensor-data-shiftable.png diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index a9f24769c9..8f97c9a02f 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -195,8 +195,8 @@ If you want, you can inspect what you created in the CLI (we'll also show the UI Flex-Context Flex-Model -------------------------------- ------------ - site-power-capacity: 500 kVA consumption-price: {'sensor': 7} + site-power-capacity: 500 kVA ===================================== Child assets of toy-building (ID: 5) @@ -226,16 +226,16 @@ Now let's look at the battery asset, as well: Child of asset toy-building (ID: 5) =================================== - Type Location Sensors to show Attributes External ID - ------- ----------------- ------------------------------------------------- ------------ ------------- - battery (52.374, 4.88969) Prices: asset=5 (flex-context=consumption-price) + Type Location Sensors to show Attributes External ID + ------- ----------------- ------------------- ------------ ------------- + battery (52.374, 4.88969) Prices: 7 Power flows: [9, 8] Flex-Context Flex-Model -------------- ------------------------- + soc-max: 450 kWh power-capacity: 500 kVA roundtrip-efficiency: 90% - soc-max: 450 kWh ==================================== Child assets of toy-battery (ID: 6) From 4b49f524c0598733fa4168d1026008a4a9b2d811 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 20:47:52 +0100 Subject: [PATCH 19/21] docs: update toy-example-setup Signed-off-by: Mohamed Belhsan Hmida --- documentation/tut/toy-example-setup.rst | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 8f97c9a02f..659fbf81e1 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -335,7 +335,7 @@ This is time series data, in FlexMeasures we call *"beliefs"*. Beliefs can also In FlexMeasures, all beliefs have a data source. Here, we use the username of the user we created earlier. We could also pass a user ID, or the name of a new data source we want to use for CLI scripts. -.. note:: Attention: We created and imported prices where the times have no time zone component! That happens a lot. FlexMeasures can localize them for you to a given timezone. Here, we localized the data to the timezone of the price sensor - ``Europe/Amsterdam`` - so the start time for the first price is `2022-03-03 00:00:00+01:00` (midnight in Amsterdam). +.. note:: Attention: We created and imported prices where the times have no time zone component! That happens a lot. FlexMeasures can localize them for you to a given timezone. Here, we localized the data to the timezone of the price sensor - ``Europe/Amsterdam`` - so the start time for the first price is `2025-11-11 00:00:00+01:00` (midnight in Amsterdam). Let's look at the price data we just loaded: @@ -347,26 +347,26 @@ Let's look at the price data we just loaded: Data spans a day and starts at 2025-11-11 00:00:00+01:00. The time resolution (x-axis) is an hour. ┌────────────────────────────────────────────────────────────┐ - │ ▗▀▚▖ │ - │ ▗▘ ▝▖ │ - │ ▞ ▌ │ - │ ▟ ▐ │ 0.015EUR/kWh - │ ▗▘ ▝▖ ▗ │ - │ ▗▘ ▚ ▄▞▘▚▖ │ - │ ▞ ▐ ▄▀▘ ▝▄ │ - │ ▄▞ ▌ ▛ ▖ │ - │▀ ▚ ▐ ▝▖ │ - │ ▝▚ ▖ ▗▘ ▝▖ │ 0.010EUR/kWh - │ ▀▄▄▞▀▄▄ ▗▀▝▖ ▞ ▐ │ - │ ▀▀▜▘ ▝▚ ▗▘ ▚ │ - │ ▌ ▞ ▌│ - │ ▝▖ ▞ ▝│ - │ ▐ ▞ │ - │ ▚ ▗▞ │ 0.005EUR/kWh - │ ▀▚▄▄▄▄▘ │ + │ ▞▄ │ + │ ▐ ▀▌ │ + │ ▗▘ ▚ │ + │ ▌ ▐ │ 0.015EUR/kWh + │ ▞ ▌ ▗ │ + │ ▐ ▐ ▗▄▀▚▖ │ + │ ▗▘ ▝▖ ▗▞▘ ▝▄ │ + │ ▄▀ ▌ ▐▘ ▝▖ │ + │ ▄▀ ▐ ▗▘ ▝▖ │ + │▞▀ ▝▚ ▄ ▞ ▝▖ │ 0.010EUR/kWh + │ ▀▄▄▀▀▄▄ ▞ ▚ ▗▘ ▐ │ + │ ▀▀▀ ▚▖ ▞ ▚ │ + │ ▚ ▗▘ ▝▖│ + │ ▚ ▗▘ ▝│ + │ ▝▖ ▗▘ │ + │ ▐▄ ▄▘ │ 0.005EUR/kWh + │ ▀▄▄▄▞ │ └────────────────────────────────────────────────────────────┘ - 5 10 15 20 - ██ day-ahead prices + 06:00 12:00 18:00 + ██ day-ahead prices (NL transmission zone) From 55efe9fe4bdfd1fe0de0330098b11ffd76fd1e5d Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 20:53:50 +0100 Subject: [PATCH 20/21] docs: restore toy account setup output --- documentation/tut/toy-example-setup.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index 659fbf81e1..c21c538e10 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -147,6 +147,25 @@ FlexMeasures offers a command to create a toy account with a battery and expose $ eval "$(flexmeasures add toy-account --kind battery --shell-vars | grep '^FM_TOY_')" + Generic asset type `solar` created successfully. + Generic asset type `wind` created successfully. + Generic asset type `one-way_evse` created successfully. + Generic asset type `two-way_evse` created successfully. + Generic asset type `battery` created successfully. + Generic asset type `building` created successfully. + Generic asset type `process` created successfully. + Creating account Toy Account ... + Toy account Toy Account with user toy-user@flexmeasures.io created successfully. You might want to run `flexmeasures show account --id 1` + Adding transmission zone type ... + Adding NL transmission zone ... + Created day-ahead prices + The sensor recording day-ahead prices is day-ahead prices (ID: 7). + Created + Created discharging + Created + Created production + The sensor recording battery discharging is discharging (ID: 8). + The sensor recording solar forecasts is production (ID: 9). This sets variables such as ``FM_TOY_PRICE_SENSOR_ID``, ``FM_TOY_BATTERY_SENSOR_ID``, ``FM_TOY_SOLAR_SENSOR_ID`` and ``FM_TOY_BUILDING_ASSET_ID`` in your current shell. From fd04d12592eac311d11b6914605757a5f0a549c7 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Tue, 14 Jul 2026 22:24:35 +0100 Subject: [PATCH 21/21] docs: align toy setup CLI output --- documentation/tut/toy-example-setup.rst | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/documentation/tut/toy-example-setup.rst b/documentation/tut/toy-example-setup.rst index c21c538e10..fa5b447eba 100644 --- a/documentation/tut/toy-example-setup.rst +++ b/documentation/tut/toy-example-setup.rst @@ -141,11 +141,11 @@ The data we need for our example is both structural (e.g. a company account, a u Let's create the structural data first. -FlexMeasures offers a command to create a toy account with a battery and expose the relevant IDs as shell variables: +FlexMeasures offers a command to create a toy account with a battery: .. code-block:: bash - $ eval "$(flexmeasures add toy-account --kind battery --shell-vars | grep '^FM_TOY_')" + $ flexmeasures add toy-account --kind battery Generic asset type `solar` created successfully. Generic asset type `wind` created successfully. @@ -154,20 +154,28 @@ FlexMeasures offers a command to create a toy account with a battery and expose Generic asset type `battery` created successfully. Generic asset type `building` created successfully. Generic asset type `process` created successfully. + Generic asset type `heat-storage` created successfully. Creating account Toy Account ... Toy account Toy Account with user toy-user@flexmeasures.io created successfully. You might want to run `flexmeasures show account --id 1` Adding transmission zone type ... Adding NL transmission zone ... - Created day-ahead prices + Created The sensor recording day-ahead prices is day-ahead prices (ID: 7). - Created - Created discharging - Created - Created production + Created + Created + Created + Created + Created The sensor recording battery discharging is discharging (ID: 8). The sensor recording solar forecasts is production (ID: 9). +Now expose the relevant IDs as shell variables for the commands below: + +.. code-block:: bash + + $ eval "$(flexmeasures add toy-account --kind battery --shell-vars | grep '^FM_TOY_')" + This sets variables such as ``FM_TOY_PRICE_SENSOR_ID``, ``FM_TOY_BATTERY_SENSOR_ID``, ``FM_TOY_SOLAR_SENSOR_ID`` and ``FM_TOY_BUILDING_ASSET_ID`` in your current shell. And with that, we're done with the structural data for this tutorial!