def _get_bus_id_from_mv_grid(self, session, subst_id): """ Queries the eTraGo bus ID for given MV grid (ding0) ID Parameters ---------- subst_id : int MV grid (ding0) ID Returns ------- int eTraGo bus ID """ if self._versioned is True: ormclass_hvmv_subst = grid.__getattribute__('EgoDpHvmvSubstation') bus_id = session.query(ormclass_hvmv_subst.otg_id).filter( ormclass_hvmv_subst.subst_id == subst_id, ormclass_hvmv_subst.version == self._grid_version).scalar() if self._versioned is False: ormclass_hvmv_subst = model_draft.__getattribute__( 'EgoGridHvmvSubstation') bus_id = session.query(ormclass_hvmv_subst.otg_id).filter( ormclass_hvmv_subst.subst_id == subst_id).scalar() return bus_id
def get_mvgrid_from_bus_id(session, bus_id): # Mapping ormclass_hvmv_subst = model_draft.__getattribute__('EgoGridHvmvSubstation') subst_id = session.query(ormclass_hvmv_subst.subst_id).filter( ormclass_hvmv_subst.otg_id == bus_id).scalar() #ToDo Check if subst_id is really the mv grid ID # Anyway, this should be adapted by Dingo return subst_id
def get_etragospecs_direct(session, bus_id, eTraGo, args): """ Reads eTraGo Results from Database and returns an Object of the Interface class ETraGoSpecs Parameters ---------- session : :class:`~.` #Todo: Add class etc.... Oemof session object (Database Interface) bus_id : int ID of the corresponding HV bus eTraGo : :class:`~.` #Todo: Add class etc.... Returns ------- etragospecs : :class:~.` eDisGo ETraGoSpecs Object """ print("\nSpecs Direct") specs_meta_data = {} performance = {} specs_meta_data.update({'TG Bus ID': bus_id}) ormclass_result_meta = model_draft.__getattribute__( 'EgoGridPfHvResultMeta') ormclass_aggr_w = model_draft.__getattribute__( 'ego_supply_aggr_weather_mview') ormclass_source = model_draft.__getattribute__('EgoGridPfHvSource') snap_idx = eTraGo.snapshots if args['global'][ 'recover'] == True: # If the results are beeing recovered, the scn_name cannot be used from Scenario Settings File result_id = args['global']['result_id'] scn_name = session.query(ormclass_result_meta.scn_name).filter( ormclass_result_meta.result_id == result_id).scalar() else: scn_name = args['eTraGo']['scn_name'] specs_meta_data.update({'scn_name': scn_name}) if scn_name == 'SH Status Quo': scn_name = 'Status Quo' # Generators t0 = time.perf_counter() weather_dpdnt = ['wind', 'solar'] ## DF procesing all_gens_df = eTraGo.generators[eTraGo.generators['bus'] == str(bus_id)] all_gens_df.reset_index(inplace=True) all_gens_df = all_gens_df.rename(columns={'index': 'generator_id'}) all_gens_df = all_gens_df[[ 'generator_id', 'p_nom', 'p_nom_opt', 'carrier' ]] names = [] for index, row in all_gens_df.iterrows(): carrier = row['carrier'] name = session.query(ormclass_source.name).filter( ormclass_source.source_id == carrier).scalar() names.append(name) all_gens_df['name'] = names all_gens_df = all_gens_df.drop(['carrier'], axis=1) ## Conventionals t1 = time.perf_counter() performance.update({'Generator Data Processing': t1 - t0}) conv_df = all_gens_df[~all_gens_df.name.isin(weather_dpdnt)] conv_cap = conv_df[['p_nom', 'name']].groupby('name').sum().T conv_dsptch_norm = pd.DataFrame(0.0, index=snap_idx, columns=list(set(conv_df['name']))) for index, row in conv_df.iterrows(): generator_id = row['generator_id'] source = row['name'] p = eTraGo.generators_t.p[str(generator_id)] p_norm = p / conv_cap[source]['p_nom'] conv_dsptch_norm[source] = conv_dsptch_norm[source] + p_norm ## Renewables t2 = time.perf_counter() performance.update({'Conventional Dispatch': t2 - t1}) ### Capacities ren_df = all_gens_df[all_gens_df.name.isin(weather_dpdnt)] w_ids = [] for index, row in ren_df.iterrows(): aggr_id = row['generator_id'] w_id = session.query(ormclass_aggr_w.c.w_id).filter( ormclass_aggr_w.c.aggr_id == aggr_id, ormclass_aggr_w.c.scn_name == scn_name).scalar() w_ids.append(w_id) ren_df = ren_df.assign(w_id=pd.Series(w_ids, index=ren_df.index)) ren_df.dropna( inplace=True) ##This should be unnecessary (and I think it isnt) aggr_gens = ren_df.groupby(['name', 'w_id']).agg({ 'p_nom': 'sum' }).reset_index() aggr_gens.rename(columns={'p_nom': 'p_nom_aggr'}, inplace=True) aggr_gens['ren_id'] = aggr_gens.index ### Dispatch and Curteilment dispatch = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) curtailment = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) for index, row in ren_df.iterrows(): gen_id = row['generator_id'] name = row['name'] w_id = row['w_id'] ren_id = int(aggr_gens[(aggr_gens['name'] == name) & (aggr_gens['w_id'] == w_id)]['ren_id']) p_nom_aggr = float( aggr_gens[aggr_gens['ren_id'] == ren_id]['p_nom_aggr']) p_nom = float(ren_df[ren_df['generator_id'] == gen_id]['p_nom']) p_series = eTraGo.generators_t.p[str(gen_id)] p_norm_tot_series = p_series / p_nom_aggr p_max_pu_series = eTraGo.generators_t.p_max_pu[str(gen_id)] p_max_norm_tot_series = p_max_pu_series * p_nom / p_nom_aggr p_curt_norm_tot_series = p_max_norm_tot_series - p_norm_tot_series dispatch[ren_id] = dispatch[ren_id] + p_norm_tot_series curtailment[ren_id] = curtailment[ren_id] + p_curt_norm_tot_series # Storage t3 = time.perf_counter() performance.update({'Renewable Dispatch and Curt.': t3 - t2}) ## Capactiy stor_df = eTraGo.storage_units[eTraGo.storage_units['bus'] == str(bus_id)] stor_df.reset_index(inplace=True) stor_df = stor_df.rename(columns={'index': 'storage_id'}) stor_df = stor_df[[ 'storage_id', 'p_nom_opt', 'p_nom', 'max_hours', 'carrier' ]] names = [] for index, row in stor_df.iterrows(): carrier = row['carrier'] name = session.query(ormclass_source.name).filter( ormclass_source.source_id == carrier).scalar() names.append(name) stor_df = stor_df.assign(name=pd.Series(names, index=stor_df.index)) stor_df = stor_df.drop(['carrier'], axis=1) stor_df['capacity_MWh'] = stor_df['p_nom_opt'] * stor_df['max_hours'] count_bat = 0 for index, row in stor_df.iterrows(): if row['max_hours'] >= 20.0: stor_df.at[index, 'name'] = 'ext_long_term' else: stor_df.at[ index, 'name'] = 'battery' # ToDo: find a more generic solution count_bat += 1 ### Project Specific Battery Capacity battery_capacity = 0.0 # MWh for index, row in stor_df.iterrows(): if row['name'] == 'battery': battery_capacity = battery_capacity + row['capacity_MWh'] ### Project Specific Battery Active Power battery_active_power = pd.Series(0.0, index=snap_idx) for index, row in stor_df.iterrows(): name = row['name'] stor_id = row['storage_id'] if name == 'battery': stor_series = eTraGo.storage_units_t.p[str(stor_id)] stor_series_kW = stor_series * 1000 battery_active_power = battery_active_power + stor_series_kW t4 = time.perf_counter() performance.update({'Storage Data Processing and Dispatch': t4 - t3}) specs = ETraGoSpecs(battery_capacity=battery_capacity, battery_active_power=battery_active_power, conv_dispatch=conv_dsptch_norm, renewables=aggr_gens, ren_dispatch=dispatch, ren_curtailment=curtailment) t5 = time.perf_counter() performance.update({'Overall time': t5 - t0}) #print(performance) # print("\n Conventional Dispatch (Normalized): \n", # conv_dsptch_norm, # "\n\n Renewable Generators: \n", # aggr_gens, # "\n\n Renewable Dispatch: \n", # dispatch, # "\n\n Renewable Curtailment: \n", # curtailment, "\n\n") # # for keys,values in performance.items(): # print(keys, ": ", values) return specs
def get_etragospecs_from_db(session, bus_id, result_id): """ Reads eTraGo Results from Database and returns an Object of the Interface class ETraGoSpecs Parameters ---------- session : :class:`~.` #Todo: Add class etc.... Oemof session object (Database Interface) bus_id : int ID of the corresponding HV bus result_id : int ID of the corresponding database result Returns ------- etragospecs : :class:~.` eDisGo ETraGoSpecs Object """ print("\nSpecs from DB") specs_meta_data = {} performance = {} specs_meta_data.update({'TG Bus ID': bus_id}) specs_meta_data.update({'Result ID': result_id}) # Mapping ormclass_result_meta = model_draft.__getattribute__( 'EgoGridPfHvResultMeta') ormclass_result_bus = model_draft.__getattribute__( 'EgoGridPfHvResultBus' ) # Instead of using the automapper, this is the explicit alternative (from egoei.db_tables). #ormclass_result_bus = model_draft.EgoGridPfHvResultBus # This is equivalent #ormclass_result_bus_t = model_draft.__getattribute__('EgoGridPfHvResultBusT') ormclass_result_gen = model_draft.__getattribute__( 'EgoGridPfHvResultGenerator') ormclass_result_gen_t = model_draft.__getattribute__( 'EgoGridPfHvResultGeneratorT') #ormclass_result_gen_single = model_draft.__getattribute__('EgoSupplyPfGeneratorSingle') #ormclass_result_load = model_draft.__getattribute__('EgoGridPfHvResultLoad') #ormclass_result_load_t = model_draft.__getattribute__('EgoGridPfHvResultLoadT') ormclass_result_stor = model_draft.__getattribute__( 'EgoGridPfHvResultStorage') ormclass_result_stor_t = model_draft.__getattribute__( 'EgoGridPfHvResultStorageT') ormclass_source = model_draft.__getattribute__('EgoGridPfHvSource') ormclass_aggr_w = model_draft.__getattribute__( 'ego_supply_aggr_weather_mview') # Meta Queries ## Check if session.query(ormclass_result_bus).filter( ormclass_result_bus.bus_id == bus_id, ormclass_result_bus.result_id == result_id).count() == 0: logger.warning('Bus not found') ## Snapshot Range snap_idx = session.query(ormclass_result_meta.snapshots).filter( ormclass_result_meta.result_id == result_id).scalar() scn_name = session.query(ormclass_result_meta.scn_name).filter( ormclass_result_meta.result_id == result_id).scalar() if scn_name == 'SH Status Quo': scn_name = 'Status Quo' specs_meta_data.update({'scn_name': scn_name}) # Generators try: t0 = time.perf_counter() weather_dpdnt = ['wind', 'solar'] ## Conventionals t1 = time.perf_counter() performance.update({'Generator Data Processing': t1 - t0}) query = session.query( ormclass_result_gen. generator_id, # This ID is an aggregate ID (single generators aggregated) ormclass_result_gen.p_nom, ormclass_source.name).join( ormclass_source, ormclass_source.source_id == ormclass_result_gen.source).filter( ormclass_result_gen.bus == bus_id, ormclass_result_gen.result_id == result_id, ormclass_source.name.notin_(weather_dpdnt)) conv_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) conv_cap = conv_df[['p_nom', 'name']].groupby('name').sum().T query = session.query(ormclass_result_gen_t.generator_id, ormclass_result_gen_t.p).filter( ormclass_result_gen_t.generator_id.in_( conv_df['generator_id']), ormclass_result_gen_t.result_id == result_id) conv_t_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) conv_t_df = pd.merge(conv_df, conv_t_df, on='generator_id')[['name', 'p']] conv_dsptch_norm = pd.DataFrame(0.0, index=snap_idx, columns=list(set(conv_df['name']))) for index, row in conv_t_df.iterrows(): source = row['name'] gen_series_norm = pd.Series( data=(row['p'] / conv_cap[source]['p_nom'] ), # Every generator normalized by installed capacity. index=snap_idx) conv_dsptch_norm[ source] = conv_dsptch_norm[source] + gen_series_norm ## Renewables t2 = time.perf_counter() performance.update({'Conventional Dispatch': t2 - t1}) ### Capacities query = session.query( ormclass_result_gen.generator_id, ormclass_result_gen.p_nom, ormclass_result_gen.p_nom_opt, ormclass_source.name, ormclass_aggr_w.c.w_id).join( ormclass_source, ormclass_source.source_id == ormclass_result_gen.source).join( ormclass_aggr_w, ormclass_aggr_w.c.aggr_id == ormclass_result_gen.generator_id).filter( ormclass_result_gen.bus == bus_id, ormclass_result_gen.result_id == result_id, ormclass_source.name.in_(weather_dpdnt), ormclass_aggr_w.c.scn_name == scn_name) ren_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) aggr_gens = ren_df.groupby(['name', 'w_id']).agg({ 'p_nom': 'sum' }).reset_index() aggr_gens.rename(columns={'p_nom': 'p_nom_aggr'}, inplace=True) aggr_gens['ren_id'] = aggr_gens.index ### Dispatch and Curteilment query = session.query( ormclass_result_gen_t. generator_id, # This is an aggregated generator ID (see ego_dp_powerflow_assignment_generator for info) ormclass_result_gen_t.p, ormclass_result_gen_t. p_max_pu # The maximum output for each snapshot per unit of p_nom for the OPF (e.g. for variable renewable generators this can change due to weather conditions; for conventional generators it represents a maximum dispatch) ).filter( ormclass_result_gen_t.generator_id.in_(ren_df['generator_id']), ormclass_result_gen_t.result_id == result_id) ren_t_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) ren_t_df = pd.merge(ren_t_df, ren_df, on='generator_id')[[ 'generator_id', 'w_id', 'name', 'p', 'p_max_pu' ]] dispatch = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) curtailment = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) for index, row in ren_t_df.iterrows(): gen_id = row['generator_id'] name = row['name'] w_id = row['w_id'] ren_id = int(aggr_gens[(aggr_gens['name'] == name) & (aggr_gens['w_id'] == w_id)]['ren_id']) p_nom_aggr = float( aggr_gens[aggr_gens['ren_id'] == ren_id]['p_nom_aggr']) p_nom = float(ren_df[ren_df['generator_id'] == gen_id]['p_nom']) p_series = pd.Series(data=row['p'], index=snap_idx) p_norm_tot_series = p_series / p_nom_aggr p_max_pu_series = pd.Series(data=row['p_max_pu'], index=snap_idx) p_max_norm_tot_series = p_max_pu_series * p_nom / p_nom_aggr p_curt_norm_tot_series = p_max_norm_tot_series - p_norm_tot_series dispatch[ren_id] = dispatch[ren_id] + p_norm_tot_series curtailment[ren_id] = curtailment[ren_id] + p_curt_norm_tot_series except: logger.exception("Generators could not be queried for \ Specs with Metadata: \n %s" % specs_meta_data) # Load # Load are not part of the Specs anymore # Storage t3 = time.perf_counter() performance.update({'Renewable Dispatch and Curt.': t3 - t2}) try: ## Capactiy query = session.query( ormclass_result_stor.storage_id, ormclass_result_stor.p_nom_opt, ormclass_result_stor.p_nom, ormclass_result_stor.max_hours, ormclass_source.name).join( ormclass_source, ormclass_source.source_id == ormclass_result_stor.source).filter( ormclass_result_stor.bus == bus_id, ormclass_result_stor.result_id == result_id, ormclass_source.name == 'extendable_storage') stor_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) stor_df['capacity_MWh'] = stor_df['p_nom_opt'] * stor_df['max_hours'] count_bat = 0 for index, row in stor_df.iterrows(): if row['max_hours'] >= 20.0: stor_df.at[index, 'name'] = 'ext_long_term' else: stor_df.at[ index, 'name'] = 'battery' # ToDo: find a more generic solution count_bat += 1 ### Project Specific Battery Capacity battery_capacity = 0.0 # MWh for index, row in stor_df.iterrows(): if row['name'] == 'battery': battery_capacity = battery_capacity + row['capacity_MWh'] ## Dispatch query = session.query( ormclass_result_stor_t.storage_id, ormclass_result_stor_t.p, ormclass_result_stor_t.state_of_charge).filter( ormclass_result_stor_t.storage_id.in_(stor_df['storage_id']), ormclass_result_stor_t.result_id == result_id) stor_t_df = pd.DataFrame( query.all(), columns=[column['name'] for column in query.column_descriptions]) stor_t_df = pd.merge(stor_t_df, stor_df, on='storage_id')[[ 'storage_id', 'name', 'p', 'state_of_charge' ]] ### Project Specific Battery Active Power battery_active_power = pd.Series(0.0, index=snap_idx) for index, row in stor_t_df.iterrows(): name = row['name'] if name == 'battery': stor_series = pd.Series( data=row['p'], # in MW index=snap_idx) stor_series_kW = [x * 1000 for x in stor_series] # in kW battery_active_power = battery_active_power + stor_series_kW except: logger.exception("Storage could not be queried for \ Specs with Metadata: \n %s" % specs_meta_data) # Return Specs t4 = time.perf_counter() performance.update({'Storage Data Processing and Dispatch': t4 - t3}) specs = ETraGoSpecs(battery_capacity=battery_capacity, battery_active_power=battery_active_power, conv_dispatch=conv_dsptch_norm, renewables=aggr_gens, ren_dispatch=dispatch, ren_curtailment=curtailment) # logger.info(specs_meta_data) t5 = time.perf_counter() performance.update({'Overall time': t5 - t0}) # print("\n Conventional Dispatch (Normalized): \n", # conv_dsptch_norm, # "\n\n Renewable Generators: \n", # aggr_gens, # "\n\n Renewable Dispatch: \n", # dispatch, # "\n\n Renewable Curtailment: \n", # curtailment, "\n\n") for keys, values in performance.items(): print(keys, ": ", values) return specs
def get_etragospecs_direct(session, bus_id, etrago_network, scn_name, grid_version, pf_post_lopf, max_cos_phi_renewable): """ Reads eTraGo Results from Database and returns and returns the interface values as a dictionary of corresponding dataframes Parameters ---------- session : sqlalchemy.orm.session.Session Handles conversations with the database. bus_id : int ID of the corresponding HV bus etrago_network: :class:`etrago.tools.io.NetworkScenario` eTraGo network object compiled by :meth:`etrago.appl.etrago` scn_name : str Name of used scenario 'Status Quo', 'NEP 2035' or 'eGo 100' Returns ------- :obj:`dict` of :pandas:`pandas.DataFrame<dataframe>` Dataframes used as eDisGo inputs """ logger.info('Specs for bus {}'.format(bus_id)) if pf_post_lopf: logger.info('Active and reactive power interface') else: logger.info('Only active power interface') specs_meta_data = {} performance = {} specs_meta_data.update({'TG Bus ID': bus_id}) if grid_version is None: logger.warning('Weather_id taken from model_draft (not tested)') ormclass_gen_single = model_draft.__getattribute__( 'EgoSupplyPfGeneratorSingle') else: ormclass_aggr_w = supply.__getattribute__('EgoAggrWeather') snap_idx = etrago_network.snapshots # Generators t0 = time.perf_counter() weather_dpdnt = ['wind', 'solar', 'wind_onshore', 'wind_offshore'] # DF procesing all_gens_df = etrago_network.generators[etrago_network.generators['bus'] == str(bus_id)] all_gens_df.index.name = 'generator_id' all_gens_df.reset_index(inplace=True) all_gens_df = all_gens_df[[ 'generator_id', 'p_nom', 'p_nom_opt', 'carrier' ]] all_gens_df = all_gens_df.rename(columns={"carrier": "name"}) all_gens_df = all_gens_df[all_gens_df['name'] != 'wind_offshore'] for index, row in all_gens_df.iterrows(): name = row['name'] if name == 'wind_onshore': all_gens_df.at[index, 'name'] = 'wind' # Conventionals t1 = time.perf_counter() performance.update({'Generator Data Processing': t1 - t0}) conv_df = all_gens_df[~all_gens_df.name.isin(weather_dpdnt)] conv_dsptch = pd.DataFrame(0.0, index=snap_idx, columns=list(set(conv_df['name']))) conv_reactive_power = pd.DataFrame(0.0, index=snap_idx, columns=list(set(conv_df['name']))) if not conv_df.empty: conventionals = True conv_cap = conv_df[['p_nom', 'name']].groupby('name').sum().T for index, row in conv_df.iterrows(): generator_id = row['generator_id'] source = row['name'] p = etrago_network.generators_t.p[str(generator_id)] p_norm = p / conv_cap[source]['p_nom'] conv_dsptch[source] = conv_dsptch[source] + p_norm if pf_post_lopf: q = etrago_network.generators_t.q[str(generator_id)] # q normalized with p_nom q_norm = q / conv_cap[source]['p_nom'] conv_reactive_power[source] = (conv_reactive_power[source] + q_norm) if pf_post_lopf: new_columns = [(col, '') for col in conv_reactive_power.columns] conv_reactive_power.columns = pd.MultiIndex.from_tuples( new_columns) else: conventionals = False logger.warning('No conventional generators at bus {}'.format(bus_id)) # Renewables t2 = time.perf_counter() performance.update({'Conventional Dispatch': t2 - t1}) # Capacities ren_df = all_gens_df[all_gens_df.name.isin(weather_dpdnt)] if ren_df.empty: logger.warning('No renewable generators at bus {}'.format(bus_id)) for index, row in ren_df.iterrows(): aggr_id = row['generator_id'] if grid_version is None: w_id = session.query(ormclass_gen_single.w_id).filter( ormclass_gen_single.aggr_id == aggr_id, ormclass_gen_single.scn_name == scn_name).limit(1).scalar() else: w_id = session.query(ormclass_aggr_w.w_id).filter( ormclass_aggr_w.aggr_id == aggr_id, #ormclass_aggr_w.scn_name == scn_name, ormclass_aggr_w.version == grid_version).limit(1).scalar() ren_df.at[index, 'w_id'] = w_id ren_df.dropna(inplace=True) aggr_gens = ren_df.groupby(['name', 'w_id']).agg({ 'p_nom': 'sum' }).reset_index() aggr_gens.rename(columns={'p_nom': 'p_nom_aggr'}, inplace=True) aggr_gens['ren_id'] = aggr_gens.index ### Dispatch and Curteilment potential = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) dispatch = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) curtailment = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) if pf_post_lopf: reactive_power = pd.DataFrame(0.0, index=snap_idx, columns=aggr_gens['ren_id']) for index, row in ren_df.iterrows(): gen_id = row['generator_id'] name = row['name'] w_id = row['w_id'] ren_id = int(aggr_gens[(aggr_gens['name'] == name) & (aggr_gens['w_id'] == w_id)]['ren_id']) p_nom_aggr = float( aggr_gens[aggr_gens['ren_id'] == ren_id]['p_nom_aggr']) p_nom = row['p_nom'] p_series = etrago_network.generators_t.p[str(gen_id)] p_norm_tot_series = p_series / p_nom_aggr p_max_pu_series = etrago_network.generators_t.p_max_pu[str(gen_id)] p_max_norm_tot_series = p_max_pu_series * p_nom / p_nom_aggr potential[ren_id] = potential[ren_id] + p_max_norm_tot_series dispatch[ren_id] = dispatch[ren_id] + p_norm_tot_series if pf_post_lopf: q_series = etrago_network.generators_t.q[str(gen_id)] q_norm_tot_series = q_series / p_nom_aggr reactive_power[ren_id] = (reactive_power[ren_id] + q_norm_tot_series) curtailment = potential.sub(dispatch) new_columns = [(aggr_gens[aggr_gens.ren_id == col].name.iloc[0], aggr_gens[aggr_gens.ren_id == col].w_id.iloc[0]) for col in potential.columns] potential.columns = pd.MultiIndex.from_tuples(new_columns) new_columns = [(aggr_gens[aggr_gens.ren_id == col].name.iloc[0], aggr_gens[aggr_gens.ren_id == col].w_id.iloc[0]) for col in dispatch.columns] dispatch.columns = pd.MultiIndex.from_tuples(new_columns) new_columns = [(aggr_gens[aggr_gens.ren_id == col].name.iloc[0], aggr_gens[aggr_gens.ren_id == col].w_id.iloc[0]) for col in curtailment.columns] curtailment.columns = pd.MultiIndex.from_tuples(new_columns) if pf_post_lopf: new_columns = [(aggr_gens[aggr_gens.ren_id == col].name.iloc[0], aggr_gens[aggr_gens.ren_id == col].w_id.iloc[0]) for col in reactive_power.columns] reactive_power.columns = pd.MultiIndex.from_tuples(new_columns) # Q limit calculation if max_cos_phi_renewable: logger.info('Applying Q limit (max cos(phi)={})'.format( max_cos_phi_renewable)) phi = math.acos(max_cos_phi_renewable) for col in reactive_power: for idx in reactive_power.index: p = dispatch.loc[idx][col] q = reactive_power.loc[idx][col] q_max, q_min = p * math.tan(phi), -p * math.tan(phi) if q > q_max: q = q_max elif q < q_min: q = q_min reactive_power.at[idx, col] = q # Reactive Power concat if conventionals: all_reactive_power = pd.concat( [conv_reactive_power, reactive_power], axis=1) else: all_reactive_power = reactive_power # Storage t3 = time.perf_counter() performance.update({'Renewable Dispatch and Curt.': t3 - t2}) # Capactiy min_extended = 0.3 stor_df = etrago_network.storage_units.loc[ (etrago_network.storage_units['bus'] == str(bus_id)) & (etrago_network.storage_units['p_nom_extendable'] == True) & (etrago_network.storage_units['p_nom_opt'] > min_extended) & (etrago_network.storage_units['max_hours'] <= 20.)] # Only batteries logger.warning('Minimum storage of {} MW'.format(min_extended)) ext_found = False if len(stor_df) == 1: logger.info('Extendable storage unit found') ext_found = True stor_id = stor_df.index[0] stor_p_series_kW = etrago_network.storage_units_t.p[str( stor_id)] * 1000 if pf_post_lopf: try: stor_q_series_kvar = etrago_network.storage_units_t.q[str( stor_id)] * 1000 except: logger.warning( "No Q series found for storage unit {}".format(stor_id)) stor_q_series_kvar = etrago_network.storage_units_t.p[str( stor_id)] * 0 if ext_found == False: logger.info( "No extendable storage unit found at bus {}".format(bus_id)) t4 = time.perf_counter() performance.update({'Storage Data Processing and Dispatch': t4 - t3}) specs = { 'conv_dispatch': conv_dsptch, 'ren_dispatch': dispatch, 'ren_potential': potential, 'ren_curtailment': curtailment } if ext_found: specs['battery_p_series'] = stor_p_series_kW if pf_post_lopf: specs['battery_q_series'] = stor_q_series_kvar else: specs['battery_p_series'] = specs['battery_q_series'] = None if pf_post_lopf: specs['reactive_power'] = all_reactive_power t5 = time.perf_counter() performance.update({'Overall time': t5 - t0}) return specs