Source code for ding0.flexopt.reinforce_measures

"""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"


# reinforcement measures according to Ackermann
import os
import ding0
import pandas as pd
from ding0.tools import config as cfg_ding0
from ding0.grid.lv_grid.build_grid import select_transformers
from ding0.core.network import TransformerDing0
from ding0.flexopt.check_tech_constraints import get_voltage_at_bus_bar
import networkx as nx
import logging

package_path = ding0.__path__[0]
logger = logging.getLogger(__name__)


[docs]def reinforce_branches_current(grid, crit_branches): #TODO: finish docstring """ Reinforce MV or LV grid by installing a new branch/line type Parameters ---------- grid : :class:`~.ding0.core.GridDing0` Grid identifier. crit_branches : dict Dict of critical branches with max. relative overloading. Note ----- The branch type to be installed is determined per branch using the rel. overloading. According to [#]_ only cables are installed. References ---------- .. [#] Ackermann et al. (RP VNS) See Also -------- ding0.flexopt.check_tech_constraints.check_load : ding0.flexopt.reinforce_measures.reinforce_branches_voltage : """ # load cable data, file_names and parameter branch_parameters = grid.network.static_data['MV_cables'] branch_parameters = branch_parameters[branch_parameters['U_n'] == grid.v_level].sort_values('I_max_th') branch_ctr = 0 for branch, rel_overload in crit_branches.items(): try: type = branch_parameters.loc[ branch_parameters[ branch_parameters['I_max_th'] >= branch .type['I_max_th'] * rel_overload ].loc[ :, 'I_max_th' ].idxmin(), : ] branch.type = type branch_ctr += 1 except: logger.warning('Branch {} could not be reinforced (current ' 'issues) as there is no appropriate cable type ' 'available. Original type is retained.'.format( branch)) pass if branch_ctr: logger.info('==> {} branches were reinforced.'.format(str(branch_ctr)))
[docs]def reinforce_branches_voltage(grid, crit_branches, grid_level='MV'): #TODO: finish docstring """ Reinforce MV or LV grid by installing a new branch/line type Parameters ---------- grid : :class:`~.ding0.core.GridDing0` Grid identifier. crit_branches : :obj:`list` of :obj:`int` List of critical branches. #TODO: check if a list or a dictionary grid_level : :obj:`str` Specifying either 'MV' for medium-voltage grid or 'LV' for low-voltage grid level. Note ----- The branch type to be installed is determined per branch - the next larger cable available is used. According to Ackermann only cables are installed. See Also -------- ding0.flexopt.check_tech_constraints.check_load : ding0.flexopt.reinforce_measures.reinforce_branches_voltage : """ # load cable data, file_names and parameter branch_parameters = grid.network.static_data['{gridlevel}_cables'.format( gridlevel=grid_level)] branch_parameters = branch_parameters[branch_parameters['U_n'] == grid.v_level].sort_values('I_max_th') branch_ctr = 0 for branch in crit_branches: try: type = branch_parameters.loc[ branch_parameters.loc[ branch_parameters['I_max_th'] > branch.type['I_max_th'] ].loc[ :, 'I_max_th' ].idxmin(), : ] branch.type = type branch_ctr += 1 except: logger.warning('Branch {} could not be reinforced (voltage ' 'issues) as there is no appropriate cable type ' 'available. Original type is retained.'.format( branch)) pass if branch_ctr: logger.info('==> {} branches were reinforced.'.format(str(branch_ctr)))
[docs]def extend_substation(grid, critical_stations, grid_level): """ Reinforce MV or LV substation by exchanging the existing trafo and installing a parallel one if necessary. First, all available transformers in a `critical_stations` are extended to maximum power. If this does not solve all present issues, additional transformers are build. Parameters ---------- grid: :class:`~.ding0.core.GridDing0` Ding0 grid container critical_stations : :obj:`list` List of stations with overloading grid_level : :obj:`str` Either "LV" or "MV". Basis to select right equipment. Note ----- Curently straight forward implemented for LV stations Returns ------- type #TODO: Description of return. Change type in the previous line accordingly """ load_factor_lv_trans_lc_normal = cfg_ding0.get( 'assumptions', 'load_factor_lv_trans_lc_normal') load_factor_lv_trans_fc_normal = cfg_ding0.get( 'assumptions', 'load_factor_lv_trans_fc_normal') trafo_params = grid.network._static_data['{grid_level}_trafos'.format( grid_level=grid_level)] trafo_s_max_max = max(trafo_params['S_nom']) v_nom = cfg_ding0.get('assumptions', 'lv_nominal_voltage') / 1e3 # v_nom in kV for station in critical_stations: # determine if load or generation case and apply load factor if station['s_max'][0] > station['s_max'][1]: case = 'load' lf_lv_trans_normal = load_factor_lv_trans_lc_normal else: case = 'gen' lf_lv_trans_normal = load_factor_lv_trans_fc_normal # cumulative maximum power of transformers installed s_max_trafos = sum([_.s_max_a for _ in station['station']._transformers]) # determine missing trafo power to solve overloading issue s_trafo_missing = max(station['s_max']) - ( s_max_trafos * lf_lv_trans_normal) # list of trafos with rated apparent power below `trafo_s_max_max` extendable_trafos = [_ for _ in station['station']._transformers if _.s_max_a < trafo_s_max_max] # try to extend power of existing trafos while (s_trafo_missing > 0) and extendable_trafos: # only work with first of potentially multiple trafos trafo = extendable_trafos[0] trafo_s_max_a_before = trafo.s_max_a # extend power of first trafo to next higher size available extend_trafo_power(extendable_trafos, trafo_params) # diminish missing trafo power by extended trafo power and update # extendable trafos list s_trafo_missing -= ((trafo.s_max_a * lf_lv_trans_normal) - trafo_s_max_a_before) extendable_trafos = [_ for _ in station['station']._transformers if _.s_max_a < trafo_s_max_max] # build new trafos inside station until if s_trafo_missing > 0: trafo_type, trafo_cnt = select_transformers(grid, s_max={ 's_max': s_trafo_missing, 'case': case }) # create transformers and add them to station of LVGD for t in range(0, trafo_cnt): lv_transformer = TransformerDing0( grid=grid, id_db=t + 1, v_level=v_nom, s_max_longterm=trafo_type['S_nom'], r_pu=trafo_type['r_pu'], x_pu=trafo_type['x_pu']) # add each transformer to its station grid._station.add_transformer(lv_transformer) logger.info("{stations_cnt} have been reinforced due to overloading " "issues.".format(stations_cnt=len(critical_stations)))
[docs]def extend_substation_voltage(crit_stations, grid_level='LV'): """ Extend substation if voltage issues at the substation occur Follows a two-step procedure: i) Existing transformers are extended by replacement with large nominal apparent power ii) New additional transformers added to substation (see 'Note') Parameters ---------- crit_stations : :obj:`list` List of stations with overloading or voltage issues. grid_level : :obj:`str` Specifiy grid level: 'MV' or 'LV' Note ----- At maximum 2 new of largest (currently 630 kVA) transformer are additionally built to resolve voltage issues at MV-LV substation bus bar. """ v_nom = cfg_ding0.get('assumptions', 'lv_nominal_voltage') / 1e3 # v_nom in kV grid = crit_stations[0]['node'].grid trafo_params = grid.network._static_data['{grid_level}_trafos'.format( grid_level=grid_level)] trafo_s_max_max = max(trafo_params['S_nom']) trafo_min_size = trafo_params.loc[trafo_params['S_nom'].idxmin(), :] v_diff_max_fc = cfg_ding0.get('assumptions', 'lv_max_v_level_fc_diff_normal') v_diff_max_lc = cfg_ding0.get('assumptions', 'lv_max_v_level_lc_diff_normal') tree = nx.dfs_tree(grid.graph, grid._station) for station in crit_stations: v_delta = max(station['v_diff']) # get list of nodes of main branch in right order extendable_trafos = [_ for _ in station['node']._transformers if _.s_max_a < trafo_s_max_max] v_delta_initially_lc = v_delta[0] v_delta_initially_fc = v_delta[1] new_transformers_cnt = 0 # extend existing trafo power while voltage issues exist and larger trafos # are available while (v_delta[0] > v_diff_max_lc) or (v_delta[1] > v_diff_max_fc): if extendable_trafos: # extend power of first trafo to next higher size available extend_trafo_power(extendable_trafos, trafo_params) elif new_transformers_cnt < 2: # build a new transformer lv_transformer = TransformerDing0( grid=grid, id_db=len(list(station.transformers())) + 1, v_level=v_nom, s_max_longterm=trafo_min_size['S_nom'], r_pu=trafo_min_size['r_pu'], x_pu=trafo_min_size['x_pu']) # add each transformer to its station grid._station.add_transformer(lv_transformer) new_transformers_cnt += 1 # update break criteria v_delta = get_voltage_at_bus_bar(grid, tree) extendable_trafos = [_ for _ in station['node']._transformers if _.s_max_a < trafo_s_max_max] if (v_delta[0] == v_delta_initially_lc) or ( v_delta[1] == v_delta_initially_fc): logger.warning("Extension of {station} has no effect on " "voltage delta at bus bar. Transformation power " "extension is halted.".format( station=station['node'])) break if (v_delta[0] > v_diff_max_lc) or (v_delta[1] > v_diff_max_fc): raise Exception ('Voltage issue at substation {} could not be resolved. LV grid reinforcement' 'can therefore never result in correct grids. Check MV grid for line reinforcement.'.format(station))
[docs]def new_substation(grid): """ Reinforce MV grid by installing a new primary substation opposite to the existing one Parameters ---------- grid : :class:`~.ding0.core.network.grids.MVGridDing0` MV Grid identifier. """
[docs]def reinforce_lv_branches_overloading(grid, crit_branches): """ Choose appropriate cable type for branches with line overloading Parameters ---------- grid : :class:`~.ding0.core.network.grids.LVGridDing0` Ding0 LV grid object crit_branches : :obj:`list` List of critical branches incl. its line loading Note ----- If maximum size cable is not capable to resolve issue due to line overloading largest available cable type is assigned to branch. Returns ------- :obj:`list` unsolved_branches : List of braches no suitable cable could be found """ unsolved_branches = [] cable_lf = cfg_ding0.get('assumptions', 'load_factor_lv_cable_lc_normal') cables = grid.network.static_data['LV_cables'] # resolve overloading issues for each branch segment for branch in crit_branches: I_max_branch_load = branch['s_max'][0]/(3**0.5 * grid.v_level / 1e3) I_max_branch_gen = branch['s_max'][1]/(3**0.5 * grid.v_level / 1e3) I_max_branch = max([I_max_branch_load, I_max_branch_gen]) suitable_cables = cables[(cables['I_max_th'] * cable_lf) > I_max_branch] if not suitable_cables.empty: cable_type = suitable_cables.loc[suitable_cables['I_max_th'].idxmin(), :] branch['branch'].type = cable_type crit_branches.remove(branch) else: cable_type_max = cables.loc[cables['I_max_th'].idxmax(), :] unsolved_branches.append(branch) branch['branch'].type = cable_type_max logger.error("No suitable cable type could be found for {branch} " "with I_th_max = {current}. " "Cable of type {cable} is chosen during " "reinforcement.".format( branch=branch['branch'], cable=cable_type_max.name, current=I_max_branch )) return unsolved_branches
[docs]def extend_trafo_power(extendable_trafos, trafo_params): """ Extend power of first trafo in list of extendable trafos Parameters ---------- extendable_trafos : :obj:`list` Trafos with rated power below maximum size available trafo trafo_params : :pandas:`pandas.DataFrame<dataframe>` Transformer parameters """ trafo = extendable_trafos[0] trafo_s_max_a_before = trafo.s_max_a trafo_nearest_larger = trafo_params.loc[ trafo_params.loc[ trafo_params['S_nom'] > trafo_s_max_a_before ].loc[ :, 'S_nom' ].idxmin(), : ] trafo.s_max_a = trafo_nearest_larger['S_nom'] trafo.r_pu = trafo_nearest_larger['r_pu'] trafo.x_pu = trafo_nearest_larger['x_pu']