mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-01 10:38:10 +00:00
Feat: add detailed ROADM impairments per roadm-path
Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com> Change-Id: I09c55dcff53ffb264609654cde0f1d8b9dc7fe9b
This commit is contained in:
@@ -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, \
|
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum, per_label_average, pretty_summary_print, \
|
||||||
watt2dbm, psd2powerdbm
|
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.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
|
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.per_degree_pch_psw = self.params.per_degree_pch_psw
|
||||||
self.ref_pch_in_dbm = {}
|
self.ref_pch_in_dbm = {}
|
||||||
self.ref_carrier = None
|
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
|
@property
|
||||||
def to_json(self):
|
def to_json(self):
|
||||||
@@ -289,6 +301,9 @@ class Roadm(_Node):
|
|||||||
to_json['params']['per_degree_psd_out_mWperGHz'] = self.per_degree_pch_psd
|
to_json['params']['per_degree_psd_out_mWperGHz'] = self.per_degree_pch_psd
|
||||||
if self.per_degree_pch_psw:
|
if self.per_degree_pch_psw:
|
||||||
to_json['params']['per_degree_psd_out_mWperSlotWidth'] = 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
|
return to_json
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@@ -404,11 +419,68 @@ class Roadm(_Node):
|
|||||||
delta_power = watt2dbm(input_power) - new_target
|
delta_power = watt2dbm(input_power) - new_target
|
||||||
|
|
||||||
spectral_info.apply_attenuation_db(delta_power)
|
spectral_info.apply_attenuation_db(delta_power)
|
||||||
spectral_info.pmd = sqrt(spectral_info.pmd ** 2 + self.params.pmd ** 2)
|
spectral_info.pmd = sqrt(spectral_info.pmd ** 2
|
||||||
spectral_info.pdl = sqrt(spectral_info.pdl ** 2 + self.params.pdl ** 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.pch_out_dbm = watt2dbm(spectral_info.signal + spectral_info.nli + spectral_info.ase)
|
||||||
self.propagated_labels = spectral_info.label
|
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):
|
def __call__(self, spectral_info, degree, from_degree):
|
||||||
self.propagate(spectral_info, degree=degree, from_degree=from_degree)
|
self.propagate(spectral_info, degree=degree, from_degree=from_degree)
|
||||||
return spectral_info
|
return spectral_info
|
||||||
|
|||||||
@@ -530,6 +530,62 @@ def set_fiber_input_power(network, fiber, equipment, pref_ch_db):
|
|||||||
fiber.ref_pch_in_dbm = pref_ch_db - loss
|
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):
|
def add_roadm_booster(network, roadm):
|
||||||
next_nodes = [n for n in network.successors(roadm)
|
next_nodes = [n for n in network.successors(roadm)
|
||||||
if not (isinstance(n, elements.Transceiver) or isinstance(n, elements.Fused)
|
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)
|
set_egress_amplifier(network, roadm, equipment, pref_ch_db, pref_total_db, verbose)
|
||||||
for roadm in roadms:
|
for roadm in roadms:
|
||||||
set_roadm_input_powers(network, roadm, equipment, pref_ch_db)
|
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))]:
|
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)
|
set_fiber_input_power(network, fiber, equipment, pref_ch_db)
|
||||||
|
|
||||||
|
|||||||
@@ -113,8 +113,68 @@ class RoadmParams(Parameters):
|
|||||||
self.pmd = kwargs['pmd']
|
self.pmd = kwargs['pmd']
|
||||||
self.pdl = kwargs['pdl']
|
self.pdl = kwargs['pdl']
|
||||||
self.restrictions = kwargs['restrictions']
|
self.restrictions = kwargs['restrictions']
|
||||||
|
self.roadm_path_impairments = self.get_roadm_path_impairments(kwargs['roadm-path-impairments'])
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise ParametersError(f'ROADM configurations must include {e}. Configuration: {kwargs}')
|
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):
|
class FusedParams(Parameters):
|
||||||
|
|||||||
@@ -217,7 +217,70 @@
|
|||||||
"restrictions": {
|
"restrictions": {
|
||||||
"preamp_variety_list": [],
|
"preamp_variety_list": [],
|
||||||
"booster_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": [{
|
"SI": [{
|
||||||
|
|||||||
@@ -102,7 +102,8 @@ class Roadm(_JsonThing):
|
|||||||
'restrictions': {
|
'restrictions': {
|
||||||
'preamp_variety_list': [],
|
'preamp_variety_list': [],
|
||||||
'booster_variety_list': []
|
'booster_variety_list': []
|
||||||
}
|
},
|
||||||
|
'roadm-path-impairments': []
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
|||||||
@@ -86,7 +86,69 @@
|
|||||||
"restrictions": {
|
"restrictions": {
|
||||||
"preamp_variety_list": [],
|
"preamp_variety_list": [],
|
||||||
"booster_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,
|
"target_pch_out_db": -20,
|
||||||
"add_drop_osnr": 38,
|
"add_drop_osnr": 38,
|
||||||
|
|||||||
@@ -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 gnpy.tools.json_io:json_io.py
|
||||||
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
||||||
default value is type_variety = default
|
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.tools.json_io:json_io.py Automatically converting requests from XLS to JSON
|
||||||
INFO gnpy.topology.request:request.py
|
INFO gnpy.topology.request:request.py
|
||||||
request 0
|
request 0
|
||||||
|
|||||||
@@ -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 gnpy.tools.json_io:json_io.py
|
||||||
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
||||||
default value is type_variety = default
|
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
|
INFO gnpy.topology.request:request.py
|
||||||
request 0
|
request 0
|
||||||
Computing path from trx Abilene to trx Albany
|
Computing path from trx Abilene to trx Albany
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
WARNING gnpy.tools.json_io:json_io.py
|
WARNING gnpy.tools.json_io:json_io.py
|
||||||
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
||||||
default value is type_variety = default
|
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 source = 'brest'
|
||||||
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'rennes'
|
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'rennes'
|
||||||
WARNING gnpy.core.network:network.py
|
WARNING gnpy.core.network:network.py
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
WARNING gnpy.tools.json_io:json_io.py
|
WARNING gnpy.tools.json_io:json_io.py
|
||||||
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
WARNING missing type_variety attribute in eqpt_config.json[Roadm]
|
||||||
default value is type_variety = default
|
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 source = 'lannion'
|
||||||
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'lorient'
|
INFO gnpy.tools.cli_examples:cli_examples.py destination = 'lorient'
|
||||||
WARNING gnpy.core.network:network.py
|
WARNING gnpy.core.network:network.py
|
||||||
|
|||||||
@@ -69,10 +69,12 @@ def test_equalization_combination_degree(delta_pdb_per_channel, degree, equaliza
|
|||||||
"restrictions": {
|
"restrictions": {
|
||||||
"preamp_variety_list": [],
|
"preamp_variety_list": [],
|
||||||
"booster_variety_list": []
|
"booster_variety_list": []
|
||||||
}
|
},
|
||||||
|
"roadm-path-impairments": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
roadm = Roadm(**roadm_config)
|
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_pch_in_dbm['tata'] = 0
|
||||||
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
||||||
frequency = 191e12 + array([0, 50e9, 150e9, 225e9, 275e9])
|
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": {
|
"restrictions": {
|
||||||
"preamp_variety_list": [],
|
"preamp_variety_list": [],
|
||||||
"booster_variety_list": []
|
"booster_variety_list": []
|
||||||
}
|
},
|
||||||
|
"roadm-path-impairments": []
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"location": {
|
"location": {
|
||||||
@@ -243,6 +246,7 @@ def test_low_input_power(target_out, delta_pdb_per_channel, correction):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
roadm = Roadm(**roadm_config)
|
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_pch_in_dbm['tata'] = 0
|
||||||
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
||||||
si = roadm(si, degree='toto', from_degree='tata')
|
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": {
|
"restrictions": {
|
||||||
"preamp_variety_list": [],
|
"preamp_variety_list": [],
|
||||||
"booster_variety_list": []
|
"booster_variety_list": []
|
||||||
}
|
},
|
||||||
|
"roadm-path-impairments": []
|
||||||
},
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"location": {
|
"location": {
|
||||||
@@ -295,6 +300,7 @@ def test_2low_input_power(target_out, delta_pdb_per_channel, correction):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
roadm = Roadm(**roadm_config)
|
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_pch_in_dbm['tata'] = 0
|
||||||
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
roadm.ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
|
||||||
si = roadm(si, degree='toto', from_degree='tata')
|
si = roadm(si, degree='toto', from_degree='tata')
|
||||||
|
|||||||
@@ -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.core.equipment import trx_mode_params
|
||||||
from gnpy.topology.request import PathRequest, compute_constrained_path, propagate
|
from gnpy.topology.request import PathRequest, compute_constrained_path, propagate
|
||||||
from gnpy.core.info import create_input_spectral_information, Carrier
|
from gnpy.core.info import create_input_spectral_information, Carrier
|
||||||
from gnpy.core.utils import db2lin, dbm2watt
|
from gnpy.core.utils import db2lin, dbm2watt, merge_amplifier_restrictions
|
||||||
from gnpy.core.exceptions import ConfigurationError
|
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
|
||||||
|
|
||||||
|
|
||||||
TEST_DIR = Path(__file__).parent
|
TEST_DIR = Path(__file__).parent
|
||||||
@@ -549,3 +549,141 @@ def test_wrong_restrictions(restrictions, fail):
|
|||||||
else:
|
else:
|
||||||
equipment = _equipment_from_json(json_data, EQPT_LIBRARY_NAME)
|
equipment = _equipment_from_json(json_data, EQPT_LIBRARY_NAME)
|
||||||
assert equipment['Roadm']['example_test'].restrictions == restrictions
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user