Feat: add detailed ROADM impairments per roadm-path

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: I09c55dcff53ffb264609654cde0f1d8b9dc7fe9b
This commit is contained in:
EstherLerouzic
2023-04-27 15:03:38 +02:00
parent fb4195c775
commit f950a6aee8
12 changed files with 483 additions and 12 deletions

View File

@@ -30,9 +30,10 @@ from logging import getLogger
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum, per_label_average, pretty_summary_print, \
watt2dbm, psd2powerdbm
from gnpy.core.parameters import RoadmParams, FusedParams, FiberParams, PumpParams, EdfaParams, EdfaOperational
from gnpy.core.parameters import RoadmParams, FusedParams, FiberParams, PumpParams, EdfaParams, EdfaOperational, \
RoadmPath, RoadmImpairment
from gnpy.core.science_utils import NliSolver, RamanSolver
from gnpy.core.info import SpectralInformation, ReferenceCarrier
from gnpy.core.info import SpectralInformation
from gnpy.core.exceptions import NetworkTopologyError, SpectrumError, ParametersError
@@ -259,6 +260,17 @@ class Roadm(_Node):
self.per_degree_pch_psw = self.params.per_degree_pch_psw
self.ref_pch_in_dbm = {}
self.ref_carrier = None
# Define the nature of from-to internal connection: express-path, drop-path, add-path
# roadm_paths contains a list of RoadmPath object for each path crossing the ROADM
self.roadm_paths = []
# roadm_path_impairments contains a dictionnary of impairments profiles corresponding to type_variety
# first listed add, drop an express constitute the default
self.roadm_path_impairments = self.params.roadm_path_impairments
# per degree definitions, in case some degrees have particular deviations with respect to default.
self.per_degree_impairments = {f'{i["from_degree"]}-{i["to_degree"]}': {"from_degree": i["from_degree"],
"to_degree": i["to_degree"],
"impairment_id": i["impairment_id"]}
for i in self.params.per_degree_impairments}
@property
def to_json(self):
@@ -289,6 +301,9 @@ class Roadm(_Node):
to_json['params']['per_degree_psd_out_mWperGHz'] = self.per_degree_pch_psd
if self.per_degree_pch_psw:
to_json['params']['per_degree_psd_out_mWperSlotWidth'] = self.per_degree_pch_psw
if self.per_degree_impairments:
to_json['per_degree_impairments'] = list(self.per_degree_impairments.values())
return to_json
def __repr__(self):
@@ -404,11 +419,68 @@ class Roadm(_Node):
delta_power = watt2dbm(input_power) - new_target
spectral_info.apply_attenuation_db(delta_power)
spectral_info.pmd = sqrt(spectral_info.pmd ** 2 + self.params.pmd ** 2)
spectral_info.pdl = sqrt(spectral_info.pdl ** 2 + self.params.pdl ** 2)
spectral_info.pmd = sqrt(spectral_info.pmd ** 2
+ self.get_roadm_path(from_degree=from_degree, to_degree=degree).impairment.pmd ** 2)
spectral_info.pdl = sqrt(spectral_info.pdl ** 2
+ self.get_roadm_path(from_degree=from_degree, to_degree=degree).impairment.pdl ** 2)
self.pch_out_dbm = watt2dbm(spectral_info.signal + spectral_info.nli + spectral_info.ase)
self.propagated_labels = spectral_info.label
def set_roadm_paths(self, from_degree, to_degree, path_type, impairment_id=None):
"""set internal path type: express, drop or add with corresponding impairment
If no impairment id is defined, then use the first profile that matches the path_type in the
profile dictionnary.
"""
# initialize impairment with params.pmd, params.cd
# if more detailed parameters are available for the Roadm, the use them instead
roadm_global_impairment = {'roadm-pmd': self.params.pmd,
'roadm-pdl': self.params.pdl}
if path_type in ['add', 'drop']:
# without detailed imparments, we assume that add OSNR contribution is the same as drop contribution
# add_drop_osnr_db = - 10log10(1/add_osnr + 1/drop_osnr) with add_osnr = drop_osnr
# = add_osnr_db + 10log10(2)
roadm_global_impairment['roadm-osnr'] = self.params.add_drop_osnr + lin2db(2)
impairment = RoadmImpairment(roadm_global_impairment)
if impairment_id is None:
# get the first item in the type variety that matches the path_type
for path_impairment_id, path_impairment in self.roadm_path_impairments.items():
if path_impairment.path_type == path_type:
impairment = path_impairment
impairment_id = path_impairment_id
break
# at this point, path_type is not part of roadm_path_impairment, impairment and impairment_id are None
else:
if impairment_id in self.roadm_path_impairments:
impairment = self.roadm_path_impairments[impairment_id]
else:
msg = f'ROADM {self.uid}: impairment profile id {impairment_id} is not defined in library'
raise NetworkTopologyError(msg)
# print(from_degree, to_degree, path_type)
self.roadm_paths.append(RoadmPath(from_degree=from_degree, to_degree=to_degree, path_type=path_type,
impairment_id=impairment_id, impairment=impairment))
def get_roadm_path(self, from_degree, to_degree):
"""Get internal path type impairment"""
for roadm_path in self.roadm_paths:
if roadm_path.from_degree == from_degree and roadm_path.to_degree == to_degree:
return roadm_path
msg = f'Could not find from_degree-to_degree {from_degree}-{to_degree} path in ROADM {self.uid}'
raise NetworkTopologyError(msg)
def get_per_degree_impairment_id(self, from_degree, to_degree):
"""returns the id of the impairment if the degrees are in the per_degree tab"""
if f'{from_degree}-{to_degree}' in self.per_degree_impairments.keys():
return self.per_degree_impairments[f'{from_degree}-{to_degree}']["impairment_id"]
return None
def get_path_type_per_id(self, impairment_id):
"""returns the path_type of the impairment if the is is defined"""
if impairment_id in self.roadm_path_impairments.keys():
return self.roadm_path_impairments[impairment_id].path_type
return None
def __call__(self, spectral_info, degree, from_degree):
self.propagate(spectral_info, degree=degree, from_degree=from_degree)
return spectral_info

