mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-10-29 17:22:42 +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, \
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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": [{
|
||||
|
||||
@@ -102,7 +102,8 @@ class Roadm(_JsonThing):
|
||||
'restrictions': {
|
||||
'preamp_variety_list': [],
|
||||
'booster_variety_list': []
|
||||
}
|
||||
},
|
||||
'roadm-path-impairments': []
|
||||
}
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user