def __init__( self, initial_rate, final_rate, fit_to_limit=True, energy_rate_change_per_update=None, update_interval=duration( minutes=ConstSettings.GeneralSettings.DEFAULT_UPDATE_INTERVAL), rate_limit_object=max): self.fit_to_limit = fit_to_limit self.initial_rate_profile_buffer = read_arbitrary_profile( InputProfileTypes.IDENTITY, initial_rate) self.initial_rate = {} self.final_rate_profile_buffer = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_rate) self.final_rate = {} if fit_to_limit is False: self.energy_rate_change_per_update_profile_buffer = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, energy_rate_change_per_update) else: self.energy_rate_change_per_update_profile_buffer = {} self.energy_rate_change_per_update = {} self.update_interval = update_interval self.update_counter = {} self.number_of_available_updates = 0 self.rate_limit_object = rate_limit_object
def test_if_storage_doesnt_buy_too_expensive(storage_strategy_test3, area_test3): storage_strategy_test3.bid_update.initial_rate = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, 0) storage_strategy_test3.bid_update.final_rate = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, 1) storage_strategy_test3.event_activate() storage_strategy_test3.event_tick() assert len(storage_strategy_test3.accept_offer.calls) == 0
def _populate_buying_rate(self): if self.buying_rate_profile is not None: self.energy_buy_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, self.buying_rate_profile) # TODO: to be checked via deleting in case increased memory is observed during runtime del self.buying_rate_profile elif self.energy_buy_rate is not None: self.energy_buy_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, self.energy_buy_rate) else: self.energy_buy_rate = self.area.config.market_maker_rate
def _area_reconfigure_prices(self, **kwargs): if key_in_dict_and_not_none(kwargs, 'initial_selling_rate'): initial_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, kwargs['initial_selling_rate']) else: initial_rate = self.offer_update.initial_rate_profile_buffer if key_in_dict_and_not_none(kwargs, 'final_selling_rate'): final_rate = read_arbitrary_profile(InputProfileTypes.IDENTITY, kwargs['final_selling_rate']) else: final_rate = self.offer_update.final_rate_profile_buffer if key_in_dict_and_not_none(kwargs, 'energy_rate_decrease_per_update'): energy_rate_change_per_update = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, kwargs['energy_rate_decrease_per_update']) else: energy_rate_change_per_update = \ self.offer_update.energy_rate_change_per_update_profile_buffer if key_in_dict_and_not_none(kwargs, 'fit_to_limit'): fit_to_limit = kwargs['fit_to_limit'] else: fit_to_limit = self.offer_update.fit_to_limit if key_in_dict_and_not_none(kwargs, 'update_interval'): if isinstance(kwargs['update_interval'], int): update_interval = duration(minutes=kwargs['update_interval']) else: update_interval = kwargs['update_interval'] else: update_interval = self.offer_update.update_interval if key_in_dict_and_not_none(kwargs, 'use_market_maker_rate'): self.use_market_maker_rate = kwargs['use_market_maker_rate'] try: self._validate_rates(initial_rate, final_rate, energy_rate_change_per_update, fit_to_limit) except Exception as e: log.error( f"PVStrategy._area_reconfigure_prices failed. Exception: {e}. " f"Traceback: {traceback.format_exc()}") return self.offer_update.set_parameters( initial_rate_profile_buffer=initial_rate, final_rate_profile_buffer=final_rate, energy_rate_change_per_update_profile_buffer= energy_rate_change_per_update, fit_to_limit=fit_to_limit, update_interval=update_interval)
def _read_predefined_profile_for_pv(self): """ Reads profile data from the predefined power profiles. Reads config and constructor parameters and selects the appropriate predefined profile. """ if self._power_profile_index is None or self._power_profile_index == 4: if self.owner.config.pv_user_profile is not None: return self.owner.config.pv_user_profile else: self._power_profile_index = self.owner.config.cloud_coverage if self._power_profile_index == 0: # 0:sunny profile_path = pathlib.Path(d3a_path + '/resources/Solar_Curve_W_sunny.csv') elif self._power_profile_index == 1: # 1:partial profile_path = pathlib.Path(d3a_path + '/resources/Solar_Curve_W_partial.csv') elif self._power_profile_index == 2: # 2:cloudy profile_path = pathlib.Path(d3a_path + '/resources/Solar_Curve_W_cloudy.csv') else: raise ValueError("Energy_profile has to be in [0,1,2,4]") # Populate energy production forecast data self.power_profile = read_arbitrary_profile(InputProfileTypes.POWER, str(profile_path))
def _read_predefined_profile_for_pv(self): """ Reads profile data from the power profile. Handles csv files and dicts. :return: key value pairs of time to energy in kWh """ self.power_profile = read_arbitrary_profile(InputProfileTypes.POWER, self._power_profile_W)
def test_profile_with_date_and_seconds_can_be_parsed(): GlobalConfig.slot_length = duration(minutes=15) profile_date = datetime(year=2019, month=3, day=2) GlobalConfig.start_date = profile_date profile_path = pathlib.Path(d3a_path + '/resources/datetime_seconds_profile.csv') profile = read_arbitrary_profile(InputProfileTypes.POWER, str(profile_path)) # After the 6th element the rest of the entries are populated with the last value expected_energy_values = [1.5, 1.25, 1.0, 0.75, 0.5, 0.25] if GlobalConfig.IS_CANARY_NETWORK: energy_values_profile = [] energy_values_after_profile = [] end_time = profile_date.add(minutes=GlobalConfig.slot_length.minutes * 6) for time, v in profile.items(): if v > 0: if time.weekday() == profile_date.weekday( ) and time.time() < end_time.time(): energy_values_profile.append(v) else: energy_values_after_profile.append(v) assert energy_values_profile == expected_energy_values all(x == 0.25 for x in energy_values_after_profile) else: assert list(profile.values())[:6] == expected_energy_values assert all(x == 0.25 for x in list(profile.values())[6:]) GlobalConfig.start_date = today(tz=TIME_ZONE)
def _event_activate_energy(self, daily_load_profile): """ Reads the power profile data and calculates the required energy for each slot. """ self.load_profile = read_arbitrary_profile(InputProfileTypes.POWER, daily_load_profile)
def hour_profile_of_market_maker_rate(context, scenario): import importlib from d3a_interface.read_user_profile import InputProfileTypes setup_file_module = importlib.import_module( "d3a.setup.{}".format(scenario)) context._market_maker_rate = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, setup_file_module.market_maker_rate) assert context._market_maker_rate is not None
def check_load_profile_csv(context, single_or_multi): house1 = next(filter(lambda x: x.name == "House 1", context.simulation.area.children)) load = next(filter(lambda x: x.name == "H1 DefinedLoad", house1.children)) if single_or_multi == "single": path = user_profile_load_csv.profile_path else: path = user_profile_load_csv_multiday.profile_path input_profile = read_arbitrary_profile(InputProfileTypes.POWER, path) for timepoint, energy in load.strategy.state._desired_energy_Wh.items(): assert energy == find_object_of_same_weekday_and_time(input_profile, timepoint) * 1000
def test_correct_interpolation_power_profile(): slot_length = 20 GlobalConfig.slot_length = duration(minutes=slot_length) profile_path = pathlib.Path(d3a_path + '/resources/Solar_Curve_W_sunny.csv') profile = read_arbitrary_profile(InputProfileTypes.POWER, str(profile_path)) times = list(profile) for ii in range(len(times) - 1): assert abs( (times[ii] - times[ii + 1]).in_seconds()) == slot_length * 60
def test_correct_time_expansion_read_arbitrary_profile(): market_maker_rate = 30 if GlobalConfig.IS_CANARY_NETWORK: GlobalConfig.sim_duration = duration(hours=3) expected_last_time_slot = today(tz=TIME_ZONE).add( days=CN_PROFILE_EXPANSION_DAYS - 1, hours=23, minutes=45) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert list(mmr.keys())[-1] == expected_last_time_slot GlobalConfig.sim_duration = duration(hours=30) expected_last_time_slot = today(tz=TIME_ZONE).add( days=CN_PROFILE_EXPANSION_DAYS - 1, hours=23, minutes=45) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert list(mmr.keys())[-1] == expected_last_time_slot else: GlobalConfig.sim_duration = duration(hours=3) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert (list(mmr.keys())[-1] - today(tz=TIME_ZONE)).days == 0 GlobalConfig.sim_duration = duration(hours=36) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert (list(mmr.keys())[-1] - today(tz=TIME_ZONE)).days == 1 GlobalConfig.sim_duration = duration(hours=48) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert list(mmr.keys())[-1] == today(tz=TIME_ZONE).add(days=1, hours=23, minutes=45) GlobalConfig.sim_duration = duration(hours=49) mmr = read_arbitrary_profile(InputProfileTypes.IDENTITY, market_maker_rate) assert list(mmr.keys())[-1] == today(tz=TIME_ZONE).add(days=2, minutes=45)
def check_pv_csv_profile(context): house1 = list( filter(lambda x: x.name == "House 1", context.simulation.area.children))[0] pv = list(filter(lambda x: x.name == "H1 PV", house1.children))[0] from d3a.setup.strategy_tests.user_profile_pv_csv import user_profile_path profile_data = read_arbitrary_profile(InputProfileTypes.POWER, user_profile_path) for timepoint, energy in pv.strategy.state._energy_production_forecast_kWh.items( ): if timepoint in profile_data.keys(): assert energy == profile_data[timepoint] else: assert energy == 0
def check_pv_profile_csv(context): house1 = list( filter(lambda x: x.name == "House 1", context.simulation.area.children))[0] pv = list(filter(lambda x: x.name == "H1 PV", house1.children))[0] input_profile = read_arbitrary_profile(InputProfileTypes.POWER, context._device_profile) produced_energy = { from_format(f'{TODAY_STR}T{k.hour:02}:{k.minute:02}', DATE_TIME_FORMAT): v for k, v in pv.strategy.state._energy_production_forecast_kWh.items() } for timepoint, energy in produced_energy.items(): if timepoint in input_profile: assert energy == input_profile[timepoint] else: assert False
def check_pv_profile(context): house1 = list( filter(lambda x: x.name == "House 1", context.simulation.area.children))[0] pv = list(filter(lambda x: x.name == "H1 PV", house1.children))[0] if pv.strategy._power_profile_index == 0: path = os.path.join(d3a_path, "resources/Solar_Curve_W_sunny.csv") if pv.strategy._power_profile_index == 1: path = os.path.join(d3a_path, "resources/Solar_Curve_W_partial.csv") if pv.strategy._power_profile_index == 2: path = os.path.join(d3a_path, "resources/Solar_Curve_W_cloudy.csv") profile_data = read_arbitrary_profile(InputProfileTypes.POWER, str(path)) for timepoint, energy in pv.strategy.state._energy_production_forecast_kWh.items( ): if timepoint in profile_data.keys(): assert energy == profile_data[timepoint] else: assert energy == 0
def event_activate(self, **kwargs): super().event_activate() self.max_available_power_kW = \ read_arbitrary_profile(InputProfileTypes.IDENTITY, self.max_available_power_kW)
def event_activate(self, **kwargs): self.energy_rate = self.area.config.market_maker_rate if self.energy_rate is None \ else read_arbitrary_profile(InputProfileTypes.IDENTITY, self.energy_rate)
def _set_market_maker_rate(self): if self.energy_rate_profile is not None: GlobalConfig.market_maker_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, self.energy_rate_profile) elif self.energy_rate is not None: GlobalConfig.market_maker_rate = self.energy_rate
def _validate_constructor_arguments(initial_soc=None, min_allowed_soc=None, battery_capacity_kWh=None, max_abs_battery_power_kW=None, initial_selling_rate=None, final_selling_rate=None, initial_buying_rate=None, final_buying_rate=None, energy_rate_change_per_update=None): if battery_capacity_kWh is not None and battery_capacity_kWh < 0: raise ValueError("Battery capacity should be a positive integer") if max_abs_battery_power_kW is not None and max_abs_battery_power_kW < 0: raise ValueError( "Battery Power rating must be a positive integer.") if initial_soc is not None and 0 < initial_soc > 100: raise ValueError("initial SOC must be in between 0-100 %") if min_allowed_soc is not None and 0 < min_allowed_soc > 100: raise ValueError("initial SOC must be in between 0-100 %") if initial_soc is not None and min_allowed_soc is not None and \ initial_soc < min_allowed_soc: raise ValueError( "Initial charge must be more than the minimum allowed soc.") if initial_selling_rate is not None and initial_selling_rate < 0: raise ValueError("Initial selling rate must be greater equal 0.") if final_selling_rate is not None: if type(final_selling_rate) is float and final_selling_rate < 0: raise ValueError("Final selling rate must be greater equal 0.") elif type(final_selling_rate) is dict and \ any(rate < 0 for _, rate in final_selling_rate.items()): raise ValueError("Final selling rate must be greater equal 0.") if initial_selling_rate is not None and final_selling_rate is not None: initial_selling_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, initial_selling_rate) final_selling_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_selling_rate) if any(initial_selling_rate[hour] < final_selling_rate[hour] for hour, _ in initial_selling_rate.items()): raise ValueError( "Initial selling rate must be greater than final selling rate." ) if initial_buying_rate is not None and initial_buying_rate < 0: raise ValueError("Initial buying rate must be greater equal 0.") if final_buying_rate is not None: final_buying_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_buying_rate) if any(rate < 0 for _, rate in final_buying_rate.items()): raise ValueError("Final buying rate must be greater equal 0.") if initial_buying_rate is not None and final_buying_rate is not None: initial_buying_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, initial_buying_rate) final_buying_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_buying_rate) if any(initial_buying_rate[hour] > final_buying_rate[hour] for hour, _ in initial_buying_rate.items()): raise ValueError( "Initial buying rate must be less than final buying rate.") if final_selling_rate is not None and final_buying_rate is not None: final_selling_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_selling_rate) final_buying_rate = read_arbitrary_profile( InputProfileTypes.IDENTITY, final_buying_rate) if any(final_buying_rate[hour] >= final_selling_rate[hour] for hour, _ in final_selling_rate.items()): raise ValueError( "final_buying_rate should be higher than final_selling_rate." ) if energy_rate_change_per_update is not None and energy_rate_change_per_update < 0: raise ValueError( "energy_rate_change_per_update should be a non-negative value." )
def read_pv_user_profile(self, pv_user_profile=None): self.pv_user_profile = None \ if pv_user_profile is None \ else read_arbitrary_profile(InputProfileTypes.POWER, ast.literal_eval(pv_user_profile))
def _read_solar_profile(profile_path): return read_arbitrary_profile(InputProfileTypes.POWER, str(profile_path))