diff --git a/src/d3a/d3a_core/live_events.py b/src/d3a/d3a_core/live_events.py index 881ba613b..59d4a6a66 100644 --- a/src/d3a/d3a_core/live_events.py +++ b/src/d3a/d3a_core/live_events.py @@ -4,6 +4,8 @@ from d3a.d3a_core.area_serializer import area_from_dict from d3a.d3a_core.exceptions import D3AException from d3a.models.area.event_dispatcher import DispatcherFactory +from d3a.models.strategy.market_maker_strategy import MarketMakerStrategy +from d3a.models.strategy.infinite_bus import InfiniteBusStrategy class LiveEventException(D3AException): @@ -48,11 +50,30 @@ def __init__(self, area_uuid, area_params): def apply(self, area): if area.uuid != self.area_uuid: return False - - area.area_reconfigure_event(**self.area_params) + self.sanitize_live_event_paramters_for_strategy_update() + area_type = self.area_params.pop("type", None) + if area_type is not None: + if area.strategy is None: + return False + if area_type == "MarketMaker": + area.strategy = MarketMakerStrategy(**self.area_params) + elif area_type == "InfiniteBus": + area.strategy = InfiniteBusStrategy(**self.area_params) + else: + return False + area.activate() + area.strategy.event_activate() + area.strategy.event_market_cycle() + else: + area.area_reconfigure_event(**self.area_params) return True + def sanitize_live_event_paramters_for_strategy_update(self): + self.area_params.pop('number_of_clones', None) + self.area_params.pop('energy_rate_profile_uuid', None) + self.area_params.pop('buying_rate_profile_uuid', None) + def __repr__(self): return f"" diff --git a/src/d3a/models/area/__init__.py b/src/d3a/models/area/__init__.py index f0aa6b405..ebb3c5b3d 100644 --- a/src/d3a/models/area/__init__.py +++ b/src/d3a/models/area/__init__.py @@ -137,7 +137,7 @@ def restore_state(self, saved_state): def area_reconfigure_event(self, **kwargs): if self.strategy is not None: self.strategy.area_reconfigure_event(**kwargs) - return + return True grid_fee_constant = kwargs["grid_fee_constant"] \ if key_in_dict_and_not_none(kwargs, 'grid_fee_constant') \ diff --git a/src/d3a/models/strategy/market_maker_strategy.py b/src/d3a/models/strategy/market_maker_strategy.py index 914289f17..027800013 100644 --- a/src/d3a/models/strategy/market_maker_strategy.py +++ b/src/d3a/models/strategy/market_maker_strategy.py @@ -19,6 +19,7 @@ from d3a.models.read_user_profile import read_and_convert_identity_profile_to_float from d3a_interface.constants_limits import GlobalConfig, ConstSettings from d3a_interface.device_validator import validate_market_maker +from d3a_interface.utils import key_in_dict_and_not_none class MarketMakerStrategy(CommercialStrategy): @@ -41,3 +42,9 @@ def event_market_cycle(self): def event_activate(self): pass + + def area_reconfigure_event(self, *args, **kwargs): + super().area_reconfigure_event(*args, **kwargs) + if key_in_dict_and_not_none(kwargs, 'grid_connected') and \ + type(kwargs['grid_connected']) == bool: + self._grid_connected = kwargs['grid_connected'] diff --git a/src/d3a/setup/d3a-settings.json b/src/d3a/setup/d3a-settings.json index 4bdbf3553..7e6d7c49a 100644 --- a/src/d3a/setup/d3a-settings.json +++ b/src/d3a/setup/d3a-settings.json @@ -5,7 +5,7 @@ "tick_length": "15s", "market_count": 1, "cloud_coverage": 0, - "start_date": "2020-10-14" + "start_date": "2020-10-15" }, "advanced_settings": { "GeneralSettings": { @@ -187,8 +187,8 @@ 3 ], "PAY_AS_CLEAR_AGGREGATION_ALGORITHM": 1, - "MIN_OFFER_AGE": 0, - "MIN_BID_AGE": 0, + "MIN_OFFER_AGE": 2, + "MIN_BID_AGE": 2, "AlternativePricing": { "COMPARE_PRICING_SCHEMES": false, "PRICING_SCHEME": 0, diff --git a/tests/test_live_events.py b/tests/test_live_events.py index 3bc18b8f0..ac11ac3f9 100644 --- a/tests/test_live_events.py +++ b/tests/test_live_events.py @@ -3,6 +3,8 @@ from d3a.models.strategy.load_hours import LoadHoursStrategy from d3a.models.strategy.pv import PVStrategy from d3a.models.strategy.storage import StorageStrategy +from d3a.models.strategy.market_maker_strategy import MarketMakerStrategy +from d3a.models.strategy.infinite_bus import InfiniteBusStrategy from d3a.models.area import Area from d3a.models.config import SimulationConfig from d3a.models.appliance.switchable import SwitchableAppliance @@ -169,6 +171,62 @@ def test_update_area_event_is_updating_the_parameters_of_an_area(self): assert self.area_house1.export_capacity_kWh == \ 765 * self.config.slot_length.total_minutes() / 60.0 + def test_update_area_event_can_switch_strategy_from_market_maker_to_infinite_bus(self): + self.strategy_mmr = MarketMakerStrategy(energy_rate=30) + self.area_mmr = Area("mmr", None, None, self.strategy_mmr, SwitchableAppliance(), + self.config, None, grid_fee_percentage=0) + self.area_mmr.parent = self.area_grid + self.area_grid.children.append(self.area_mmr) + + event_dict = { + "eventType": "update_area", + "area_uuid": self.area_mmr.uuid, + "area_representation": {'type': 'InfiniteBus'} + } + + self.area_grid.activate() + + self.live_events.add_event(event_dict) + self.live_events.handle_all_events(self.area_grid) + assert type(self.area_mmr.strategy) == InfiniteBusStrategy + + def test_update_area_event_can_switch_strategy_from_infinite_bus_to_market_maker(self): + self.strategy_mmr = InfiniteBusStrategy(energy_sell_rate=30, energy_buy_rate=25) + self.area_mmr = Area("mmr", None, None, self.strategy_mmr, SwitchableAppliance(), + self.config, None, grid_fee_percentage=0) + self.area_mmr.parent = self.area_grid + self.area_grid.children.append(self.area_mmr) + + event_dict = { + "eventType": "update_area", + "area_uuid": self.area_mmr.uuid, + "area_representation": {'type': 'MarketMaker'} + } + + self.area_grid.activate() + + self.live_events.add_event(event_dict) + self.live_events.handle_all_events(self.area_grid) + assert type(self.area_mmr.strategy) == MarketMakerStrategy + + def test_update_area_event_cannot_switch_non_strategy_area_to_any_strategy(self): + self.area_mmr = Area("mmr", None, None, None, SwitchableAppliance(), + self.config, None, grid_fee_percentage=0) + self.area_mmr.parent = self.area_grid + self.area_grid.children.append(self.area_mmr) + + event_dict = { + "eventType": "update_area", + "area_uuid": self.area_mmr.uuid, + "area_representation": {'type': 'MarketMaker'} + } + + self.area_grid.activate() + + self.live_events.add_event(event_dict) + self.live_events.handle_all_events(self.area_grid) + assert self.area_mmr.strategy is None + def test_create_area_event_failing_due_to_wrong_parameter_settings_no_exception_raised(self): event_dict = { "eventType": "create_area",