diff --git a/documentation/cli/change_log.rst b/documentation/cli/change_log.rst index 05304c6eb..92f2175bc 100644 --- a/documentation/cli/change_log.rst +++ b/documentation/cli/change_log.rst @@ -8,7 +8,7 @@ since v0.15.0 | July XX, 2023 ================================= * Allow deleting multiple sensors with a single call to ``flexmeasures delete sensor`` by passing the ``--id`` option multiple times. -* Add ``flexmeasures add schedule for-shiftable`` to create a new shiftable load schedule for a given power sensor. +* Add ``flexmeasures add schedule for-process`` to create a new process schedule for a given power sensor. since v0.14.1 | June XX, 2023 diff --git a/documentation/cli/commands.rst b/documentation/cli/commands.rst index 6ac8dcdc8..a050fb3e8 100644 --- a/documentation/cli/commands.rst +++ b/documentation/cli/commands.rst @@ -36,7 +36,7 @@ of which some are referred to in this documentation. ``flexmeasures add source`` Add a new data source. ``flexmeasures add forecasts`` Create forecasts. ``flexmeasures add schedule for-storage`` Create a charging schedule for a storage asset. -``flexmeasures add schedule for-shiftable`` Create a schedule for a shiftable load. +``flexmeasures add schedule for-process`` Create a schedule for a process asset. ``flexmeasures add holidays`` Add holiday annotations to accounts and/or assets. ``flexmeasures add annotation`` Add annotation to accounts, assets and/or sensors. ``flexmeasures add toy-account`` Create a toy account, for tutorials and trying things. diff --git a/flexmeasures/cli/data_add.py b/flexmeasures/cli/data_add.py index 74dbdda23..98d47c99e 100755 --- a/flexmeasures/cli/data_add.py +++ b/flexmeasures/cli/data_add.py @@ -1160,7 +1160,7 @@ def add_schedule_for_storage( click.secho("New schedule is stored.", **MsgStyle.SUCCESS) -@create_schedule.command("for-shiftable") +@create_schedule.command("for-process") @with_appcontext @click.option( "--sensor-id", @@ -1191,26 +1191,26 @@ def add_schedule_for_storage( help="Duration of schedule, after --start. Follow up with a duration in ISO 6801 format, e.g. PT1H (1 hour) or PT45M (45 minutes).", ) @click.option( - "--load-duration", - "load_duration", + "--process-duration", + "process_duration", type=DurationField(), required=True, - help="Duration of the load. Follow up with a duration in ISO 6801 format, e.g. PT1H (1 hour) or PT45M (45 minutes).", + help="Duration of the process. Follow up with a duration in ISO 6801 format, e.g. PT1H (1 hour) or PT45M (45 minutes).", ) @click.option( - "--load-type", - "load_type", + "--process-type", + "process_type", type=click.Choice(["INFLEXIBLE", "BREAKABLE", "SHIFTABLE"], case_sensitive=False), required=False, default="SHIFTABLE", - help="Load shift policy type: INFLEXIBLE, BREAKABLE or SHIFTABLE.", + help="Process schedule policy: INFLEXIBLE, BREAKABLE or SHIFTABLE.", ) @click.option( - "--load-power", - "load_power", + "--process-power", + "process_power", type=ur.Quantity, required=True, - help="Constant power of the load during the activation period, e.g. 4kW.", + help="Constant power of the process during the activation period, e.g. 4kW.", ) @click.option( "--forbid", @@ -1227,22 +1227,22 @@ def add_schedule_for_storage( help="Whether to queue a scheduling job instead of computing directly. " "To process the job, run a worker (on any computer, but configured to the same databases) to process the 'scheduling' queue. Defaults to False.", ) -def add_schedule_shiftable_load( +def add_schedule_process( power_sensor: Sensor, consumption_price_sensor: Sensor, start: datetime, duration: timedelta, - load_duration: timedelta, - load_type: str, - load_power: ur.Quantity, + process_duration: timedelta, + process_type: str, + process_power: ur.Quantity, forbid: List | None = None, as_job: bool = False, ): - """Create a new schedule for a shiftable asset. + """Create a new schedule for a process asset. Current limitations: - Only supports consumption blocks. - - Not taking into account grid constraints or other loads. + - Not taking into account grid constraints or other processes. """ if forbid is None: @@ -1258,7 +1258,7 @@ def add_schedule_shiftable_load( end = start + duration - load_power = convert_units(load_power.magnitude, load_power.units, "MW") # type: ignore + process_power = convert_units(process_power.magnitude, process_power.units, "MW") # type: ignore scheduling_kwargs = dict( start=start, @@ -1266,9 +1266,9 @@ def add_schedule_shiftable_load( belief_time=server_now(), resolution=power_sensor.event_resolution, flex_model={ - "duration": pd.Timedelta(load_duration).isoformat(), - "load-type": load_type, - "power": load_power, + "duration": pd.Timedelta(process_duration).isoformat(), + "process-type": process_type, + "power": process_power, "time-restrictions": [TimeIntervalSchema().dump(f) for f in forbid], }, flex_context={ diff --git a/flexmeasures/cli/tests/conftest.py b/flexmeasures/cli/tests/conftest.py index 1ac161344..83eea3d18 100644 --- a/flexmeasures/cli/tests/conftest.py +++ b/flexmeasures/cli/tests/conftest.py @@ -100,26 +100,26 @@ def reporter_config_raw(app, db, setup_dummy_data): @pytest.mark.skip_github @pytest.fixture(scope="module") -def shiftable_load_power_sensor(db, app): +def process_power_sensor(db, app): """ - Create an asset of type "LoadType" and a power sensor to hold the result of + Create an asset of type "ProcessType" and a power sensor to hold the result of the scheduler. """ - shiftable_load_asset_type = GenericAssetType(name="LoadType") + process_asset_type = GenericAssetType(name="process") - db.session.add(shiftable_load_asset_type) + db.session.add(process_asset_type) - shiftable_asset = GenericAsset( - name="Test Asset", generic_asset_type=shiftable_load_asset_type + processasset = GenericAsset( + name="Test Asset", generic_asset_type=process_asset_type ) - db.session.add(shiftable_asset) + db.session.add(processasset) power_sensor = Sensor( "power", - generic_asset=shiftable_asset, + generic_asset=processasset, event_resolution=timedelta(hours=1), unit="MW", ) diff --git a/flexmeasures/cli/tests/test_data_add.py b/flexmeasures/cli/tests/test_data_add.py index 9b14b0564..3e293854b 100644 --- a/flexmeasures/cli/tests/test_data_add.py +++ b/flexmeasures/cli/tests/test_data_add.py @@ -214,28 +214,26 @@ def test_add_reporter(app, db, setup_dummy_data, reporter_config_raw): @pytest.mark.skip_github -@pytest.mark.parametrize("load_type", [("INFLEXIBLE"), ("SHIFTABLE"), ("BREAKABLE")]) -def test_add_shiftable( - app, db, shiftable_load_power_sensor, add_market_prices, load_type -): +@pytest.mark.parametrize("process_type", [("INFLEXIBLE"), ("SHIFTABLE"), ("BREAKABLE")]) +def test_add_process(app, db, process_power_sensor, add_market_prices, process_type): """ Schedule a 4h of consumption block at a constant power of 400kW in a day using - the three shiftable policies: inflexible, shiftable and breakable. + the three process policies: inflexible, shiftable and breakable. """ - from flexmeasures.cli.data_add import add_schedule_shiftable_load + from flexmeasures.cli.data_add import add_schedule_process epex_da = Sensor.query.filter(Sensor.name == "epex_da").one_or_none() - shiftable_load_power_sensor_id = shiftable_load_power_sensor + process_power_sensor_id = process_power_sensor cli_input_params = { - "sensor-id": shiftable_load_power_sensor_id, + "sensor-id": process_power_sensor_id, "start": "2015-01-02T00:00:00+01:00", "duration": "PT24H", - "load-duration": "PT4H", - "load-power": "0.4MW", - "load-type": load_type, + "process-duration": "PT4H", + "process-power": "0.4MW", + "process-type": process_type, "consumption-price-sensor": epex_da.id, "forbid": '{"start" : "2015-01-02T00:00:00+01:00", "duration" : "PT2H"}', } @@ -244,15 +242,15 @@ def test_add_shiftable( runner = app.test_cli_runner() # call command - result = runner.invoke(add_schedule_shiftable_load, cli_input) + result = runner.invoke(add_schedule_process, cli_input) print(result) assert result.exit_code == 0 # run command without errors - shiftable_load_power_sensor = Sensor.query.get(shiftable_load_power_sensor_id) + process_power_sensor = Sensor.query.get(process_power_sensor_id) - schedule = shiftable_load_power_sensor.search_beliefs() + schedule = process_power_sensor.search_beliefs() # check if the schedule is not empty more detailed testing can be found - # in data/models/planning/tests/test_shiftable_loads.py. + # in data/models/planning/tests/test_processs.py. assert (schedule == -0.4).event_value.sum() == 4 diff --git a/flexmeasures/data/models/planning/tests/test_process.py b/flexmeasures/data/models/planning/tests/test_process.py index 3dea9021c..08a198963 100644 --- a/flexmeasures/data/models/planning/tests/test_process.py +++ b/flexmeasures/data/models/planning/tests/test_process.py @@ -16,7 +16,7 @@ "process_type, optimal_start", [("INFLEXIBLE", datetime(2015, 1, 2, 0)), ("SHIFTABLE", datetime(2015, 1, 2, 8))], ) -def test_shiftable_scheduler(add_battery_assets, process, process_type, optimal_start): +def test_processscheduler(add_battery_assets, process, process_type, optimal_start): """ Test scheduling a process of 4kW of power that last 4h using the ProcessScheduler without time restrictions. @@ -94,7 +94,7 @@ def test_duration_exceeds_planning_window( assert (schedule == 4).all() -def test_shiftable_scheduler_time_restrictions(add_battery_assets, process): +def test_processscheduler_time_restrictions(add_battery_assets, process): """ Test ProcessScheduler with a time restrictions consisting of a block of 2h starting at 8am. The resulting schedules avoid the 8am-10am period and schedules for a valid period.