diff --git a/component_library/component_models/BaseStorage.py b/component_library/component_models/BaseStorage.py index 81e292171c8b40d3f1879c1f52440be5f462d78b..9b8388e3b3434feae032b97faa84fca600c24b41 100644 --- a/component_library/component_models/BaseStorage.py +++ b/component_library/component_models/BaseStorage.py @@ -229,6 +229,7 @@ class BaseStorage(BaseComponent): self._constraint_maxcap(model, var_dict, T) self._constraint_conser(model, flows, var_dict, T) self._constraint_vdi2067(model, var_dict, T) + self._constraint_bidirectional_flows(model, var_dict, T, self.max_size) #self._constraint_reopt(model, flows, var_dict, T) def add_variables(self, input_profiles, plant_parameters, var_dict, flows, diff --git a/input_files/models/district_models/example_CA/config.csv b/input_files/models/district_models/example_CA/config.csv new file mode 100644 index 0000000000000000000000000000000000000000..8d15c623466861f434533b792b808cc2d943dd11 --- /dev/null +++ b/input_files/models/district_models/example_CA/config.csv @@ -0,0 +1,2 @@ +injection_price,injection_price_variable,injection/pvpeak,gas_price,gas_price_variable,elec_price,elec_price_variable,heat_price,heat_price_variable,cooling_price,cooling_price_variable,injection_price_gas,injection_price_gas_variable,injection_price_heat,injection_price_heat_variable,injection_price_cooling,injection_price_cooling_variable,elec_emission,gas_emission,yearly_interest,planning_horizon +0.0793,0,0.7,0.0606,0,0.3046,0,0,0,0,0,0,0,0,0,0,0,0.401,0.21,0.03,20 diff --git a/input_files/models/district_models/example_CA/data_path.csv b/input_files/models/district_models/example_CA/data_path.csv new file mode 100644 index 0000000000000000000000000000000000000000..aa0479f160f0098ef724bba48c299f1cb0ca738d --- /dev/null +++ b/input_files/models/district_models/example_CA/data_path.csv @@ -0,0 +1,5 @@ +type,path,unit +demand_electric,generate,h +demand_heat,generate,h +irradiance,input_files/data/irradiance/Lindenberg2006BSRN_Irradiance_60sec.csv,min +temperature,input_files/data/temperature/temperature.csv,h diff --git a/input_files/models/district_models/example_CA/elec_matrix.csv b/input_files/models/district_models/example_CA/elec_matrix.csv new file mode 100644 index 0000000000000000000000000000000000000000..2ac712600082b9ce6e4524a08bf81d224af2feab --- /dev/null +++ b/input_files/models/district_models/example_CA/elec_matrix.csv @@ -0,0 +1,5 @@ +comp_name,comp_type,model,min_size,max_size,current_size,inv_pv_bat,battery,grd,elec_cns +inv_pv_bat,Inverter,STP-7000TL-20,0,20,0,0,1,1,1 +battery,LiionBattery,BAT1,0,6,6,1,0,0,0 +grd,StandardACGrid,GRD1,1000,1000,1000,1,0,0,1 +elec_cns,StandardElectricalConsumption,CNS1,1000,1000,0,0,0,0,0 diff --git a/input_files/models/district_models/example_community/config.csv b/input_files/models/district_models/example_community/config.csv new file mode 100644 index 0000000000000000000000000000000000000000..6d77d4fff831a65e6ae805c2c5f457dca1457eb2 --- /dev/null +++ b/input_files/models/district_models/example_community/config.csv @@ -0,0 +1,2 @@ +injection_price,injection_price_variable,injection/pvpeak,gas_price,gas_price_variable,elec_price,elec_price_variable,network_usage_capacity_fee_low,network_usage_energy_fee_low,network_usage_capacity_fee_high,network_usage_energy_fee_high,heat_price,levies_int,levies_ext,concession,electricity_tax_int,electricity_tax_ext,VAT,heat_price_variable,cooling_price,cooling_price_variable,injection_price_gas,injection_price_gas_variable,injection_price_heat,injection_price_heat_variable,injection_price_cooling,injection_price_cooling_variable,elec_emission,gas_emission,yearly_interest,planning_horizon +0.0793,0,0.7,0.0606,0,0.3046,0,14.79,0.0506,50.54,0.0363,0,0.0276,0.0496,0.0199,0,0.0205,0.190,0,0,0,0,0,0,0,0,0,0.401,0.21,0.03,20 diff --git a/input_files/models/district_models/example_community/data_path.csv b/input_files/models/district_models/example_community/data_path.csv new file mode 100644 index 0000000000000000000000000000000000000000..e0a77e063aad3d88e2eef7d788cd38527bc3d28a --- /dev/null +++ b/input_files/models/district_models/example_community/data_path.csv @@ -0,0 +1,2 @@ +type,path,unit +elec_price,input_files/data/prices/day-ahead/hourly_price.csv,h diff --git a/input_files/models/district_models/example_community/elec_matrix.csv b/input_files/models/district_models/example_community/elec_matrix.csv new file mode 100644 index 0000000000000000000000000000000000000000..f072d2fea74caf87564656c41c00a5bc1b9b9a79 --- /dev/null +++ b/input_files/models/district_models/example_community/elec_matrix.csv @@ -0,0 +1,6 @@ +comp_name,comp_type,model,min_size,max_size,current_size,pv_roof,inv_pv_bat,battery,grd,elec_cns +pv_roof,StandardPVGenerator,PV2,6,6,6,0,1,0,0,0 +inv_pv_bat,Inverter,STP-7000TL-20,0,20,0,0,0,1,1,1 +battery,LiionBattery,BAT1,6,6,6,0,1,0,0,0 +grd,StandardACGrid,GRD1,1000,1000,1000,0,1,0,0,1 +elec_cns,StandardElectricalConsumption,CNS1,1000,1000,0,0,0,0,0,0 diff --git a/runme_community_new.py b/runme_community_new.py new file mode 100644 index 0000000000000000000000000000000000000000..cb2fea46b0fe5045dc45597360131f0dde300df5 --- /dev/null +++ b/runme_community_new.py @@ -0,0 +1,315 @@ +# runme.py is the central script to execute the optimization of the community. + +# Importing the necessary files + +import numpy as np +import Model_Library.District.scripts as scripts +from datetime import timedelta +import math +import time +import pandas as pd +import Model_Library.Prosumer.scripts as scripts +import Model_Library.Prosumer.main as main +import Model_Library.Prosumer.scripts.extract_inputs as extract_inputs +from functools import partial +from multiprocessing import Pool +from tqdm import tqdm +import os +import argparse + +from Model_Library.Prosumer.scripts.results_evaluation.results_evaluation import Plot_savings + +import Model_Library.Prosumer.main_ca as main_ca +import Model_Library.District.main_district as main_district + + +def process_each_prosumer(prosumer_name, prosumer_dict, data_source, commentary, no_process_bar_rh): + try: + # PLEASE CHANGE HERE + # Set the simulation time frame and optional rolling horizon configurations: + # 't_start': start date of simulations, Unit: JJJJ-MM-DD hh:mm:ss + # 't_end': end date of simulations, Unit: JJJJ-MM-DD hh:mm:ss + # 't_step': granularity of optimization model, Unit: hours + # Rolling horizon (RH) can be set by: + # 't_rh_horizon': width of rolling horizon intervals, Unit: hours, MINIMUM VALUE: 2 !!! + # 't_rh_shift': rolling horizon shift between intervals, Unit: hours + # 't_current_value_length': number of values at beginning of rolling horizon interval that are replaced by real values, Unit: hours + # 't_history': number of days before actual simulation interval for the demand generator to be able to make required predictions + if prosumer_dict[prosumer_name]['rolling_horizon']: + t_start = pd.Timestamp("2019-09-01 00:00:00") + t_end = pd.Timestamp("2019-09-01 5:00:00") + t_step = 1 + t_rh_horizon = 3 + t_rh_shift = 1 + t_current_value_length = 2 + t_history = 14 # days + + # PLEASE CHANGE HERE + # Prediction settings + predictions = {'demand_electric': 'SameHourYesterday', + 'demand_heat': 'SameHourYesterday', + 'day_ahead_price': 'SameHourYesterday', + 'intraday_price': 'SameHourYesterday', + 'solar_radiation': 'Perfect', + # currently the method generate_g_t_series takes the same t_start as the prediction -> no historical + # data for the prediction available: easy fix would be to set a minus time delta in the t_start + # argument of generate_g_t_series + 'temperature': 'SameHourYesterday'} + else: + t_start = pd.Timestamp("2019-07-01 00:00:00") + t_end = pd.Timestamp("2019-7-10 23:00:00") + pd.Timedelta(hours=1) + t_step = 1 + t_rh_horizon = (t_end - t_start) / pd.Timedelta(hours=1) + t_rh_shift = t_rh_horizon - 1 + t_current_value_length = t_rh_horizon + t_history = 0 # days + + # PLEASE CHANGE HERE + # Prediction settings + predictions = {'demand_electric': 'Perfect', + 'demand_heat': 'Perfect', + 'day_ahead_price': 'Perfect', + 'intraday_price': 'Perfect', + 'solar_radiation': 'Perfect', + # currently the method generate_g_t_series takes the same t_start as the prediction -> no historical + # data for the prediction available: easy fix would be to set a minus time delta in the t_start + # argument of generate_g_t_series + 'temperature': 'Perfect'} + + # Fixed variables - DO NOT CHANGE + storage_states = {} + interim_results = {} + final_iteration = False + + # Set aggregation options + parser = argparse.ArgumentParser(description='Start optimization from DB or local data') + parser.add_argument('-a', '--aggregate', action="store_true", dest="aggregate", + help="activating aggregation of input time series", default=False) + options = parser.parse_args() + + # Calculate number of rolling horizon intervals and loop through them + for t in tqdm(pd.date_range(t_start, t_end - pd.Timedelta(hours=t_rh_shift + 1), freq=str(t_rh_shift) + 'H'), disable=no_process_bar_rh): + # ToDo: replace first value with perfect value (can be done in runme) + # set end date for current loop + t_end_loop = t + pd.Timedelta(hours=t_rh_horizon) + + # exceptions that occur at global end of simulation horizon + if t_end_loop > t_end: + t_end_loop = t_end + if t_current_value_length > (t_end_loop - t) / pd.Timedelta(hours=1): + t_current_value_length = (t_end_loop - t) / pd.Timedelta(hours=1) + + # Set flag for final iteration + if t == t_end - pd.Timedelta(hours=t_rh_shift + 1): + final_iteration = True + + # Start main programme + prosumer = main.Main(data_source, {prosumer_name: prosumer_dict[prosumer_name]}, t, t_end_loop, t_step, + predictions, t_current_value_length, t_end, + t_history, commentary, storage_states, t_rh_shift, aggregation=options.aggregate) + + # Run optimization + prosumer.run_optimization(prosumer.prosumer_name_list) + + # Show results - Results are only plotted after last iteration of rolling horizon + prosumer.show_results(prosumer.prosumer_name_list, interim_results, final_iteration) + + # Get storage states from this iteration + storage_states = prosumer.charge_status + + # Get interim results of current rolling horizon interval + interim_results = prosumer.interim_results + + return prosumer.prosumer[prosumer_name] + except ValueError: + print(prosumer_name+" could not be optimized!") + +# # MAIN PROGRAM ------------------------------------------------------------------------------------------------------- +if __name__ == "__main__": + # Initialization scenario path and global variables for the prosumer optimization + # Start timer + start = time.time() + + # PLEASE CHANGE HERE + # Path to local data - this is only used when selecting local mode + # 'topology_path': path to matrices that define the prosumer topology + # 'config_path': path to global configurations like prices, injection prices, emission costs, etc. + #topology_path = 'input_files/models/Study_Base' + #config_path = topology_path + '/config.csv' + #data_path = topology_path + '/data_path.csv' + #prosumer_name = 'office' + #prosumer_dict = {prosumer_name: {'topology_path': topology_path, 'config_path': config_path, 'data_path': data_path}} + topology_path = ['input_files/models/prosumer_models/SCN0_CAT1'] + prosumer_name = ['SCN0_CAT1', 'SCN0_CAT1'] + rolling_horizon = [False] + elec_demand = [1500, 10000] + therm_demand = [5000, 20000] + hot_water_demand = 1500#[1500, 1500] + step_elec_demand = 500 + step_therm_demand = 500 + #step_hot_water_demand = 0 + prosumer_dict = {} + """ for i in range(len(prosumer_name)): + for j in range(elec_demand[0], elec_demand[1], step_elec_demand): + for k in range(therm_demand[0], therm_demand[1], step_therm_demand): + #for l in range(hot_water_demand[0], hot_water_demand[1], step_hot_water_demand): + prosumer_dict[prosumer_name[i]+'_'+str(j)+'_'+str(k)] = {'elec_demand': j, + 'therm_demand': k, + 'hot_water_demand': hot_water_demand, + 'topology_path': topology_path[i], + 'config_path': topology_path[i] + '/config.csv', + 'data_path': topology_path[i] + '/data_path.csv', + 'rolling_horizon': rolling_horizon[i]}""" + + prosumer_dict={'SCN2_CAT1_PV11_3000_6000': {'elec_demand': 3000, 'therm_demand': 6000, 'hot_water_demand': 1500, + 'topology_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV11', + 'config_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV11/config.csv', + 'data_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV11/data_path.csv', + 'rolling_horizon': False}, + 'SCN2_CAT1_PV12_BA_6000_6000': {'elec_demand': 6000, 'therm_demand': 6000, 'hot_water_demand': 1500, + 'topology_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV12_BA', + 'config_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV12_BA/config.csv', + 'data_path': 'input_files/models/prosumer_models/SCN2_CAT1_PV12_BA/data_path.csv', + 'rolling_horizon': False}, + 'SCN0_CAT1_6000_6000': {'elec_demand': 6000, 'therm_demand': 6000, 'hot_water_demand': 1500, + 'topology_path': 'input_files/models/prosumer_models/SCN0_CAT1', + 'config_path': 'input_files/models/prosumer_models/SCN0_CAT1/config.csv', + 'data_path': 'input_files/models/prosumer_models/SCN0_CAT1/data_path.csv', + 'rolling_horizon': False}, + 'SCN0_CAT1_3000_6000': {'elec_demand': 3000, 'therm_demand': 6000, 'hot_water_demand': 1500, + 'topology_path': 'input_files/models/prosumer_models/SCN0_CAT1', + 'config_path': 'input_files/models/prosumer_models/SCN0_CAT1/config.csv', + 'data_path': 'input_files/models/prosumer_models/SCN0_CAT1/data_path.csv', + 'rolling_horizon': False}} + # PLEASE CHANGE HERE + # Select data source + # Options: '1': import from database, '2': import from local folder + data_source = 2 + reference_results = {} + commentary = False + no_process_bar_rh = True + parallel_processing = False + + # Timer output + tic = time.time() + # Start program + # Run multiple independent prosumers in parallel on multiple cores + final_prosumer_dict = dict.fromkeys(prosumer_dict.keys()) + if parallel_processing: + count_processes = len(prosumer_dict.keys()) + pool = Pool(os.cpu_count()) + parallel_func = partial(process_each_prosumer, prosumer_dict=prosumer_dict, data_source=data_source, commentary=commentary, no_process_bar_rh=no_process_bar_rh) + mapped_values = list(tqdm(pool.map(parallel_func, list(prosumer_dict.keys())), total=count_processes)) + # Normal processing, one core only + else: + for prosumer_name in list(prosumer_dict.keys()): + final_prosumer_dict[prosumer_name] = process_each_prosumer(prosumer_name= prosumer_name, prosumer_dict=prosumer_dict, data_source=data_source, commentary=commentary, no_process_bar_rh=no_process_bar_rh) + + # Timer output + toc = time.time() + + if reference_results: + for topology in prosumer_name: + Plot_savings(reference_results, topology) + + # Timer output + end = time.time() + # Additional console output + if commentary: + print("============ Execution Times =============") + print("Pre-processing [s]: \t" + str(tic - start)) + #print("(Interaction with database [s]:\t" + str(prosumer.connect_with_db) + ")") + print("Optimization [s]: \t" + str(toc - tic)) + print("Post-processing [s]: \t" + str(end - toc)) + print("----------------------------------------") + print("Total [s]: \t" + str((end - toc) + (toc - tic) + (tic - start))) + print("==========================================") + +'-----------------COMMUNITY-PART----------------------------------' +start_total = time.time() + +data_source = 2 # [1]: datasource from data bank; [2]: datasource from local +commentary = False + +t_start = pd.Timestamp("2019-07-01 00:00:00") +t_end = pd.Timestamp("2019-07-10 23:00:00") + pd.Timedelta(hours=1) +t_step = 1 +t_rh_horizon = (t_end - t_start) / pd.Timedelta(hours=1) + +t_horizon = (pd.Timestamp(t_end) - pd.Timestamp(t_start)) / np.timedelta64(t_step, 'h') + +"------------------------Communnity Asset---------------------------" +"-------------------------------------------------------------------" + +topology_path = 'input_files/models/district_models/example_CA' +config_path = topology_path + '/config.csv' +data_path = topology_path + '/data_path.csv' +ca_dict = {'ca_bat': {'elec_demand': 0, + 'therm_demand': 0, + 'hot_water_demand': 0, + 'topology_path': topology_path, + 'config_path': config_path, + 'data_path': data_path}} +#ca_dict = {} +# PLEASE CHANGE HERE +# Prediction settings +predictions = {'demand_electric': 'SameHourYesterday', + 'demand_heat': 'SameHourYesterday', + 'day_ahead_price': 'SameHourYesterday', + 'intraday_price': 'SameHourYesterday', + 'solar_radiation': 'Perfect', + # currently the method generate_g_t_series takes the same t_start as the prediction -> no historical + # data for the prediction available: easy fix would be to set a minus time delta in the t_start + # argument of generate_g_t_series + 'temperature': 'SameHourYesterday'} + +# Fixed variables - DO NOT CHANGE +storage_states = {} +interim_results = {} +final_iteration = False + +ca_strategy = 'sizing_max_operational_profit' + +# create time steps list +time_steps = [] +for t in pd.date_range(pd.Timestamp(t_start), + pd.Timestamp(t_start) + timedelta(hours=t_horizon) - timedelta(hours=t_step), + freq=str(t_step)+'H'): + time_steps.append(t) + +t_start = pd.Timestamp(t_start) +t_end = pd.Timestamp(t_start) + timedelta(hours=t_horizon) - timedelta(hours=t_step) +t_rh_shift = 0 +t_current_value_length = (t_end-t_start)/np.timedelta64(t_step, 'h') +t_history = t_horizon/24 + +# initialize community component in the same way prosumers are. +# The difference is that they are not directly optimized +comm_assets = main.Main_CA(data_source, ca_dict, t_start, t_end, t_step, predictions, t_current_value_length, t_end, + t_history, commentary, storage_states, t_rh_shift, aggregation=False) + + +"""---------------------------COMMUNITY-LEVEL----------------------""" +"""----------------------------------------------------------------""" + +topology_path = 'input_files/models/district_models/example_community' +config_path = topology_path + '/config.csv' +data_path_comm = topology_path + '/data_path.csv' +comm_dict = {'community': {'topology_path': topology_path, 'config_path': config_path, 'data_path': data_path_comm}} +comm_strategy = ['max_operational_profit'] + +community_main = main_district.MainDistrict(final_prosumer_dict, + comm_assets, + time_steps, + t_step, + comm_dict, + ca_strategy, + comm_strategy, + t_horizon) + +# ------------POST-PROCESS----------------- + + + + +