def run_rolling_window(self, rolling_interval, rolling_step, system=None, optimizer=None,): self.logger.info('Running simulation.') if system is not None and self.system is None: self.system = system else: ValueError('Error assigning system to the simulation.') if optimizer is not None and self.optimizer is None: self.optimizer = optimizer else: ValueError('Error assigning optimizer to the simulation.') if self.system is None: raise ValueError('A system is required.') if self.optimizer is None: raise ValueError('An optimizer is required.') rolling_step = timestep_conversion(rolling_step, pd_units=True) time_range = pandas.date_range(start=rolling_interval[0], end=rolling_interval[1], freq=rolling_step) for step in time_range: self.run_single_step(current_time=step) self.system.clear() self.optimizer.clear() self.clear()
def test_timestep_to_seconds(self): with self.assertRaises(ValueError): timestep_conversion('1s', std_units=True, pd_units=False) with self.assertRaises(ValueError): timestep_conversion('10h', std_units=False, pd_units=True) case = [ ('1H', 3600), ('1h', 3600), ('1T', 60), ('1m', 60), ('15min', 15 * 60), ('15T', 15 * 60), ('15m', 15 * 60), ('100s', 100), ('100S', 100), ] for case_input, case_output in case: self.assertEqual(timestep_to_seconds(case_input), case_output)
def synthetic_solar_irradiance( time_interval, panel_tilt, panel_orientation, latitude, longitude, timestep='1h', unit='kW', utc_localize=False ): """Creates synthetic solar radiation data for a location and panel geometry. """ ground_vector = (0.0, 0.0, 1.0) # (x, y, z) length = 1 panel_vector = polar2cart(1.0, polar_angle=90.0 - panel_tilt, azimuthal_angle=panel_orientation) time_interval = check_time_interval(time_interval, last_step_included=False, delta_step=timestep) if unit == 'kW' or unit == 'kw': scaler = 1000 # Converts to kW else: scaler = 1 # The pysolar module requires localized data start_datetime = pytz.utc.localize(time_interval[0]) end_datetime = pytz.utc.localize(time_interval[1]) date_range = pandas.date_range( start=start_datetime, end=end_datetime, freq=timestep_conversion(timestep, pd_units=True) ) dates, radiations = list(), list() for time in date_range: azimuth, altitude_deg = pysolar.solar.get_position(latitude, longitude, time) sun_vector = polar2cart(1, polar_angle=90.0 - altitude_deg, azimuthal_angle=azimuth) if altitude_deg <= 0: horizontal_radiation = 0. else: horizontal_radiation = pysolar.radiation.get_radiation_direct(time, altitude_deg) / scaler perpendicular_radiation = horizontal_radiation / cos(angle_between(sun_vector, ground_vector)) tilted_radiatiation = perpendicular_radiation * cos(angle_between(sun_vector, panel_vector)) radiations.append(tilted_radiatiation) radiation_results = pandas.DataFrame({'pysolar_radiation': radiations}, index=date_range) if not utc_localize: radiation_results.index = radiation_results.index.tz_convert(None) return radiation_results
def get_spanish_electricity_prices( time_interval, timestep, tariff='tariff_2.0A', token=None, utc_interval=True, last_included=False ): time_interval = check_time_interval(time_interval, str_format=Parameter.UTC_DATETIME_FORMAT) hour_step = '1h' if timestep != hour_step: timestep = timestep_conversion(timestep, pd_units=True) hourly_interval = time_interval.copy() if time_interval[0].time() > datetime.time(time_interval[0].hour): hourly_interval[0] = time_interval[0].replace(minute=0, second=0, microsecond=0) if time_interval[1].time() > datetime.time(time_interval[1].hour): hourly_interval[1] = time_interval[1].replace(minute=0, second=0, microsecond=0) + datetime.timedelta( hours=1) prices = get_hourly_spanish_electricity_prices( time_interval=hourly_interval, utc_interval=utc_interval, token=token, tariff=tariff, last_included=last_included ) prices = prices.to_frame('prices') # Convert pandas.Series to pandas.DataFrame prices = series_upsampling( prices, new_time_interval=time_interval, new_timestep=timestep, old_timestep=hour_step, upsample_values=False ) else: prices = get_hourly_spanish_electricity_prices( time_interval=time_interval, utc_interval=utc_interval, token=token, tariff=tariff, last_included=last_included ) return prices
def extract_results_from_opt_model(self, config): self.logger.info('Extracting results from optimization model.') # Extract variables of interest from the model raw_results = {} for e in self.optimization_model.E_set: raw_results[e] = numpy.array([ self.optimization_model.E[e][t].value for t in self.optimization_model.periods ]) if self.system.has_battery: raw_results['soc'] = numpy.array([ self.optimization_model.soc[t].value for t in self.optimization_model.periods ]) # check_soc = numpy.array([self.optimization_model.soc[t].value for t in range(len(self.optimization_model.soc))]) # print(f'Full SOC vector: {check_soc}') # Check the raw_results for undefined values in the solver output for key, values in raw_results.items(): if None in values: raise ValueError( 'The solver was unable to find a solution for \ some variables in at least: {}'.format(key)) # Combine var also checks that the system do not charge and discharge or # buy and sell energy at the same time. Below are the error messages: results = {} if not (self.system.has_interruptable_loads or self.system.has_schedulable_loads): results['building_load'] = self.system.fix_electrical_load else: raise NotImplementedError('Flexible loads not implemented yet.') if self.system.has_stochastic_generators: results[ 'stochastic_generation'] = self.system.stochastic_electrical_gen.copy( ) if self.system.has_external_grid: error_supply = 'Invalid solution. The system buys and sells \ electricity at the same time' results['power_supply_flow'] = combine_positive_negative_variables( raw_results['buy'], raw_results['sell'], error_supply) if self.system.has_battery: error_batt = 'Invalid solution. The system is charging and \ discharging the battery at the same time' results[ 'battery_energy_flow'] = combine_positive_negative_variables( raw_results['batt_dis'], raw_results['batt_chrg'], error_batt) results['battery_soc'] = numpy.array(raw_results['soc']) if self.system.has_external_grid: results['prices_buy'] = numpy.array( self.optimization_model.prices['buy']) results['prices_sell'] = numpy.array( self.optimization_model.prices['sell']) timestep = timestep_conversion(config['timestep'], pd_units=True) results_index = pandas.date_range(start=config['start'], periods=config['periods'], freq=timestep) self.results = pandas.DataFrame(results, index=results_index) # We assume that the first SOC <results_index[0]> in the results is the initial SOC. Therefore, the target SOC # for the next period is the second one <results_index[1]> self.target_soc = float(self.results.at[results_index[1], 'battery_soc']) return results
def forecast(self, forecast_interval, input_data, target_label, timestep): forecast_interval = check_time_interval(forecast_interval) try: input_data.index = input_data.index.tz_convert(None) except TypeError: pass columns = list(input_data.columns) columns.remove(target_label) if len(columns) > 0: self.logger.info( f'Using regressors: {columns}, in FB Prophet model.') input_data.interpolate(inplace=True) training_data = input_data[ input_data.index < forecast_interval[0]].copy() test_data = input_data[input_data.index >= forecast_interval[0]].copy() training_data[self.raw_index_label] = training_data.index.strftime( self.datetime_format) training_data.reset_index(inplace=True, drop=True) rename_columns = { self.raw_index_label: self.ph_index, target_label: self.raw_target_label } training_data.rename(index=str, columns=rename_columns, inplace=True) model = Prophet() for column in columns: model.add_regressor(column) self.logger.info('Training FB Prophet model.') with suppress_stdout_stderr(): model.fit(training_data) if test_data.empty: timestep = timestep_conversion(timestep, pd_units=True) future = pd.date_range(start=forecast_interval[0], end=forecast_interval[1], freq=timestep).values test_data = pd.DataFrame({self.ph_index: future}) else: test_data[self.raw_index_label] = test_data.index.strftime( self.datetime_format) test_data.reset_index(inplace=True, drop=True) rename_columns = { self.raw_index_label: self.ph_index, target_label: self.raw_target_label } test_data.rename(index=str, columns=rename_columns, inplace=True) test_data.drop(columns=[self.raw_target_label], inplace=True) self.logger.info('Producing FB Prophet forecast.') forecast_table = model.predict(test_data) forecast_table.set_index(self.ph_index, drop=True, inplace=True) forecast_table.index = pd.to_datetime(forecast_table.index, format=self.datetime_format, utc=True) forecast = forecast_table.loc[:, [self.y_hat]] forecast.rename(columns={self.y_hat: target_label}, inplace=True) forecast.index.rename(self.raw_index_label, inplace=True) return forecast
def test_timestep_conversion(self): with self.assertRaises(ValueError): timestep_conversion('1h', std_units=True, pd_units=True) with self.assertRaises(ValueError): timestep_conversion('2h', std_units=True, pd_units=False) with self.assertRaises(ValueError): timestep_conversion('2h', std_units=False, pd_units=False) with self.assertRaises(ValueError): timestep_conversion('2s', std_units=True, pd_units=False) with self.assertRaises(ValueError): timestep_conversion('2s', std_units=False, pd_units=True) case_std = [ ('1H', '1h'), ('1h', '1h'), ('1T', '1m'), ('1m', '1m'), ('15min', '15m'), ('15T', '15m'), ('15m', '15m'), ('100s', '100s'), ('100S', '100s'), ] case_pd = [ ('1H', '1H'), ('1h', '1H'), ('1T', '1T'), ('1m', '1T'), ('15min', '15min'), ('15T', '15T'), ('15m', '15T'), ('100s', '100S'), ('100S', '100S'), ] for case_input, case_output in case_std: self.assertEqual(timestep_conversion(case_input, std_units=True), case_output) for case_input, case_output in case_pd: self.assertEqual(timestep_conversion(case_input, pd_units=True), case_output)
def run_single_step( self, system=None, optimizer=None, current_time=None, simulation_end='prices_availability', midnight_ahead=None, simulation_length=None ): self.logger.info('Running simulation.') if system is not None and self.system is None: self.system = system else: ValueError('Error assigning system to the simulation.') if optimizer is not None and self.optimizer is None: self.optimizer = optimizer else: ValueError('Error assigning optimizer to the simulation.') if current_time is not None: self.current_time = current_time else: self.current_time = get_current_time() # Utc time self.start = find_next_step_start(current_time=self.current_time, timestep=self.timestep) # i.e. assume current_time=13:23, and timestep=5m, next start would be 13:25 if simulation_end == 'fix': self.end = get_fix_simulation_length_end_timestamp(self.start, simulation_length=simulation_length) elif simulation_end == 'prices_availability': if self.system.has_external_grid: """Assume we are at day D and we want to run the simulation, at least, for the remaining of day D." The next day is D+1. We want the prices of the remaining of the D plus, if available, the prices of day D+1. Therefore, we'll need the utc time corresponding to the midnight between day D and D+1 or between D+1 and D+2 (depending on prices availability). """ publication_time = self.system.get_external_grid_object().publication_time local_current_datetime = utc_to_local(self.current_time, local_tz=self.local_tz) midnight_ahead = 1 if local_current_datetime.time() >= publication_time else 0 # 0 is D-D+1 midnight self.end = get_following_midnight_utc_timestamp( self.current_time, local_tz=self.local_tz, midnight_ahead=midnight_ahead ) else: message = ( f'The Simulation object cannot determine the end of the simulation based ' f'on prices availability because there is no grid in the system.' ) raise ValueError(message) elif simulation_end == 'midnight_ahead': self.end = get_following_midnight_utc_timestamp( self.current_time, local_tz=self.local_tz, midnight_ahead=midnight_ahead ) else: raise ValueError('Invalid method to compute the simulation end.') pandas_timestep = timestep_conversion(self.timestep, pd_units=True) self.periods = len(pandas.date_range(start=self.start, end=self.end, closed='left', freq=pandas_timestep)) self.interval = [self.start, self.end] time_config = self.get_time_configuration() self.system.prepare_to_optimize(config=time_config) self.results = self.optimizer.solve(system=self.system, config=time_config) return self.results