View File

@@ -530,6 +530,62 @@ def set_fiber_input_power(network, fiber, equipment, pref_ch_db):
fiber.ref_pch_in_dbm = pref_ch_db - loss
def set_roadm_internal_paths(roadm, network):
"""Set ROADM path types (express, add, drop)
Uses implicit guess if no information is set in ROADM
"""
next_oms = [n.uid for n in network.successors(roadm) if not isinstance(n, elements.Transceiver)]
previous_oms = [n.uid for n in network.predecessors(roadm) if not isinstance(n, elements.Transceiver)]
drop_port = [n.uid for n in network.successors(roadm) if isinstance(n, elements.Transceiver)]
add_port = [n.uid for n in network.predecessors(roadm) if isinstance(n, elements.Transceiver)]
default_express = 'express'
default_add = 'add'
default_drop = 'drop'
# take user defined element impairment id if it exists
correct_from_degrees = []
correct_add = []
correct_to_degrees = []
correct_drop = []
for from_degree in previous_oms:
correct_from_degrees.append(from_degree)
for to_degree in next_oms:
correct_to_degrees.append(to_degree)
impairment_id = roadm.get_per_degree_impairment_id(from_degree, to_degree)
roadm.set_roadm_paths(from_degree=from_degree, to_degree=to_degree, path_type=default_express,
impairment_id=impairment_id)
for drop in drop_port:
correct_drop.append(drop)
impairment_id = roadm.get_per_degree_impairment_id(from_degree, drop)
path_type = roadm.get_path_type_per_id(impairment_id)
# a degree connected to a transceiver MUST be add or drop
# but a degree connected to something else could be an express, add or drop
# (for example case of external shelves)
if path_type and path_type != 'drop':
msg = f'Roadm {roadm.uid} path_type is defined as {path_type} but it should be drop'
raise NetworkTopologyError(msg)
roadm.set_roadm_paths(from_degree=from_degree, to_degree=drop, path_type=default_drop,
impairment_id=impairment_id)
for to_degree in next_oms:
for add in add_port:
correct_add.append(add)
impairment_id = roadm.get_per_degree_impairment_id(add, to_degree)
path_type = roadm.get_path_type_per_id(impairment_id)
if path_type and path_type != 'add':
msg = f'Roadm {roadm.uid} path_type is defined as {path_type} but it should be add'
raise NetworkTopologyError(msg)
roadm.set_roadm_paths(from_degree=add, to_degree=to_degree, path_type=default_add,
impairment_id=impairment_id)
# sanity check: raise an error if per_degree from or to degrees are not in the correct list
# raise an error if user defined path_type is not consistent with inferred path_type:
for item in roadm.per_degree_impairments.values():
if item['from_degree'] not in correct_from_degrees + correct_add or \
item['to_degree'] not in correct_to_degrees + correct_drop:
msg = f'Roadm {roadm.uid} has wrong from-to degree uid {item["from_degree"]} - {item["to_degree"]}'
raise NetworkTopologyError(msg)
def add_roadm_booster(network, roadm):
next_nodes = [n for n in network.successors(roadm)
if not (isinstance(n, elements.Transceiver) or isinstance(n, elements.Fused)
@@ -777,6 +833,7 @@ def build_network(network, equipment, pref_ch_db, pref_total_db, set_connector_l
set_egress_amplifier(network, roadm, equipment, pref_ch_db, pref_total_db, verbose)
for roadm in roadms:
set_roadm_input_powers(network, roadm, equipment, pref_ch_db)
set_roadm_internal_paths(roadm, network)
for fiber in [f for f in network.nodes() if isinstance(f, (elements.Fiber, elements.RamanFiber))]:
set_fiber_input_power(network, fiber, equipment, pref_ch_db)

View File

@@ -113,8 +113,68 @@ class RoadmParams(Parameters):
self.pmd = kwargs['pmd']
self.pdl = kwargs['pdl']
self.restrictions = kwargs['restrictions']
self.roadm_path_impairments = self.get_roadm_path_impairments(kwargs['roadm-path-impairments'])
except KeyError as e:
raise ParametersError(f'ROADM configurations must include {e}. Configuration: {kwargs}')
self.per_degree_impairments = kwargs.get('per_degree_impairments', [])
def get_roadm_path_impairments(self, path_impairments_list):
"""Get the ROADM list of profiles for impairments definition
transform the ietf model into gnpy internal model: add a path-type in the attributes
"""
if not path_impairments_list:
return {}
authorized_path_types = {
'roadm-express-path': 'express',
'roadm-add-path': 'add',
'roadm-drop-path': 'drop',
}
roadm_path_impairments = {}
for path_impairment in path_impairments_list:
index = path_impairment['roadm-path-impairments-id']
path_type = next(key for key in path_impairment if key in authorized_path_types.keys())
impairment_dict = dict({'path-type': authorized_path_types[path_type]}, **path_impairment[path_type])
roadm_path_impairments[index] = RoadmImpairment(impairment_dict)
return roadm_path_impairments
class RoadmPath:
def __init__(self, from_degree, to_degree, path_type, impairment_id=None, impairment=None):
"""Records roadm internal paths, types and impairment
path_type must be in "express", "add", "drop"
impairment_id must be one of the id detailed in equipement
"""
self.from_degree = from_degree
self.to_degree = to_degree
self.path_type = path_type
self.impairment_id = impairment_id
self.impairment = impairment
class RoadmImpairment:
"""Generic definition of impairments for express, add and drop"""
def __init__(self, params):
"""Records roadm internal paths and types"""
self.path_type = params.get('path-type')
self.pmd = params.get('roadm-pmd')
self.cd = params.get('roadm-cd')
self.pdl = params.get('roadm-pdl')
self.inband_crosstalk = params.get('roadm-inband-crosstalk')
self.maxloss = params.get('roadm-maxloss', 0)
if params.get('frequency-range') is not None:
self.fmin = params.get('frequency-range')['lower-frequency']
self.fmax = params.get('frequency-range')['upper-frequency']
else:
self.fmin, self.fmax = None, None
self.osnr = params.get('roadm-osnr', None)
self.pmax = params.get('roadm-pmax', None)
self.nf = params.get('roadm-noise-figure', None)
self.minloss = params.get('minloss', None)
self.typloss = params.get('typloss', None)
self.pmin = params.get('pmin', None)
self.ptyp = params.get('ptyp', None)
class FusedParams(Parameters):

View File

@@ -217,7 +217,70 @@
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"roadm-path-impairments": []
}, {
"type_variety": "detailed_impairments",
"target_pch_out_db": -20,
"add_drop_osnr": 38,
"pmd": 0,
"pdl": 0,
"restrictions": {
"preamp_variety_list":[],
"booster_variety_list":[]
},
"roadm-path-impairments": [
{
"roadm-path-impairments-id": 0,
"roadm-express-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 16.5
}
}, {
"roadm-path-impairments-id": 1,
"roadm-add-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 11.5,
"roadm-pmax": 2.5,
"roadm-osnr": 41,
"roadm-noise-figure": 23
}
}, {
"roadm-path-impairments-id": 2,
"roadm-drop-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 11.5,
"roadm-minloss": 7.5,
"roadm-typloss": 10,
"roadm-pmin": -13.5,
"roadm-pmax": -9.5,
"roadm-ptyp": -12,
"roadm-osnr": 41,
"roadm-noise-figure": 15
}
}
]
}
],
"SI": [{

View File

@@ -102,7 +102,8 @@ class Roadm(_JsonThing):
'restrictions': {
'preamp_variety_list': [],
'booster_variety_list': []
}
},
'roadm-path-impairments': []
}
def __init__(self, **kwargs):

