mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-10-30 01:32:21 +00:00
Restrictions on auto-adding amplifiers into ROADMs
This feature is intended to support designs such as OpenROADM where the line degree integrates a specific preamp/booster pair. In that case, it does not make sense for our autodesign to "pick an amplifier". The restrictions can be activated by: - Listing them in `eqpt_config.json`, so that they are effective for all ROADM instances. - On a per-ROADM basis within the Excel sheet or the JSON definitions. Restrictions apply to an entire ROADM as a whole, not to the individual degrees. If a per-degree exception is needed, the amplifier of this degree can be defined in the equipment sheet or in the network definition. If no booster amplifier should be placed on a degree, use the `Fused` node in place of an amplifier. Signed-off-by: Esther Le Rouzic <esther.lerouzic@orange.com> Co-authored-by: Jan Kundrát <jan.kundrat@telecominfraproject.com>
This commit is contained in:
committed by
Jan Kundrát
parent
22acd88d44
commit
d94dc51d88
@@ -19,8 +19,8 @@ In order to work the excel file MUST contain at least 2 sheets:
|
||||
Nodes sheet
|
||||
-----------
|
||||
|
||||
Nodes sheet contains seven columns.
|
||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
|
||||
Nodes sheet contains nine columns.
|
||||
Each line represents a 'node' (ROADM site or an in line amplifier site ILA or a Fused)::
|
||||
|
||||
City (Mandatory) ; State ; Country ; Region ; Latitude ; Longitude ; Type
|
||||
|
||||
@@ -38,6 +38,9 @@ Each line represents a 'node' (ROADM site or an in line amplifier site ILA)::
|
||||
|
||||
- *Longitude*, *Latitude* are not mandatory. If filled they should contain numbers.
|
||||
|
||||
- **Booster_restriction** and **Preamp_restriction** are not mandatory.
|
||||
If used, they must contain one or several amplifier type_variety names separated by ' | '. This information is used to restrict types of amplifiers used in a ROADM node during autodesign. If a ROADM booster or preamp is already specified in the Eqpt sheet , the field is ignored. The field is also ignored if the node is not a ROADM node.
|
||||
|
||||
**There MUST NOT be empty line(s) between two nodes lines**
|
||||
|
||||
|
||||
@@ -166,6 +169,7 @@ This generates a text file meshTopologyExampleV2_eqt_sheet.txt whose content ca
|
||||
- **amp type** is not mandatory.
|
||||
If filled it must contain types listed in `eqpt_config.json <examples/eqpt_config.json>`_ in "Edfa" list "type_variety".
|
||||
If not filled it takes "std_medium_gain" as default value.
|
||||
If filled with fused, a fused element with 0.0 dB loss will be placed instead of an amplifier. This might be used to avoid booster amplifier on a ROADM direction.
|
||||
|
||||
- **amp_gain** is not mandatory. It is the value to be set on the amplifier (in dB).
|
||||
If not filled, it will be determined with design rules in the convert.py file.
|
||||
|
||||
13
README.rst
13
README.rst
@@ -408,10 +408,15 @@ existing parameters:
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
| ``restrictions`` | (strings) | Authorized type_variety of amplifier for |
|
||||
| | | booster or preamp. |
|
||||
| | | Listed type_variety MUST be defined in the |
|
||||
| | | Edfa catalog. |
|
||||
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
|
||||
| | strings) | and ``booster_variety_list`` represent |
|
||||
| | | list of ``type_variety`` amplifiers which |
|
||||
| | | are allowed for auto-design within ROADM's |
|
||||
| | | line degrees. |
|
||||
| | | |
|
||||
| | | If no booster should be placed on a degree, |
|
||||
| | | insert a ``Fused`` node on the degree |
|
||||
| | | output. |
|
||||
+--------------------------+-----------+---------------------------------------------+
|
||||
|
||||
The ``SpectralInformation`` object can be configured as follows. The user can
|
||||
|
||||
@@ -177,8 +177,8 @@
|
||||
"target_pch_out_db": -20,
|
||||
"add_drop_osnr": 38,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":["low_gain_preamp", "high_gain_preamp"],
|
||||
"booster_variety_list":["std_booster"]
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
|
||||
@@ -681,25 +681,6 @@
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"operational": {
|
||||
"gain_target": null,
|
||||
"delta_p": 1.0,
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lannion_CAS to Stbrieuc",
|
||||
"metadata": {
|
||||
@@ -1041,6 +1022,21 @@
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in Lorient_KMA to Vannes_KBE",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "Lorient_KMA",
|
||||
"region": "RLD",
|
||||
"latitude": 2.0,
|
||||
"longitude": 3.0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
|
||||
Binary file not shown.
@@ -31,6 +31,7 @@ from itertools import chain
|
||||
from json import dumps
|
||||
from pathlib import Path
|
||||
from difflib import get_close_matches
|
||||
from gnpy.core.utils import silent_remove
|
||||
import time
|
||||
|
||||
all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows))
|
||||
@@ -54,7 +55,9 @@ class Node(object):
|
||||
'region': '',
|
||||
'latitude': 0,
|
||||
'longitude': 0,
|
||||
'node_type': 'ILA'
|
||||
'node_type': 'ILA',
|
||||
'booster_restriction' : '',
|
||||
'preamp_restriction' : ''
|
||||
}
|
||||
|
||||
class Link(object):
|
||||
@@ -235,7 +238,6 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):
|
||||
|
||||
def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
nodes, links, eqpts = parse_excel(input_filename)
|
||||
|
||||
if filter_region:
|
||||
nodes = [n for n in nodes if n.region.lower() in filter_region]
|
||||
cities = {n.city for n in nodes}
|
||||
@@ -244,10 +246,8 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links}
|
||||
nodes = [n for n in nodes if n.city in cities]
|
||||
|
||||
|
||||
global nodes_by_city
|
||||
nodes_by_city = {n.city: n for n in nodes}
|
||||
|
||||
#create matching dictionary for node name mismatch analysis
|
||||
|
||||
cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes}
|
||||
@@ -298,7 +298,22 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] +
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' \
|
||||
and x.booster_restriction == '' and x.preamp_restriction == ''] +
|
||||
[{'uid': f'roadm {x.city}',
|
||||
'params' : {
|
||||
'restrictions': {
|
||||
'preamp_variety_list': silent_remove(x.preamp_restriction.split(' | '),''),
|
||||
'booster_variety_list': silent_remove(x.booster_restriction.split(' | '),'')
|
||||
}
|
||||
},
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
'latitude': x.latitude,
|
||||
'longitude': x.longitude}},
|
||||
'type': 'Roadm'}
|
||||
for x in nodes_by_city.values() if x.node_type.lower() == 'roadm' and \
|
||||
(x.booster_restriction != '' or x.preamp_restriction != '')] +
|
||||
[{'uid': f'west fused spans in {x.city}',
|
||||
'metadata': {'location': {'city': x.city,
|
||||
'region': x.region,
|
||||
@@ -348,8 +363,9 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
'delta_p': e.east_amp_dp,
|
||||
'tilt_target': e.east_tilt,
|
||||
'out_voa' : e.east_att_out}
|
||||
}
|
||||
for e in eqpts if e.east_amp_type.lower() != ''] +
|
||||
}
|
||||
for e in eqpts if (e.east_amp_type.lower() != '' and \
|
||||
e.east_amp_type.lower() != 'fused')] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
@@ -361,8 +377,31 @@ def convert_file(input_filename, names_matching=False, filter_region=[]):
|
||||
'delta_p': e.west_amp_dp,
|
||||
'tilt_target': e.west_tilt,
|
||||
'out_voa' : e.west_att_out}
|
||||
}
|
||||
for e in eqpts if e.west_amp_type.lower() != ''],
|
||||
}
|
||||
for e in eqpts if (e.west_amp_type.lower() != '' and \
|
||||
e.west_amp_type.lower() != 'fused')] +
|
||||
# fused edfa variety is a hack to indicate that there should not be
|
||||
# booster amplifier out the roadm.
|
||||
# If user specifies ILA in Nodes sheet and fused in Eqpt sheet, then assumes that
|
||||
# this is a fused nodes.
|
||||
[{'uid': f'east edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.east_amp_type.lower() == 'fused'] +
|
||||
[{'uid': f'west edfa in {e.from_city} to {e.to_city}',
|
||||
'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
|
||||
'region': nodes_by_city[e.from_city].region,
|
||||
'latitude': nodes_by_city[e.from_city].latitude,
|
||||
'longitude': nodes_by_city[e.from_city].longitude}},
|
||||
'type': 'Fused',
|
||||
'params': {'loss': 0}
|
||||
}
|
||||
for e in eqpts if e.west_amp_type.lower() == 'fused'],
|
||||
'connections':
|
||||
list(chain.from_iterable([eqpt_connection_by_city(n.city)
|
||||
for n in nodes]))
|
||||
@@ -414,7 +453,9 @@ def parse_excel(input_filename):
|
||||
'Region': 'region',
|
||||
'Latitude': 'latitude',
|
||||
'Longitude': 'longitude',
|
||||
'Type': 'node_type'
|
||||
'Type': 'node_type',
|
||||
'Booster_restriction': 'booster_restriction',
|
||||
'Preamp_restriction': 'preamp_restriction'
|
||||
}
|
||||
eqpt_headers = \
|
||||
{ 'Node A': 'from_city',
|
||||
@@ -571,7 +612,7 @@ def midpoint(city_a, city_b):
|
||||
#output_json_file_name = 'coronet_conus_example.json'
|
||||
#TODO get column size automatically from tupple size
|
||||
|
||||
NODES_COLUMN = 8
|
||||
NODES_COLUMN = 10
|
||||
NODES_LINE = 4
|
||||
LINKS_COLUMN = 16
|
||||
LINKS_LINE = 3
|
||||
|
||||
@@ -117,7 +117,7 @@ class Transceiver(Node):
|
||||
self._calc_snr(spectral_info)
|
||||
return spectral_info
|
||||
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr')
|
||||
RoadmParams = namedtuple('RoadmParams', 'target_pch_out_db add_drop_osnr restrictions')
|
||||
|
||||
class Roadm(Node):
|
||||
def __init__(self, *args, params, **kwargs):
|
||||
@@ -126,15 +126,19 @@ class Roadm(Node):
|
||||
self.effective_loss = None
|
||||
self.effective_pch_out_db = self.params.target_pch_out_db
|
||||
self.passive = True
|
||||
self.restrictions = self.params.restrictions
|
||||
|
||||
@property
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' : {'target_pch_out_db' : self.effective_pch_out_db},
|
||||
'params' : {
|
||||
'target_pch_out_db' : self.effective_pch_out_db,
|
||||
'restrictions' : self.restrictions
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
@@ -186,6 +190,9 @@ class Fused(Node):
|
||||
def to_json(self):
|
||||
return {'uid' : self.uid,
|
||||
'type' : type(self).__name__,
|
||||
'params' :{
|
||||
'loss': self.loss
|
||||
},
|
||||
'metadata' : {
|
||||
'location': self.metadata['location']._asdict()
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ Model_dual_stage = namedtuple('Model_dual_stage', 'preamp_variety booster_variet
|
||||
|
||||
class common:
|
||||
def update_attr(self, default_values, kwargs, name):
|
||||
clean_kwargs = {k:v for k,v in kwargs.items() if v !=''}
|
||||
for k,v in default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k,v))
|
||||
if k not in clean_kwargs and name != 'Amp' :
|
||||
clean_kwargs = {k:v for k, v in kwargs.items() if v != ''}
|
||||
for k, v in default_values.items():
|
||||
setattr(self, k, clean_kwargs.get(k, v))
|
||||
if k not in clean_kwargs and name != 'Amp':
|
||||
print(f'\x1b[1;31;40m'+
|
||||
f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'
|
||||
f'\n default value is {k} = {v}'
|
||||
+ '\x1b[0m')
|
||||
f'\n WARNING missing {k} attribute in eqpt_config.json[{name}]'+
|
||||
f'\n default value is {k} = {v}'+
|
||||
f'\x1b[0m')
|
||||
time.sleep(1)
|
||||
|
||||
class SI(common):
|
||||
@@ -42,13 +42,13 @@ class SI(common):
|
||||
{
|
||||
"f_min": 191.35e12,
|
||||
"f_max": 196.1e12,
|
||||
"baud_rate": 32e9,
|
||||
"baud_rate": 32e9,
|
||||
"spacing": 50e9,
|
||||
"power_dbm": 0,
|
||||
"power_range_db": [0,0,0.5],
|
||||
"power_range_db": [0, 0, 0.5],
|
||||
"roll_off": 0.15,
|
||||
"tx_osnr": 45,
|
||||
"sys_margins": 0
|
||||
"sys_margins": 0
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
@@ -69,7 +69,7 @@ class Span(common):
|
||||
'con_in': 0,
|
||||
'con_out': 0
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Span')
|
||||
|
||||
@@ -77,8 +77,12 @@ class Roadm(common):
|
||||
default_values = \
|
||||
{
|
||||
'target_pch_out_db': -17,
|
||||
'add_drop_osnr': 100
|
||||
}
|
||||
'add_drop_osnr': 100,
|
||||
'restrictions': {
|
||||
'preamp_variety_list':[],
|
||||
'booster_variety_list':[]
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
self.update_attr(self.default_values, kwargs, 'Roadm')
|
||||
@@ -134,7 +138,7 @@ class Amp(common):
|
||||
config = Path(filename).parent / 'default_edfa_config.json'
|
||||
|
||||
type_variety = kwargs['type_variety']
|
||||
type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files
|
||||
type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files
|
||||
nf_def = None
|
||||
dual_stage_def = None
|
||||
|
||||
@@ -180,7 +184,7 @@ class Amp(common):
|
||||
with open(config, encoding='utf-8') as f:
|
||||
json_data = load(f)
|
||||
|
||||
return cls(**{**kwargs, **json_data,
|
||||
return cls(**{**kwargs, **json_data,
|
||||
'nf_model': nf_def, 'dual_stage_model': dual_stage_def})
|
||||
|
||||
|
||||
@@ -247,7 +251,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
|
||||
"""return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)"""
|
||||
trx_params = {}
|
||||
default_si_data = equipment['SI']['default']
|
||||
|
||||
|
||||
try:
|
||||
trxs = equipment['Transceiver']
|
||||
#if called from path_requests_run.py, trx_mode is filled with None when not specified by user
|
||||
@@ -264,14 +268,14 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
|
||||
f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.')
|
||||
else:
|
||||
mode_params = {"format": "undetermined",
|
||||
"baud_rate": None,
|
||||
"OSNR": None,
|
||||
"bit_rate": None,
|
||||
"roll_off": None,
|
||||
"tx_osnr":None,
|
||||
"min_spacing":None,
|
||||
"cost":None}
|
||||
trx_params = {**mode_params}
|
||||
"baud_rate": None,
|
||||
"OSNR": None,
|
||||
"bit_rate": None,
|
||||
"roll_off": None,
|
||||
"tx_osnr":None,
|
||||
"min_spacing":None,
|
||||
"cost":None}
|
||||
trx_params = {**mode_params}
|
||||
trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min']
|
||||
trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max']
|
||||
|
||||
@@ -298,7 +302,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
|
||||
nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing'])
|
||||
trx_params['nb_channel'] = nch
|
||||
print(f'There are {nch} channels propagating')
|
||||
|
||||
|
||||
trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3
|
||||
|
||||
return trx_params
|
||||
@@ -306,8 +310,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
|
||||
def automatic_spacing(baud_rate):
|
||||
"""return the min possible channel spacing for a given baud rate"""
|
||||
# TODO : this should parametrized in a cfg file
|
||||
spacing_list = [(33e9,37.5e9), (38e9,50e9), (50e9,62.5e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
|
||||
#[(max_baud_rate, spacing_for_this_baud_rate)]
|
||||
# list of possible tuples [(max_baud_rate, spacing_for_this_baud_rate)]
|
||||
spacing_list = [(33e9, 37.5e9), (38e9, 50e9), (50e9, 62.5e9), (67e9, 75e9), (92e9, 100e9)]
|
||||
return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2)
|
||||
|
||||
def automatic_nch(f_min, f_max, spacing):
|
||||
@@ -333,18 +337,28 @@ def update_dual_stage(equipment):
|
||||
if edfa.type_def == 'dual_stage':
|
||||
edfa_preamp = edfa_dict[edfa.dual_stage_model.preamp_variety]
|
||||
edfa_booster = edfa_dict[edfa.dual_stage_model.booster_variety]
|
||||
for k,v in edfa_preamp.__dict__.items():
|
||||
attr_k = 'preamp_'+k
|
||||
setattr(edfa, attr_k, v)
|
||||
for k,v in edfa_booster.__dict__.items():
|
||||
attr_k = 'booster_'+k
|
||||
setattr(edfa, attr_k, v)
|
||||
for key, value in edfa_preamp.__dict__.items():
|
||||
attr_k = 'preamp_' + key
|
||||
setattr(edfa, attr_k, value)
|
||||
for key, value in edfa_booster.__dict__.items():
|
||||
attr_k = 'booster_' + key
|
||||
setattr(edfa, attr_k, value)
|
||||
edfa.p_max = edfa_booster.p_max
|
||||
edfa.gain_flatmax = edfa_booster.gain_flatmax + edfa_preamp.gain_flatmax
|
||||
if edfa.gain_min < edfa_preamp.gain_min:
|
||||
raise EquipmentConfigError(f'Dual stage {edfa.type_variety} min gain is lower than its preamp min gain')
|
||||
return equipment
|
||||
|
||||
def roadm_restrictions_sanity_check(equipment):
|
||||
""" verifies that booster and preamp restrictions specified in roadm equipment are listed
|
||||
in the edfa.
|
||||
"""
|
||||
restrictions = equipment['Roadm']['default'].restrictions['booster_variety_list'] + \
|
||||
equipment['Roadm']['default'].restrictions['preamp_variety_list']
|
||||
for amp_name in restrictions:
|
||||
if amp_name not in equipment['Edfa']:
|
||||
raise EquipmentConfigError(f'ROADM restriction {amp_name} does not refer to a defined EDFA name')
|
||||
|
||||
def equipment_from_json(json_data, filename):
|
||||
"""build global dictionnary eqpt_library that stores all eqpt characteristics:
|
||||
edfa type type_variety, fiber type_variety
|
||||
@@ -359,11 +373,12 @@ def equipment_from_json(json_data, filename):
|
||||
equipment[key] = {}
|
||||
typ = globals()[key]
|
||||
for entry in entries:
|
||||
subkey = entry.get('type_variety', 'default')
|
||||
subkey = entry.get('type_variety', 'default')
|
||||
if key == 'Edfa':
|
||||
equipment[key][subkey] = Amp.from_json(filename, **entry)
|
||||
else:
|
||||
else:
|
||||
equipment[key][subkey] = typ(**entry)
|
||||
equipment = update_trx_osnr(equipment)
|
||||
equipment = update_dual_stage(equipment)
|
||||
roadm_restrictions_sanity_check(equipment)
|
||||
return equipment
|
||||
|
||||
@@ -20,7 +20,8 @@ from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused
|
||||
from gnpy.core.equipment import edfa_nf
|
||||
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
|
||||
from gnpy.core.units import UNITS
|
||||
from gnpy.core.utils import load_json, save_json, round2float, db2lin, lin2db
|
||||
from gnpy.core.utils import (load_json, save_json, round2float, db2lin,
|
||||
merge_amplifier_restrictions)
|
||||
from collections import namedtuple
|
||||
|
||||
logger = getLogger(__name__)
|
||||
@@ -50,10 +51,12 @@ def network_from_json(json_data, equipment):
|
||||
for el_config in json_data['elements']:
|
||||
typ = el_config.pop('type')
|
||||
variety = el_config.pop('type_variety', 'default')
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
if typ in equipment and variety in equipment[typ]:
|
||||
extra_params = equipment[typ][variety]
|
||||
el_config.setdefault('params', {}).update(extra_params.__dict__)
|
||||
elif typ in ['Edfa', 'Fiber']: #catch it now because the code will crash later!
|
||||
temp = el_config.setdefault('params', {})
|
||||
temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
|
||||
el_config['params'] = temp
|
||||
elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later!
|
||||
raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
|
||||
'\nplease check it is properly defined in the eqpt_config json file')
|
||||
cls = getattr(elements, typ)
|
||||
@@ -65,10 +68,8 @@ def network_from_json(json_data, equipment):
|
||||
for cx in json_data['connections']:
|
||||
from_node, to_node = cx['from_node'], cx['to_node']
|
||||
try:
|
||||
if isinstance(nodes[from_node], Fiber):
|
||||
if isinstance(nodes[from_node], Fiber):
|
||||
edge_length = nodes[from_node].params.length
|
||||
# print(from_node)
|
||||
# print(edge_length)
|
||||
else:
|
||||
edge_length = 0.01
|
||||
g.add_edge(nodes[from_node], nodes[to_node], weight = edge_length)
|
||||
@@ -90,21 +91,27 @@ def network_to_json(network):
|
||||
data.update(connections)
|
||||
return data
|
||||
|
||||
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid):
|
||||
def select_edfa(raman_allowed, gain_target, power_target, equipment, uid, restrictions=None):
|
||||
"""amplifer selection algorithm
|
||||
@Orange Jean-Luc Augé
|
||||
"""
|
||||
Edfa_list = namedtuple('Edfa_list', 'variety power gain_min nf')
|
||||
TARGET_EXTENDED_GAIN = equipment['Span']['default'].target_extended_gain
|
||||
edfa_dict = equipment['Edfa']
|
||||
|
||||
# for roadm restriction only: create a dict including not allowed for design amps
|
||||
# because main use case is to have specific radm amp which are not allowed for ILA
|
||||
# with the auto design
|
||||
edfa_dict = {name: amp for (name, amp) in equipment['Edfa'].items()
|
||||
if restrictions is None or name in restrictions}
|
||||
|
||||
pin = power_target - gain_target
|
||||
|
||||
#create 2 list of available amplifiers with relevant attributs for their selection
|
||||
# create 2 list of available amplifiers with relevant attributes for their selection
|
||||
|
||||
#edfa list with :
|
||||
#extended gain min allowance of 3dB: could be parametrized, but a bit complex
|
||||
#extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json
|
||||
#power attribut include power AND gain limitations
|
||||
# edfa list with:
|
||||
# extended gain min allowance of 3dB: could be parametrized, but a bit complex
|
||||
# extended gain max allowance TARGET_EXTENDED_GAIN is coming from eqpt_config.json
|
||||
# power attribut include power AND gain limitations
|
||||
edfa_list = [Edfa_list(
|
||||
variety=edfa_variety,
|
||||
power=min(
|
||||
@@ -119,7 +126,7 @@ def select_edfa(raman_allowed, gain_target, power_target, equipment, uid):
|
||||
-edfa.gain_min,
|
||||
nf=edfa_nf(gain_target, edfa_variety, equipment)) \
|
||||
for edfa_variety, edfa in edfa_dict.items()
|
||||
if (edfa.allowed_for_design and not edfa.raman)]
|
||||
if ((edfa.allowed_for_design or restrictions is not None) and not edfa.raman)]
|
||||
|
||||
#consider a Raman list because of different gain_min requirement:
|
||||
#do not allow extended gain min for Raman
|
||||
@@ -326,7 +333,7 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
else: #gain mode with effective_gain
|
||||
gain_target = node.effective_gain
|
||||
dp = prev_dp - node_loss + gain_target
|
||||
#print(node.delta_p, dp, gain_target)
|
||||
|
||||
power_target = pref_total_db + dp
|
||||
|
||||
raman_allowed = False
|
||||
@@ -335,9 +342,24 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
equipment['Span']['default'].max_fiber_lineic_loss_for_raman
|
||||
raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
|
||||
|
||||
if node.params.type_variety == '' :
|
||||
# implementation of restrictions on roadm boosters
|
||||
if isinstance(prev_node,Roadm):
|
||||
if prev_node.restrictions['booster_variety_list']:
|
||||
restrictions = prev_node.restrictions['booster_variety_list']
|
||||
else:
|
||||
restrictions = None
|
||||
elif isinstance(next_node,Roadm):
|
||||
# implementation of restrictions on roadm preamp
|
||||
if next_node.restrictions['preamp_variety_list']:
|
||||
restrictions = next_node.restrictions['preamp_variety_list']
|
||||
else:
|
||||
restrictions = None
|
||||
else:
|
||||
restrictions = None
|
||||
|
||||
if node.params.type_variety == '':
|
||||
edfa_variety, power_reduction = select_edfa(raman_allowed,
|
||||
gain_target, power_target, equipment, node.uid)
|
||||
gain_target, power_target, equipment, node.uid, restrictions)
|
||||
extra_params = equipment['Edfa'][edfa_variety]
|
||||
node.params.update_params(extra_params.__dict__)
|
||||
dp += power_reduction
|
||||
@@ -351,7 +373,7 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
|
||||
)
|
||||
|
||||
node.delta_p = dp if power_mode else None
|
||||
node.effective_gain = gain_target
|
||||
node.effective_gain = gain_target
|
||||
set_amplifier_voa(node, power_target, power_mode)
|
||||
if isinstance(next_node, Roadm) or isinstance(next_node, Transceiver):
|
||||
break
|
||||
@@ -484,10 +506,13 @@ def add_fiber_padding(network, fibers, padding):
|
||||
#add a padding att_in at the input of the 1st fiber:
|
||||
#address the case when several fibers are spliced together
|
||||
first_fiber = find_first_node(network, fiber)
|
||||
if first_fiber.att_in is None:
|
||||
first_fiber.att_in = padding - this_span_loss
|
||||
else :
|
||||
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
|
||||
# in order to support no booster , fused might be placed
|
||||
# just after a roadm: need to check that first_fiber is really a fiber
|
||||
if isinstance(first_fiber,Fiber):
|
||||
if first_fiber.att_in is None:
|
||||
first_fiber.att_in = padding - this_span_loss
|
||||
else:
|
||||
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
|
||||
|
||||
def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
default_span_data = equipment['Span']['default']
|
||||
@@ -510,6 +535,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
|
||||
amplified_nodes = [n for n in network.nodes()
|
||||
if isinstance(n, Fiber) or isinstance(n, Roadm)]
|
||||
|
||||
for node in amplified_nodes:
|
||||
add_egress_amplifier(network, node)
|
||||
|
||||
@@ -522,4 +548,3 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
|
||||
trx = [t for t in network.nodes() if isinstance(t, Transceiver)]
|
||||
for t in trx:
|
||||
set_egress_amplifier(network, t, equipment, pref_total_db)
|
||||
|
||||
|
||||
@@ -205,6 +205,36 @@
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Att_B",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Att_F",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
],
|
||||
@@ -247,6 +277,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa5",
|
||||
"to_node": "Att_F"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_F",
|
||||
"to_node": "trx F"
|
||||
},
|
||||
{
|
||||
@@ -255,6 +289,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1",
|
||||
"to_node": "Att_B"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_B",
|
||||
"to_node": "trx B"
|
||||
}
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,7 +49,16 @@
|
||||
"p_max": 21,
|
||||
"nf0": 5,
|
||||
"allowed_for_design": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"type_variety": "std_booster",
|
||||
"type_def": "fixed_gain",
|
||||
"gain_flatmax": 21,
|
||||
"gain_min": 20,
|
||||
"p_max": 21,
|
||||
"nf0": 5,
|
||||
"allowed_for_design": false
|
||||
}
|
||||
],
|
||||
"Fiber":[{
|
||||
"type_variety": "SSMF",
|
||||
@@ -75,8 +84,8 @@
|
||||
"target_pch_out_db": -20,
|
||||
"add_drop_osnr": 38,
|
||||
"restrictions": {
|
||||
"preamp_variety_list":["low_gain_preamp", "high_gain_preamp"],
|
||||
"booster_variety_list":["std_booster"]
|
||||
"preamp_variety_list":[],
|
||||
"booster_variety_list":[]
|
||||
}
|
||||
}],
|
||||
"SI":[{
|
||||
|
||||
Binary file not shown.
@@ -160,7 +160,11 @@
|
||||
"uid": "roadm Lannion_CAS",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -175,7 +179,11 @@
|
||||
"uid": "roadm Lorient_KMA",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -190,7 +198,11 @@
|
||||
"uid": "roadm Vannes_KBE",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -205,7 +217,11 @@
|
||||
"uid": "roadm Rennes_STA",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -220,7 +236,11 @@
|
||||
"uid": "roadm Brest_KLA",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -235,7 +255,13 @@
|
||||
"uid": "roadm a",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": [
|
||||
"std_booster"
|
||||
]
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -250,7 +276,13 @@
|
||||
"uid": "roadm b",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [
|
||||
"std_low_gain"
|
||||
],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -265,7 +297,11 @@
|
||||
"uid": "roadm c",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -280,7 +316,11 @@
|
||||
"uid": "roadm d",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -295,7 +335,11 @@
|
||||
"uid": "roadm e",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -310,7 +354,11 @@
|
||||
"uid": "roadm f",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -325,7 +373,11 @@
|
||||
"uid": "roadm g",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -340,7 +392,11 @@
|
||||
"uid": "roadm h",
|
||||
"type": "Roadm",
|
||||
"params": {
|
||||
"target_pch_out_db": -20
|
||||
"target_pch_out_db": -20,
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
@@ -354,6 +410,9 @@
|
||||
{
|
||||
"uid": "west fused spans in Corlay",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
@@ -366,6 +425,9 @@
|
||||
{
|
||||
"uid": "west fused spans in Loudeac",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
@@ -378,6 +440,9 @@
|
||||
{
|
||||
"uid": "west fused spans in Morlaix",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.0,
|
||||
@@ -390,6 +455,9 @@
|
||||
{
|
||||
"uid": "east fused spans in Corlay",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
@@ -402,6 +470,9 @@
|
||||
{
|
||||
"uid": "east fused spans in Loudeac",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
@@ -414,6 +485,9 @@
|
||||
{
|
||||
"uid": "east fused spans in Morlaix",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 1
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 1.0,
|
||||
@@ -737,7 +811,7 @@
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 50.0,
|
||||
"length": 10.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
@@ -1199,10 +1273,10 @@
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"type_variety": "SSMF",
|
||||
"length": 50.0,
|
||||
"length": 10.0,
|
||||
"loss_coef": 0.2,
|
||||
"length_units": "km",
|
||||
"att_in": 0,
|
||||
"att_in": 8.0,
|
||||
"con_in": 0,
|
||||
"con_out": 0
|
||||
},
|
||||
@@ -1499,12 +1573,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in c to d",
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.0,
|
||||
"longitude": 1.0,
|
||||
"city": "c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa0_roadm Lorient_KMA",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"gain_target": 20.0,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
@@ -1523,7 +1612,7 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"gain_target": 20.0,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
@@ -1654,7 +1743,7 @@
|
||||
{
|
||||
"uid": "Edfa0_roadm a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"type_variety": "std_booster",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"delta_p": null,
|
||||
@@ -1673,7 +1762,7 @@
|
||||
{
|
||||
"uid": "Edfa1_roadm a",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"type_variety": "std_booster",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"delta_p": null,
|
||||
@@ -1732,7 +1821,7 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"gain_target": 22,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
@@ -1751,26 +1840,7 @@
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 6.0,
|
||||
"longitude": 1.75,
|
||||
"city": "c",
|
||||
"region": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Edfa2_roadm c",
|
||||
"type": "Edfa",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 20,
|
||||
"gain_target": 22,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
@@ -2186,9 +2256,9 @@
|
||||
{
|
||||
"uid": "Edfa0_fiber (c → d)-",
|
||||
"type": "Edfa",
|
||||
"type_variety": "std_low_gain",
|
||||
"type_variety": "test_fixed_gain",
|
||||
"operational": {
|
||||
"gain_target": 10.0,
|
||||
"gain_target": 22.0,
|
||||
"delta_p": null,
|
||||
"tilt_target": 0,
|
||||
"out_voa": 0
|
||||
@@ -2723,6 +2793,10 @@
|
||||
"from_node": "roadm Brest_KLA",
|
||||
"to_node": "Edfa0_roadm Brest_KLA"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm c",
|
||||
"to_node": "east edfa in c to d"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm a",
|
||||
"to_node": "trx a"
|
||||
@@ -2759,10 +2833,6 @@
|
||||
"from_node": "roadm c",
|
||||
"to_node": "Edfa1_roadm c"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm c",
|
||||
"to_node": "Edfa2_roadm c"
|
||||
},
|
||||
{
|
||||
"from_node": "roadm d",
|
||||
"to_node": "trx d"
|
||||
@@ -3051,6 +3121,10 @@
|
||||
"from_node": "west edfa in Lannion_CAS to Morlaix",
|
||||
"to_node": "roadm Lannion_CAS"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in c to d",
|
||||
"to_node": "fiber (c → d)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa0_roadm Lorient_KMA",
|
||||
"to_node": "fiber (Lorient_KMA → Loudeac)-F054"
|
||||
@@ -3105,10 +3179,6 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa1_roadm c",
|
||||
"to_node": "fiber (c → d)-"
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa2_roadm c",
|
||||
"to_node": "fiber (c → f)-"
|
||||
},
|
||||
{
|
||||
@@ -3284,4 +3354,4 @@
|
||||
"to_node": "roadm h"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,6 +218,14 @@
|
||||
},
|
||||
{
|
||||
"uid": "roadm a",
|
||||
"params": {
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [],
|
||||
"booster_variety_list": [
|
||||
"std_booster"
|
||||
]
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "a",
|
||||
@@ -230,6 +238,14 @@
|
||||
},
|
||||
{
|
||||
"uid": "roadm b",
|
||||
"params": {
|
||||
"restrictions": {
|
||||
"preamp_variety_list": [
|
||||
"std_low_gain"
|
||||
],
|
||||
"booster_variety_list": []
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "b",
|
||||
@@ -647,7 +663,7 @@
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
@@ -1025,7 +1041,7 @@
|
||||
"type": "Fiber",
|
||||
"type_variety": "SSMF",
|
||||
"params": {
|
||||
"length": 50.0,
|
||||
"length": 10.0,
|
||||
"length_units": "km",
|
||||
"loss_coef": 0.2,
|
||||
"con_in": null,
|
||||
@@ -1291,6 +1307,21 @@
|
||||
"tilt_target": 0,
|
||||
"out_voa": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "east edfa in c to d",
|
||||
"metadata": {
|
||||
"location": {
|
||||
"city": "c",
|
||||
"region": "",
|
||||
"latitude": 6.0,
|
||||
"longitude": 1.0
|
||||
}
|
||||
},
|
||||
"type": "Fused",
|
||||
"params": {
|
||||
"loss": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"connections": [
|
||||
@@ -1536,6 +1567,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "roadm c",
|
||||
"to_node": "east edfa in c to d"
|
||||
},
|
||||
{
|
||||
"from_node": "east edfa in c to d",
|
||||
"to_node": "fiber (c → d)-"
|
||||
},
|
||||
{
|
||||
@@ -1743,4 +1778,4 @@
|
||||
"to_node": "trx h"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,22 @@
|
||||
"longitude": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
"uid": "Att_B",
|
||||
"type": "Fused",
|
||||
"params":{
|
||||
"loss":16
|
||||
},
|
||||
"metadata": {
|
||||
"location": {
|
||||
"latitude": 2.0,
|
||||
"longitude": 1.0,
|
||||
"city": "Corlay",
|
||||
"region": "RLD"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"uid": "Site_B",
|
||||
"type": "Transceiver",
|
||||
@@ -110,6 +125,10 @@
|
||||
},
|
||||
{
|
||||
"from_node": "Edfa2",
|
||||
"to_node": "Att_B"
|
||||
},
|
||||
{
|
||||
"from_node": "Att_B",
|
||||
"to_node": "Site_B"
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ from numpy import mean
|
||||
|
||||
#network_file_name = 'tests/test_network.json'
|
||||
network_file_name = Path(__file__).parent.parent / 'tests/LinkforTest.json'
|
||||
#TODO: note that this json entries has a weird topology since EDfa1 has a possible branch on a receiver B
|
||||
# this might not pass future tests/ code updates
|
||||
#network_file_name = Path(__file__).parent.parent / 'examples/edfa_example_network.json'
|
||||
eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json'
|
||||
|
||||
|
||||
203
tests/test_roadm_restrictions.py
Normal file
203
tests/test_roadm_restrictions.py
Normal file
@@ -0,0 +1,203 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# @Author: Esther Le Rouzic
|
||||
# @Date: 2019-05-22
|
||||
"""
|
||||
@author: esther.lerouzic
|
||||
checks that fused placed in amp type is correctly converted to a fused element instead of an edfa
|
||||
and that no additional amp is added.
|
||||
checks that restrictions in roadms are correctly applied during autodesign
|
||||
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
from gnpy.core.utils import lin2db, load_json
|
||||
from gnpy.core.elements import Fused, Roadm, Edfa
|
||||
from gnpy.core.equipment import load_equipment, Amp, automatic_nch
|
||||
from gnpy.core.network import network_from_json, build_network
|
||||
|
||||
|
||||
TEST_DIR = Path(__file__).parent
|
||||
EQPT_LIBRARY_NAME = TEST_DIR / 'data/eqpt_config.json'
|
||||
NETWORK_FILE_NAME = TEST_DIR / 'data/testTopology_expected.json'
|
||||
# adding tests to check the roadm restrictions
|
||||
|
||||
# mark node_uid amps as fused for testing purpose
|
||||
@pytest.mark.parametrize("node_uid", ['east edfa in Lannion_CAS to Stbrieuc'])
|
||||
def test_no_amp_feature(node_uid):
|
||||
''' Check that booster is not placed on a roadm if fused is specified
|
||||
test_parser covers partly this behaviour. This test should guaranty that the
|
||||
feature is preserved even if convert is changed
|
||||
'''
|
||||
equipment = load_equipment(EQPT_LIBRARY_NAME)
|
||||
json_network = load_json(NETWORK_FILE_NAME)
|
||||
|
||||
for elem in json_network['elements']:
|
||||
if elem['uid'] == node_uid:
|
||||
#replace edfa node by a fused node in the topology
|
||||
elem['type'] = 'Fused'
|
||||
elem.pop('type_variety')
|
||||
elem.pop('operational')
|
||||
elem['params'] = {'loss': 0}
|
||||
|
||||
next_node_uid = next(conn['to_node'] for conn in json_network['connections'] \
|
||||
if conn['from_node'] == node_uid)
|
||||
previous_node_uid = next(conn['from_node'] for conn in json_network['connections'] \
|
||||
if conn['to_node'] == node_uid)
|
||||
|
||||
network = network_from_json(json_network, equipment)
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
|
||||
node = next(nd for nd in network.nodes() if nd.uid == node_uid)
|
||||
next_node = next(network.successors(node))
|
||||
previous_node = next(network.predecessors(node))
|
||||
|
||||
if not isinstance(node, Fused):
|
||||
raise AssertionError()
|
||||
if not node.params.loss == 0.0:
|
||||
raise AssertionError()
|
||||
if not next_node_uid == next_node.uid:
|
||||
raise AssertionError()
|
||||
if not previous_node_uid == previous_node.uid:
|
||||
raise AssertionError()
|
||||
|
||||
@pytest.fixture()
|
||||
def equipment():
|
||||
"""init transceiver class to access snr and osnr calculations"""
|
||||
equipment = load_equipment(EQPT_LIBRARY_NAME)
|
||||
# define some booster and preamps
|
||||
restrictions_list = [
|
||||
{
|
||||
'type_variety': 'booster_medium_gain',
|
||||
'type_def': 'variable_gain',
|
||||
'gain_flatmax': 25,
|
||||
'gain_min': 15,
|
||||
'p_max': 21,
|
||||
'nf_min': 5.8,
|
||||
'nf_max': 10,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False
|
||||
},
|
||||
{
|
||||
'type_variety': 'preamp_medium_gain',
|
||||
'type_def': 'variable_gain',
|
||||
'gain_flatmax': 26,
|
||||
'gain_min': 15,
|
||||
'p_max': 23,
|
||||
'nf_min': 6,
|
||||
'nf_max': 10,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False
|
||||
},
|
||||
{
|
||||
'type_variety': 'preamp_high_gain',
|
||||
'type_def': 'variable_gain',
|
||||
'gain_flatmax': 35,
|
||||
'gain_min': 25,
|
||||
'p_max': 21,
|
||||
'nf_min': 5.5,
|
||||
'nf_max': 7,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False
|
||||
},
|
||||
{
|
||||
'type_variety': 'preamp_low_gain',
|
||||
'type_def': 'variable_gain',
|
||||
'gain_flatmax': 16,
|
||||
'gain_min': 8,
|
||||
'p_max': 23,
|
||||
'nf_min': 6.5,
|
||||
'nf_max': 11,
|
||||
'out_voa_auto': False,
|
||||
'allowed_for_design': False
|
||||
}]
|
||||
# add them to the library
|
||||
for entry in restrictions_list:
|
||||
equipment['Edfa'][entry['type_variety']] = Amp.from_json(EQPT_LIBRARY_NAME, **entry)
|
||||
return equipment
|
||||
|
||||
|
||||
@pytest.mark.parametrize("restrictions", [
|
||||
{
|
||||
'preamp_variety_list':[],
|
||||
'booster_variety_list':[]
|
||||
},
|
||||
{
|
||||
'preamp_variety_list':[],
|
||||
'booster_variety_list':['booster_medium_gain']
|
||||
},
|
||||
{
|
||||
'preamp_variety_list':['preamp_medium_gain', 'preamp_high_gain', 'preamp_low_gain'],
|
||||
'booster_variety_list':[]
|
||||
}])
|
||||
def test_restrictions(restrictions, equipment):
|
||||
''' test that restriction is correctly applied if provided in eqpt_config and if no Edfa type
|
||||
were provided in the network json
|
||||
'''
|
||||
# add restrictions
|
||||
equipment['Roadm']['default'].restrictions = restrictions
|
||||
# build network
|
||||
json_network = load_json(NETWORK_FILE_NAME)
|
||||
network = network_from_json(json_network, equipment)
|
||||
|
||||
amp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)]
|
||||
preamp_nodes_nobuild_uid = [nd.uid for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)]
|
||||
amp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)}
|
||||
preamp_nodes_nobuild = {nd.uid : nd for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)}
|
||||
# roadm dict with restrictions before build
|
||||
roadms = {nd.uid: nd for nd in network.nodes() if isinstance(nd, Roadm)}
|
||||
# Build the network once using the default power defined in SI in eqpt config
|
||||
# power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
|
||||
# spacing, f_min and f_max
|
||||
p_db = equipment['SI']['default'].power_dbm
|
||||
p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
|
||||
equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
|
||||
|
||||
build_network(network, equipment, p_db, p_total_db)
|
||||
|
||||
amp_nodes = [nd for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.predecessors(nd)), Roadm)\
|
||||
and next(network.predecessors(nd)).restrictions['booster_variety_list']]
|
||||
|
||||
preamp_nodes = [nd for nd in network.nodes() \
|
||||
if isinstance(nd, Edfa) and isinstance(next(network.successors(nd)), Roadm)\
|
||||
and next(network.successors(nd)).restrictions['preamp_variety_list']]
|
||||
|
||||
# check that previously existing amp are not changed
|
||||
for amp in amp_nodes:
|
||||
if amp.uid in amp_nodes_nobuild_uid:
|
||||
print(amp.uid, amp.params.type_variety)
|
||||
if not amp.params.type_variety == amp_nodes_nobuild[amp.uid].params.type_variety:
|
||||
raise AssertionError()
|
||||
for amp in preamp_nodes:
|
||||
if amp.uid in preamp_nodes_nobuild_uid:
|
||||
if not amp.params.type_variety == preamp_nodes_nobuild[amp.uid].params.type_variety:
|
||||
raise AssertionError()
|
||||
# check that restrictions are correctly applied
|
||||
for amp in amp_nodes:
|
||||
if amp.uid not in amp_nodes_nobuild_uid:
|
||||
# and if roadm had no restrictions before build:
|
||||
if restrictions['booster_variety_list'] and \
|
||||
not roadms[next(network.predecessors(amp)).uid]\
|
||||
.restrictions['booster_variety_list']:
|
||||
if not amp.params.type_variety in restrictions['booster_variety_list']:
|
||||
|
||||
raise AssertionError()
|
||||
for amp in preamp_nodes:
|
||||
if amp.uid not in preamp_nodes_nobuild_uid:
|
||||
if restrictions['preamp_variety_list'] and\
|
||||
not roadms[next(network.successors(amp)).uid].restrictions['preamp_variety_list']:
|
||||
if not amp.params.type_variety in restrictions['preamp_variety_list']:
|
||||
raise AssertionError()
|
||||
Reference in New Issue
Block a user