Source code for ding0.core.network.stations

"""This file is part of DING0, the DIstribution Network GeneratOr.
DING0 is a tool to generate synthetic medium and low voltage power
distribution grids based on open data.

It is developed in the project open_eGo: https://openegoproject.wordpress.com

DING0 lives at github: https://github.com/openego/ding0/
The documentation is available on RTD: http://ding0.readthedocs.io"""

__copyright__  = "Reiner Lemoine Institut gGmbH"
__license__    = "GNU Affero General Public License Version 3 (AGPL-3.0)"
__url__        = "https://github.com/openego/ding0/blob/master/LICENSE"
__author__     = "nesnoj, gplssm"


from . import StationDing0
from ding0.core.network import TransformerDing0
from ding0.tools import config as cfg_ding0

from itertools import compress
import numpy as np


[docs]class MVStationDing0(StationDing0): """ Defines a MV station in DINGO """ def __init__(self, **kwargs): super().__init__(**kwargs)
[docs] def peak_generation(self, mode): """Calculates cumulative peak generation of generators connected to underlying grids This is done instantaneously using bottom-up approach. Parameters ---------- mode: :obj:`str` determines which generators are included:: 'MV': Only generation capacities of MV level are considered. 'MVLV': Generation capacities of MV and LV are considered (= cumulative generation capacities in entire MVGD). Returns ------- float Cumulative peak generation """ if mode == 'MV': return sum([_.capacity for _ in self.grid.generators()]) elif mode == 'MVLV': # calc MV geno capacities cum_mv_peak_generation = sum([_.capacity for _ in self.grid.generators()]) # calc LV geno capacities cum_lv_peak_generation = 0 for load_area in self.grid.grid_district.lv_load_areas(): cum_lv_peak_generation += load_area.peak_generation return cum_mv_peak_generation + cum_lv_peak_generation else: raise ValueError('parameter \'mode\' is invalid!')
[docs] def set_operation_voltage_level(self): """Set operation voltage level """ mv_station_v_level_operation = float(cfg_ding0.get('mv_routing_tech_constraints', 'mv_station_v_level_operation')) self.v_level_operation = mv_station_v_level_operation * self.grid.v_level
[docs] def select_transformers(self): """ Selects appropriate transformers for the HV-MV substation. The transformers are chosen according to max. of load case and feedin-case considering load factors. The HV-MV transformer with the next higher available nominal apparent power is chosen. If one trafo is not sufficient, multiple trafos are used. Additionally, in a second step an redundant trafo is installed with max. capacity of the selected trafos of the first step according to general planning principles for MV distribution grids (n-1). Parameters ---------- transformers : dict Contains technical information of p hv/mv transformers **kwargs : dict Should contain a value behind the key 'peak_load' Note ----- Parametrization of transformers bases on [#]_. Potential hv-mv-transformers are chosen according to [#]_. References ---------- .. [#] Deutsche Energie-Agentur GmbH (dena), "dena-Verteilnetzstudie. Ausbau- und Innovationsbedarf der Stromverteilnetze in Deutschland bis 2030.", 2012 .. [#] X. Tao, "Automatisierte Grundsatzplanung von Mittelspannungsnetzen", Dissertation, 2006 """ # get power factor for loads and generators cos_phi_load = cfg_ding0.get('assumptions', 'cos_phi_load') cos_phi_feedin = cfg_ding0.get('assumptions', 'cos_phi_gen') # get trafo load factors load_factor_mv_trans_lc_normal = float(cfg_ding0.get('assumptions', 'load_factor_mv_trans_lc_normal')) load_factor_mv_trans_fc_normal = float(cfg_ding0.get('assumptions', 'load_factor_mv_trans_fc_normal')) # get equipment parameters of MV transformers trafo_parameters = self.grid.network.static_data['MV_trafos'] # get peak load and peak generation cum_peak_load = self.peak_load / cos_phi_load cum_peak_generation = self.peak_generation(mode='MVLV') / cos_phi_feedin # check if load or generation is greater respecting corresponding load factor if (cum_peak_load / load_factor_mv_trans_lc_normal) > \ (cum_peak_generation / load_factor_mv_trans_fc_normal): # use peak load and load factor from load case load_factor_mv_trans = load_factor_mv_trans_lc_normal residual_apparent_power = cum_peak_load else: # use peak generation and load factor for feedin case load_factor_mv_trans = load_factor_mv_trans_fc_normal residual_apparent_power = cum_peak_generation # determine number and size of required transformers # get max. trafo transformer_max = trafo_parameters.iloc[trafo_parameters['S_nom'].idxmax()] while residual_apparent_power > 0: if residual_apparent_power > load_factor_mv_trans * transformer_max['S_nom']: transformer = transformer_max else: # choose trafo transformer = trafo_parameters.iloc[ trafo_parameters[trafo_parameters['S_nom'] * load_factor_mv_trans > residual_apparent_power]['S_nom'].idxmin()] # add transformer on determined size with according parameters self.add_transformer(TransformerDing0(**{'grid': self.grid, 'v_level': self.grid.v_level, 's_max_longterm': transformer['S_nom'], 'id_db': len(list(self.transformers())) + 1})) # calc residual load residual_apparent_power -= (load_factor_mv_trans * transformer['S_nom']) # if no transformer was selected (no load in grid district), use smallest one if len(self._transformers) == 0: transformer = trafo_parameters.iloc[trafo_parameters['S_nom'].idxmin()] self.add_transformer( TransformerDing0(grid=self.grid, v_level=self.grid.v_level, s_max_longterm=transformer['S_nom'], id_db=1)) # add redundant transformer of the size of the largest transformer s_max_max = max((o.s_max_a for o in self._transformers)) self.add_transformer(TransformerDing0(**{'grid': self.grid, 'v_level': self.grid.v_level, 's_max_longterm': s_max_max, 'id_db': len(list(self.transformers())) + 1}))
@property def pypsa_bus_id(self): """ Returns specific ID for representing bus in pypsa network. Returns ------- :obj:`str`: Representative of pypsa bus """ return '_'.join(['Busbar', 'mvgd', str(self.grid.id_db), 'MV']) @property def pypsa_bus0_id(self): """ Returns specific ID for representing bus in pypsa network. Representative node at high voltage side (also used for transformer) Returns ------- :obj:`str`: Representative of pypsa bus """ return '_'.join(['Busbar', 'mvgd', str(self.grid.id_db), 'HV']) def __repr__(self): return '_'.join(['MVStation', 'mvgd', str(self.grid.id_db)])
[docs]class LVStationDing0(StationDing0): """ Defines a LV station in DINGO """ def __init__(self, **kwargs): super().__init__(**kwargs) self.lv_load_area = kwargs.get('lv_load_area', None) self.osm_id_node = kwargs.get('osm_id_node', None) # defined node in graph where station is located @property def peak_generation(self): """Calculates cumulative peak generation of generators connected to underlying LV grid. This is done instantaneously using bottom-up approach. Returns ------- float Cumulative peak generation """ return sum([_.capacity for _ in self.grid.generators()]) @property def pypsa_bus_id(self): """ Returns specific ID for representing bus in pypsa network. Returns ------- :obj:`str`: Representative of pypsa bus """ return '_'.join(['BusBar', 'mvgd', str( self.grid.grid_district.lv_load_area.mv_grid_district.mv_grid.\ id_db), 'lvgd', str(self.grid.id_db), 'LV']) @property def pypsa_bus0_id(self): """ Returns specific ID for representing bus in pypsa network. Representative node at medium voltage side (also used for transformer) Returns ------- :obj:`str`: Representative of pypsa bus """ return '_'.join(['BusBar', 'mvgd', str( self.grid.grid_district.lv_load_area.mv_grid_district.mv_grid. \ id_db), 'lvgd', str(self.grid.id_db), 'MV']) def __repr__(self): return '_'.join(['LVStation', 'mvgd', str( self.grid.grid_district.lv_load_area.mv_grid_district.mv_grid.\ id_db), 'lvgd', str(self.grid.id_db)])