View File

@@ -86,7 +86,69 @@
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"roadm-path-impairments": []
}, {
"type_variety": "example_detailed_impairments",
"target_pch_out_db": -20,
"add_drop_osnr": 35,
"pmd": 0,
"pdl": 0,
"restrictions": {
"preamp_variety_list":[],
"booster_variety_list":[]
},
"roadm-path-impairments": [
{
"roadm-path-impairments-id": 0,
"roadm-express-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 16.5
}
}, {
"roadm-path-impairments-id": 1,
"roadm-add-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 11.5,
"roadm-pmax": 2.5,
"roadm-osnr": 41,
"roadm-noise-figure": 23
}
}, {
"roadm-path-impairments-id": 2,
"roadm-drop-path": {
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 11.5,
"roadm-minloss": 7.5,
"roadm-typloss": 10,
"roadm-pmin": -13.5,
"roadm-pmax": -9.5,
"roadm-ptyp": -12,
"roadm-osnr": 41,
"roadm-noise-figure": 15
}
}]
}, {
"target_pch_out_db": -20,
"add_drop_osnr": 38,

View File

@@ -2,6 +2,9 @@ INFO gnpy.tools.cli_examples:cli_examples.py Computing path requests meshTop
WARNING gnpy.tools.json_io:json_io.py
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
default value is type_variety = default
WARNING gnpy.tools.json_io:json_io.py
WARNING missing roadm-path-impairments attribute in eqpt_config.json[Roadm]
default value is roadm-path-impairments = []
INFO gnpy.tools.json_io:json_io.py Automatically converting requests from XLS to JSON
INFO gnpy.topology.request:request.py
request 0

View File

@@ -2,6 +2,9 @@ INFO gnpy.tools.cli_examples:cli_examples.py Computing path requests CORONET
WARNING gnpy.tools.json_io:json_io.py
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
default value is type_variety = default
WARNING gnpy.tools.json_io:json_io.py
WARNING missing roadm-path-impairments attribute in eqpt_config.json[Roadm]
default value is roadm-path-impairments = []
INFO gnpy.topology.request:request.py
request 0
Computing path from trx Abilene to trx Albany

View File

@@ -1,6 +1,9 @@
WARNING gnpy.tools.json_io:json_io.py
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
default value is type_variety = default
WARNING gnpy.tools.json_io:json_io.py
WARNING missing roadm-path-impairments attribute in eqpt_config.json[Roadm]
default value is roadm-path-impairments = []
INFO gnpy.tools.cli_examples:cli_examples.py source = 'brest'
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'rennes'
WARNING gnpy.core.network:network.py

View File

@@ -1,6 +1,9 @@
WARNING gnpy.tools.json_io:json_io.py
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
default value is type_variety = default
WARNING gnpy.tools.json_io:json_io.py
WARNING missing roadm-path-impairments attribute in eqpt_config.json[Roadm]
default value is roadm-path-impairments = []
INFO gnpy.tools.cli_examples:cli_examples.py source = 'lannion'
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'lorient'
WARNING gnpy.core.network:network.py

View File

@@ -69,10 +69,12 @@ def test_equalization_combination_degree(delta_pdb_per_channel, degree, equaliza
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"roadm-path-impairments": []
}
}
roadm = Roadm(**roadm_config)
roadm.set_roadm_paths(from_degree='tata', to_degree=degree, path_type='express')
roadm.ref_pch_in_dbm['tata'] = 0
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
frequency = 191e12 + array([0, 50e9, 150e9, 225e9, 275e9])
@@ -231,7 +233,8 @@ def test_low_input_power(target_out, delta_pdb_per_channel, correction):
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"roadm-path-impairments": []
},
"metadata": {
"location": {
@@ -243,6 +246,7 @@ def test_low_input_power(target_out, delta_pdb_per_channel, correction):
}
}
roadm = Roadm(**roadm_config)
roadm.set_roadm_paths(from_degree='tata', to_degree='toto', path_type='express')
roadm.ref_pch_in_dbm['tata'] = 0
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
si = roadm(si, degree='toto', from_degree='tata')
@@ -283,7 +287,8 @@ def test_2low_input_power(target_out, delta_pdb_per_channel, correction):
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": []
}
},
"roadm-path-impairments": []
},
"metadata": {
"location": {
@@ -295,6 +300,7 @@ def test_2low_input_power(target_out, delta_pdb_per_channel, correction):
}
}
roadm = Roadm(**roadm_config)
roadm.set_roadm_paths(from_degree='tata', to_degree='toto', path_type='express')
roadm.ref_pch_in_dbm['tata'] = 0
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
si = roadm(si, degree='toto', from_degree='tata')

