diff --git a/Input/single_year_example.xlsx b/Input/single_year_example.xlsx index 18c86836..0583aefd 100644 Binary files a/Input/single_year_example.xlsx and b/Input/single_year_example.xlsx differ diff --git a/run_single_year.py b/run_single_year.py index b9841507..6e4bb977 100644 --- a/run_single_year.py +++ b/run_single_year.py @@ -22,19 +22,19 @@ objective = 'cost' # set either 'cost' or 'CO2' as objective # Choose Solver (cplex, glpk, gurobi, ...) -solver = 'glpk' +solver = 'gurobi' # simulation timesteps -(offset, length) = (0, 8760) # time step selection +(offset, length) = (0, 120) # time step selection timesteps = range(offset, offset+length+1) dt = 1 # length of each time step (unit: hours) # detailed reporting commodity/sites report_tuples = [ - (2019, 'North', 'Elec'), - (2019, 'Mid', 'Elec'), - (2019, 'South', 'Elec'), - (2019, ['North', 'Mid', 'South'], 'Elec') + (2020, 'North', 'Elec'), + (2020, 'Mid', 'Elec'), + (2020, 'South', 'Elec'), + (2020, ['North', 'Mid', 'South'], 'Elec') ] # optional: define names for sites in report_tuples @@ -42,10 +42,10 @@ # plotting commodities/sites plot_tuples = [ - (2019, 'North', 'Elec'), - (2019, 'Mid', 'Elec'), - (2019, 'South', 'Elec'), - (2019, ['North', 'Mid', 'South'], 'Elec') + (2020, 'North', 'Elec'), + (2020, 'Mid', 'Elec'), + (2020, 'South', 'Elec'), + (2020, ['North', 'Mid', 'South'], 'Elec') ] # optional: define names for sites in plot_tuples @@ -67,12 +67,12 @@ # select scenarios to be run scenarios = [ urbs.scenario_base, - urbs.scenario_stock_prices, - urbs.scenario_co2_limit, - urbs.scenario_co2_tax_mid, - urbs.scenario_no_dsm, - urbs.scenario_north_process_caps, - urbs.scenario_all_together + # urbs.scenario_stock_prices, + # urbs.scenario_co2_limit, + # urbs.scenario_co2_tax_mid, + # urbs.scenario_no_dsm, + # urbs.scenario_north_process_caps, + # urbs.scenario_all_together ] for scenario in scenarios: diff --git a/urbs/features/BuySellPrice.py b/urbs/features/BuySellPrice.py index f1203262..40a760db 100644 --- a/urbs/features/BuySellPrice.py +++ b/urbs/features/BuySellPrice.py @@ -73,7 +73,7 @@ def res_sell_total_rule(m, stf, sit, com, com_type): total_consumption = 0 for tm in m.tm: total_consumption += ( - m.e_co_sell[tm, stf, sit, com, com_type]) + m.e_co_sell[tm, stf, sit, com, com_type] * m.typeday['weight_typeday'][(stf,tm)]) total_consumption *= m.weight return (total_consumption <= m.commodity_dict['max'][(stf, sit, com, com_type)]) @@ -99,7 +99,7 @@ def res_buy_total_rule(m, stf, sit, com, com_type): total_consumption = 0 for tm in m.tm: total_consumption += ( - m.e_co_buy[tm, stf, sit, com, com_type]) + m.e_co_buy[tm, stf, sit, com, com_type] * m.typeday['weight_typeday'][(stf,tm)]) total_consumption *= m.weight return (total_consumption <= m.commodity_dict['max'][(stf, sit, com, com_type)]) @@ -174,7 +174,7 @@ def revenue_costs(m): try: return -sum( m.e_co_sell[(tm,) + c] * - m.buy_sell_price_dict[c[2]][(c[0], tm)] * m.weight * + m.buy_sell_price_dict[c[2]][(c[0], tm)] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][c] * m.commodity_dict['cost_factor'][c] for tm in m.tm @@ -182,7 +182,7 @@ def revenue_costs(m): except KeyError: return -sum( m.e_co_sell[(tm,) + c] * - m.buy_sell_price_dict[c[2], ][(c[0], tm)] * m.weight * + m.buy_sell_price_dict[c[2], ][(c[0], tm)] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][c] * m.commodity_dict['cost_factor'][c] for tm in m.tm @@ -194,7 +194,7 @@ def purchase_costs(m): try: return sum( m.e_co_buy[(tm,) + c] * - m.buy_sell_price_dict[c[2]][(c[0], tm)] * m.weight * + m.buy_sell_price_dict[c[2]][(c[0], tm)] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][c] * m.commodity_dict['cost_factor'][c] for tm in m.tm @@ -202,7 +202,7 @@ def purchase_costs(m): except KeyError: return sum( m.e_co_buy[(tm,) + c] * - m.buy_sell_price_dict[c[2], ][(c[0], tm)] * m.weight * + m.buy_sell_price_dict[c[2], ][(c[0], tm)] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][c] * m.commodity_dict['cost_factor'][c] for tm in m.tm diff --git a/urbs/features/TypeDay.py b/urbs/features/TypeDay.py new file mode 100644 index 00000000..f556e604 --- /dev/null +++ b/urbs/features/TypeDay.py @@ -0,0 +1,31 @@ +import math +import pyomo.core as pyomo + +def add_typeday(m): + + # Validation: + if not (len(m.timesteps) % 24 == 0 or len(m.timesteps) % 24 == 1): + print('Warning: length of timesteps does not end at the end of a day!') + + # change weight parameter to 1, since the whole year is representated by weight_typeday + m.del_component(m.weight) + m.weight = pyomo.Param( + initialize=1, + doc='Pre-factor for variable costs and emissions for annual result for type days = 1') + + # m.t_endofday = pyomo.Set( + # within=m.t, + # initialize=[i * 24 * m.dt for i in list(range(1,1+int(len(m.timesteps) / m.dt / 24)))], + # ordered=True, + # doc='timestep at the end of each day') + # + # m.res_storage_state_cyclicity_typeday = pyomo.Constraint( + # m.t_endofday, m.sto_tuples, + # rule=res_storage_state_cyclicity_typeday_rule, + # doc='storage content initial == storage content at the end of each day') + + return m + +def res_storage_state_cyclicity_typeday_rule(m, t, stf, sit, sto, com): + return (m.e_sto_con[m.t[1], stf, sit, sto, com] == + m.e_sto_con[t, stf, sit, sto, com]) diff --git a/urbs/features/__init__.py b/urbs/features/__init__.py index b6741392..07730189 100644 --- a/urbs/features/__init__.py +++ b/urbs/features/__init__.py @@ -12,4 +12,5 @@ from .dsm import add_dsm, dsm_surplus from .BuySellPrice import add_buy_sell_price, bsp_surplus, revenue_costs, \ purchase_costs -from .TimeVarEff import add_time_variable_efficiency \ No newline at end of file +from .TimeVarEff import add_time_variable_efficiency +from .TypeDay import add_typeday \ No newline at end of file diff --git a/urbs/features/storage.py b/urbs/features/storage.py index 856135d4..32eab6e3 100644 --- a/urbs/features/storage.py +++ b/urbs/features/storage.py @@ -289,11 +289,11 @@ def storage_cost(m, cost_type): m.storage_dict['cost_factor'][s] for s in m.sto_tuples) elif cost_type == 'Variable': - return sum(m.e_sto_con[(tm,) + s] * m.weight * + return sum(m.e_sto_con[(tm,) + s] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.storage_dict['var-cost-c'][s] * m.storage_dict['cost_factor'][s] + (m.e_sto_in[(tm,) + s] + m.e_sto_out[(tm,) + s]) * - m.weight * m.storage_dict['var-cost-p'][s] * + m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.storage_dict['var-cost-p'][s] * m.storage_dict['cost_factor'][s] for tm in m.tm for s in m.sto_tuples) diff --git a/urbs/features/transmission.py b/urbs/features/transmission.py index 7c3a8257..61c8ac0e 100644 --- a/urbs/features/transmission.py +++ b/urbs/features/transmission.py @@ -370,18 +370,18 @@ def transmission_cost(m, cost_type): for t in m.tra_tuples) elif cost_type == 'Variable': if m.mode['dpf']: - return sum(m.e_tra_in[(tm,) + t] * m.weight * + return sum(m.e_tra_in[(tm,) + t] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.transmission_dict['var-cost'][t] * m.transmission_dict['cost_factor'][t] for tm in m.tm for t in m.tra_tuples_tp) + \ - sum(m.e_tra_abs[(tm,) + t] * m.weight * + sum(m.e_tra_abs[(tm,) + t] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.transmission_dict['var-cost'][t] * m.transmission_dict['cost_factor'][t] for tm in m.tm for t in m.tra_tuples_dc) else: - return sum(m.e_tra_in[(tm,) + t] * m.weight * + return sum(m.e_tra_in[(tm,) + t] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.transmission_dict['var-cost'][t] * m.transmission_dict['cost_factor'][t] for tm in m.tm diff --git a/urbs/identify.py b/urbs/identify.py index 48ba95b0..a563c1f7 100644 --- a/urbs/identify.py +++ b/urbs/identify.py @@ -29,6 +29,7 @@ def identify_mode(data): 'bsp': False, # buy sell price 'tve': False, # time variable efficiency 'dpf': False, # dc power flow + 'tdy': False, # type days 'exp': { # expansion 'pro': True, 'tra': False, @@ -55,6 +56,8 @@ def identify_mode(data): if 'reactance' in data['transmission'].keys(): if any(data['transmission']['reactance'] > 0): mode['dpf'] = True + if any(data['type day']['weight_typeday'] > 0): + mode['tdy'] = True return mode diff --git a/urbs/input.py b/urbs/input.py index 2db93450..ee0199f7 100644 --- a/urbs/input.py +++ b/urbs/input.py @@ -82,6 +82,8 @@ def read_input(input_files, year): demand = xls.parse('Demand').set_index(['t']) demand = pd.concat([demand], keys=[support_timeframe], names=['support_timeframe']) + typeday = demand.loc[:, ['weight_typeday']] + demand = demand.drop(columns=['weight_typeday']) # split columns by dots '.', so that 'DE.Elec' becomes # the two-level column index ('DE', 'Elec') demand.columns = split_columns(demand.columns, '.') @@ -163,6 +165,7 @@ def read_input(input_files, year): 'commodity': commodity, 'process': process, 'process_commodity': process_commodity, + 'type day': typeday, 'demand': demand, 'supim': supim, 'transmission': transmission, @@ -245,6 +248,13 @@ def pyomo_model_prep(data, timesteps): if m.mode['tve']: m.eff_factor_dict = \ data["eff_factor"].dropna(axis=0, how='all').to_dict() + if m.mode['tdy']: + m.typeday = data['type day'].dropna(axis=0, how='all').to_dict() + else: + # if mode 'typeday' is not active, create a dict with ones + temp = pd.DataFrame(index=data['demand'].dropna(axis=0, how='all').index) + temp['weight_typeday']=1 + m.typeday = temp.to_dict() # Create columns of support timeframe values commodity['support_timeframe'] = (commodity.index. diff --git a/urbs/model.py b/urbs/model.py index 87052791..0ac99517 100644 --- a/urbs/model.py +++ b/urbs/model.py @@ -38,7 +38,7 @@ def create_model(data, dt=1, timesteps=None, objective='cost', # costs are annual by default, variable costs are scaled by weight) and # among different simulation durations meaningful. m.weight = pyomo.Param( - initialize=float(8760) / (len(m.timesteps) * dt), + initialize=float(8760) / ((len(m.timesteps) - 1) * dt), doc='Pre-factor for variable costs and emissions for an annual result') # dt = spacing between timesteps. Required for storage equation that @@ -79,6 +79,7 @@ def create_model(data, dt=1, timesteps=None, objective='cost', indexlist.add(tuple(key)[0]) m.stf = pyomo.Set( initialize=indexlist, + ordered=True, doc='Set of modeled support timeframes (e.g. years)') # site (e.g. north, middle, south...) @@ -287,6 +288,8 @@ def create_model(data, dt=1, timesteps=None, objective='cost', m.pro_timevar_output_tuples = pyomo.Set( within=m.stf * m.sit * m.pro * m.com, doc='empty set needed for (partial) process output') + if m.mode['tdy']: + m = add_typeday(m) # Equation declarations # equation bodies are defined in separate functions, referred to here by @@ -505,7 +508,7 @@ def res_stock_total_rule(m, stf, sit, com, com_type): total_consumption = 0 for tm in m.tm: total_consumption += ( - m.e_co_stock[tm, stf, sit, com, com_type]) + m.e_co_stock[tm, stf, sit, com, com_type] * m.typeday['weight_typeday'][(stf,tm)]) total_consumption *= m.weight return (total_consumption <= m.commodity_dict['max'][(stf, sit, com, com_type)]) @@ -534,7 +537,7 @@ def res_env_total_rule(m, stf, sit, com, com_type): # calculate total creation of environmental commodity com env_output_sum = 0 for tm in m.tm: - env_output_sum += (- commodity_balance(m, tm, stf, sit, com)) + env_output_sum += (- commodity_balance(m, tm, stf, sit, com) * m.typeday['weight_typeday'][(stf,tm)]) env_output_sum *= m.weight return (env_output_sum <= m.commodity_dict['max'][(stf, sit, com, com_type)]) @@ -681,7 +684,7 @@ def res_global_co2_limit_rule(m, stf): # minus because negative commodity_balance represents creation # of that commodity. co2_output_sum += (- commodity_balance(m, tm, - stf, sit, 'CO2')) + stf, sit, 'CO2') * m.typeday['weight_typeday'][(stf,tm)]) # scaling to annual output (cf. definition of m.weight) co2_output_sum *= m.weight @@ -704,6 +707,7 @@ def res_global_co2_budget_rule(m): # creation of that commodity. co2_output_sum += (- commodity_balance (m, tm, stf, sit, 'CO2') * + m.typeday['weight_typeday'][(stf, tm)] * m.weight * stf_dist(stf, m)) @@ -784,7 +788,7 @@ def def_costs_rule(m, cost_type): elif cost_type == 'Variable': cost = \ - sum(m.tau_pro[(tm,) + p] * m.weight * + sum(m.tau_pro[(tm,) + p] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.process_dict['var-cost'][p] * m.process_dict['cost_factor'][p] for tm in m.tm @@ -797,7 +801,7 @@ def def_costs_rule(m, cost_type): elif cost_type == 'Fuel': return m.costs[cost_type] == sum( - m.e_co_stock[(tm,) + c] * m.weight * + m.e_co_stock[(tm,) + c] * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][c] * m.commodity_dict['cost_factor'][c] for tm in m.tm for c in m.com_tuples @@ -805,7 +809,7 @@ def def_costs_rule(m, cost_type): elif cost_type == 'Environmental': return m.costs[cost_type] == sum( - - commodity_balance(m, tm, stf, sit, com) * m.weight * + - commodity_balance(m, tm, stf, sit, com) * m.weight * m.typeday['weight_typeday'][(m.stf[1],tm)] * m.commodity_dict['price'][(stf, sit, com, com_type)] * m.commodity_dict['cost_factor'][(stf, sit, com, com_type)] for tm in m.tm @@ -837,6 +841,7 @@ def co2_rule(m): # creation of that commodity. if m.mode['int']: co2_output_sum += (- commodity_balance(m, tm, stf, sit, 'CO2') * + m.typeday['weight_typeday'][(stf, tm)] * m.weight * stf_dist(stf, m)) else: co2_output_sum += (- commodity_balance(m, tm, stf, sit, 'CO2') * diff --git a/urbs/plot.py b/urbs/plot.py index 0e15b794..418fdd1e 100644 --- a/urbs/plot.py +++ b/urbs/plot.py @@ -49,7 +49,7 @@ def sort_plot_elements(elements): quotient = quotient.fillna(0) # sort created/consumed ascencing with quotient i.e. base load first elements = elements.append(quotient) - new_columns = elements.columns[elements.ix[elements.last_valid_index()] + new_columns = elements.columns[elements.loc[elements.last_valid_index()] .argsort()] elements_sorted = elements[new_columns][:-1] diff --git a/urbs/validation.py b/urbs/validation.py index b5b2f3c8..6789cba3 100644 --- a/urbs/validation.py +++ b/urbs/validation.py @@ -159,6 +159,21 @@ def validate_input(data): "worksheet 'DSM' must be from the list of site " "names specified in the worksheet 'Site'.") + if any(data['type day']['weight_typeday'] > 0): + if not data['dsm'].empty: + print('Warning: Typeday and DSM active!') + + if not (len(data['type day'].dropna(axis=0, how='all').index) % 24 == 0 or + len(data['type day'].dropna(axis=0, how='all').index) % 24 - 1 == 0): + print('Warning: Weighting of the typeday does not end at the end of a day, please check the length of ' + '-Demand-weight_typeday') + + if min(data['type day'].iloc[1:,0]) < 1: + print('Warning: weighting_typeday < 1') + + if sum(data['type day'].loc[:,'weight_typeday'].dropna(axis=0, how='all')) != 8760: + print('Warning: The sum of weighting_typeday does not equal a year') + # report that variable costs may have error if used with CO2 minimization and DCPF def validate_dc_objective(data, objective): if not data['transmission'].empty: