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