View File

@@ -23,8 +23,8 @@ from gnpy.tools.json_io import network_from_json, load_equipment, load_json, Amp
from gnpy.core.equipment import trx_mode_params
from gnpy.topology.request import PathRequest, compute_constrained_path, propagate
from gnpy.core.info import create_input_spectral_information, Carrier
from gnpy.core.utils import db2lin, dbm2watt
from gnpy.core.exceptions import ConfigurationError
from gnpy.core.utils import db2lin, dbm2watt, merge_amplifier_restrictions
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
TEST_DIR = Path(__file__).parent
@@ -549,3 +549,141 @@ def test_wrong_restrictions(restrictions, fail):
else:
equipment = _equipment_from_json(json_data, EQPT_LIBRARY_NAME)
assert equipment['Roadm']['example_test'].restrictions == restrictions
@pytest.mark.parametrize('roadm, from_degree, to_degree, expected_impairment_id, expected_type', [
('roadm Lannion_CAS', 'trx Lannion_CAS', 'east edfa in Lannion_CAS to Corlay', 1, 'add'),
('roadm Lannion_CAS', 'west edfa in Lannion_CAS to Stbrieuc', 'east edfa in Lannion_CAS to Corlay', 0, 'express'),
('roadm Lannion_CAS', 'west edfa in Lannion_CAS to Stbrieuc', 'trx Lannion_CAS', 2, 'drop'),
('roadm h', 'west edfa in h to g', 'trx h', None, 'drop')
])
def test_roadm_impairments(roadm, from_degree, to_degree, expected_impairment_id, expected_type):
"""Check that impairment id and types are correct
"""
json_data = load_json(NETWORK_FILE_NAME)
for el in json_data['elements']:
if el['uid'] == 'roadm Lannion_CAS':
el['type_variety'] = 'example_detailed_impairments'
equipment = load_equipment(EQPT_LIBRARY_NAME)
network = network_from_json(json_data, equipment)
build_network(network, equipment, 0.0, 20.0)
roadm = next(n for n in network.nodes() if n.uid == roadm)
assert roadm.get_roadm_path(from_degree, to_degree).path_type == expected_type
assert roadm.get_roadm_path(from_degree, to_degree).impairment_id == expected_impairment_id
@pytest.mark.parametrize('type_variety, from_degree, to_degree, impairment_id, expected_type', [
(None, 'trx Lannion_CAS', 'east edfa in Lannion_CAS to Corlay', 1, 'add'),
('default', 'trx Lannion_CAS', 'east edfa in Lannion_CAS to Corlay', 3, 'add'),
(None, 'west edfa in Lannion_CAS to Stbrieuc', 'east edfa in Lannion_CAS to Corlay', None, 'express')
])
def test_roadm_per_degree_impairments(type_variety, from_degree, to_degree, impairment_id, expected_type):
"""Check that impairment type is correct also if per degree impairment is defined
"""
json_data = load_json(EQPT_LIBRARY_NAME)
assert 'type_variety' not in json_data['Roadm'][2]
json_data['Roadm'][2]['roadm-path-impairments'] = [
{
"roadm-path-impairments-id": 1,
"roadm-add-path": {
"roadm-osnr": 41,
}
}, {
"roadm-path-impairments-id": 3,
"roadm-add-path": {
"roadm-inband-crosstalk": 0,
"roadm-osnr": 20,
"roadm-noise-figure": 23
}
}]
equipment = _equipment_from_json(json_data, EQPT_LIBRARY_NAME)
assert equipment['Roadm']['default'].type_variety == 'default'
json_data = load_json(NETWORK_FILE_NAME)
for el in json_data['elements']:
if el['uid'] == 'roadm Lannion_CAS' and type_variety is not None:
el['type_variety'] = type_variety
el['params'] = {
"per_degree_impairments": [
{
"from_degree": from_degree,
"to_degree": to_degree,
"impairment_id": impairment_id
}]
}
network = network_from_json(json_data, equipment)
build_network(network, equipment, 0.0, 20.0)
roadm = next(n for n in network.nodes() if n.uid == 'roadm Lannion_CAS')
assert roadm.get_roadm_path(from_degree, to_degree).path_type == expected_type
assert roadm.get_roadm_path(from_degree, to_degree).impairment_id == impairment_id
@pytest.mark.parametrize('from_degree, to_degree, impairment_id, error, message', [
('trx Lannion_CAS', 'east edfa in Lannion_CAS to Corlay', 2, NetworkTopologyError,
'Roadm roadm Lannion_CAS path_type is defined as drop but it should be add'), # wrong path_type
('trx Lannion_CAS', 'east edfa toto', 1, ConfigurationError,
'Roadm roadm Lannion_CAS has wrong from-to degree uid trx Lannion_CAS - east edfa toto'), # wrong degree
('trx Lannion_CAS', 'east edfa in Lannion_CAS to Corlay', 11, NetworkTopologyError,
'ROADM roadm Lannion_CAS: impairment profile id 11 is not defined in library') # wrong impairment_id
])
def test_wrong_roadm_per_degree_impairments(from_degree, to_degree, impairment_id, error, message):
"""Check that wrong per degree definitions are correctly catched
"""
equipment = load_equipment(EQPT_LIBRARY_NAME)
json_data = load_json(NETWORK_FILE_NAME)
for el in json_data['elements']:
if el['uid'] == 'roadm Lannion_CAS':
el['type_variety'] = 'example_detailed_impairments'
el['params'] = {
"per_degree_impairments": [
{
"from_degree": from_degree,
"to_degree": to_degree,
"impairment_id": impairment_id
}]
}
network = network_from_json(json_data, equipment)
with pytest.raises(error, match=message):
build_network(network, equipment, 0.0, 20.0)
@pytest.mark.parametrize('path_type, type_variety, expected_pmd, expected_pdl, expected_osnr', [
('express', 'default', 5.0e-12, 0.5, None), # roadm instance parameters pre-empts library
('express', 'example_test', 5.0e-12, 0.5, None),
('express', 'example_detailed_impairments', 0, 0, None), # detailed parameters pre-empts global instance ones
('add', 'default', 5.0e-12, 0.5, None),
('add', 'example_test', 5.0e-12, 0.5, None),
('add', 'example_detailed_impairments', 0, 0, 41)])
def test_impairment_initialization(path_type, type_variety, expected_pmd, expected_pdl, expected_osnr):
"""Check that impairments are correctly initialized, with this order:
- use equipment roadm impairments if no impairment are set in the ROADM instance
- use roadm global impairment if roadm global impairment are set
- use roadm detailed impairment for the corresponding path_type if roadm type_variety has detailed impairments
- use roadm per degree impairment if they are defined
"""
equipment = load_equipment(EQPT_LIBRARY_NAME)
extra_params = equipment['Roadm'][type_variety].__dict__
roadm_config = {
"uid": "roadm Lannion_CAS",
"params": {
"add_drop_osnr": 38,
"pmd": 5.0e-12,
"pdl": 0.5
}
}
if type_variety != 'default':
roadm_config["type_variety"] = type_variety
roadm_config['params'] = merge_amplifier_restrictions(roadm_config['params'], extra_params)
roadm = Roadm(**roadm_config)
roadm.set_roadm_paths(from_degree='tata', to_degree='toto', path_type=path_type)
assert roadm.get_roadm_path(from_degree='tata', to_degree='toto').path_type == path_type
assert roadm.get_roadm_path(from_degree='tata', to_degree='toto').impairment.pmd == expected_pmd
assert roadm.get_roadm_path(from_degree='tata', to_degree='toto').impairment.pdl == expected_pdl
if path_type == 'add':
# we assume for simplicity that add contribution is the same as drop contribution
# add_drop_osnr_db = 10log10(1/add_osnr + 1/drop_osnr)
if type_variety in ['default', 'example_test']:
assert roadm.get_roadm_path(from_degree='tata',
to_degree='toto').impairment.osnr == roadm.params.add_drop_osnr + lin2db(2)
else:
assert roadm.get_roadm_path(from_degree='tata', to_degree='toto').impairment.osnr == expected_osnr