Skip to content

Commit

Permalink
Add frequency dependency on ROADM impairments
Browse files Browse the repository at this point in the history
Signed-off-by: EstherLerouzic <[email protected]>
Change-Id: Icb88bf9c42c09deb0064e3299b78b080462fef79
  • Loading branch information
EstherLerouzic committed Sep 30, 2024
1 parent 9ed4ec6 commit d96e923
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 41 deletions.
48 changes: 41 additions & 7 deletions gnpy/core/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def propagate(self, spectral_info, degree, from_degree):
# record input powers to compute the actual loss at the end of the process
input_power_dbm = watt2dbm(spectral_info.signal + spectral_info.nli + spectral_info.ase)
# apply min ROADM loss if it exists
roadm_maxloss_db = self.get_roadm_path(from_degree, degree).impairment.maxloss
roadm_maxloss_db = self.get_impairment('roadm-maxloss', spectral_info.frequency, from_degree, degree)
spectral_info.apply_attenuation_db(roadm_maxloss_db)
# records the total power after applying minimum loss
net_input_power_dbm = watt2dbm(spectral_info.signal + spectral_info.nli + spectral_info.ase)
Expand All @@ -433,7 +433,7 @@ def propagate(self, spectral_info, degree, from_degree):
# the power out of the ROADM for the ref channel is the min value between target power and input power.
ref_pch_in_dbm = self.ref_pch_in_dbm[from_degree]
# Calculate the output power for the reference channel (only for visualization)
self.ref_pch_out_dbm = min(ref_pch_in_dbm - roadm_maxloss_db, ref_per_degree_pch)
self.ref_pch_out_dbm = min(ref_pch_in_dbm - max(roadm_maxloss_db), ref_per_degree_pch)

# Definition of effective_loss:
# Optical power of carriers are equalized by the ROADM, so that the experienced loss is not the same for
Expand Down Expand Up @@ -465,11 +465,11 @@ def propagate(self, spectral_info, degree, from_degree):
spectral_info.apply_attenuation_db(delta_power)

# Update the PMD information
pmd_impairment = self.get_roadm_path(from_degree=from_degree, to_degree=degree).impairment.pmd
pmd_impairment = self.get_impairment('roadm-pmd', spectral_info.frequency, from_degree, degree)
spectral_info.pmd = sqrt(spectral_info.pmd ** 2 + pmd_impairment ** 2)

# Update the PMD information
pdl_impairment = self.get_roadm_path(from_degree=from_degree, to_degree=degree).impairment.pdl
pdl_impairment = self.get_impairment('roadm-pdl', spectral_info.frequency, from_degree, degree)
spectral_info.pdl = sqrt(spectral_info.pdl ** 2 + pdl_impairment ** 2)

# Update the per channel power with the result of propagation
Expand All @@ -487,13 +487,19 @@ def set_roadm_paths(self, from_degree, to_degree, path_type, impairment_id=None)
"""
# 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}
roadm_global_impairment = {
'impairment': [{
'roadm-pmd': self.params.pmd,
'roadm-pdl': self.params.pdl,
'frequency-range': {
'lower-frequency': None,
'upper-frequency': None
}}]}
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)
roadm_global_impairment['impairment'][0]['roadm-osnr'] = self.params.add_drop_osnr + lin2db(2)
impairment = RoadmImpairment(roadm_global_impairment)

if impairment_id is None:
Expand Down Expand Up @@ -534,6 +540,34 @@ def get_path_type_per_id(self, impairment_id):
return self.roadm_path_impairments[impairment_id].path_type
return None

def get_impairment(self, impairment: str, frequency_array: array, from_degree: str, degree: str) \
-> array:
"""
Retrieves the specified impairment values for the given frequency array.
Parameters:
impairment (str): The type of impairment to retrieve (roadm-pmd, roamd-maxloss...).
frequency_array (array): The frequencies at which to check for impairments.
from_degree (str): The ingress degree for the roadm internal path.
degree (str): The egress degree for the roadm internal path.
Returns:
array: An array of impairment values for the specified frequencies.
"""
result = []
impairment_per_band = self.get_roadm_path(from_degree, degree).impairment.impairments
for frequency in frequency_array:
for item in impairment_per_band:
f_min = item['frequency-range']['lower-frequency']
f_max = item['frequency-range']['upper-frequency']
if (f_min is None or f_min <= frequency <= f_max):
item[impairment] = item.get(impairment, RoadmImpairment.default_values[impairment])
if item[impairment] is not None:
result.append(item[impairment])
break # Stop searching after the first match for this frequency
if result:
return array(result)

def __call__(self, spectral_info, degree, from_degree):
self.propagate(spectral_info, degree=degree, from_degree=from_degree)
return spectral_info
Expand Down
36 changes: 17 additions & 19 deletions gnpy/core/parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def get_roadm_path_impairments(self, path_impairments_list):
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][0])
impairment_dict = {'path-type': authorized_path_types[path_type], 'impairment': path_impairment[path_type]}
roadm_path_impairments[index] = RoadmImpairment(impairment_dict)
return roadm_path_impairments

Expand All @@ -158,26 +158,24 @@ def __init__(self, from_degree, to_degree, path_type, impairment_id=None, impair

class RoadmImpairment:
"""Generic definition of impairments for express, add and drop"""
default_values = {
'roadm-pmd': None,
'roadm-cd': None,
'roadm-pdl': None,
'roadm-inband-crosstalk': None,
'roadm-maxloss': 0,
'roadm-osnr': None,
'roadm-pmax': None,
'roadm-noise-figure': None,
'minloss': None,
'typloss': None,
'pmin': None,
'ptyp': None
}

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)
self.impairments = params['impairment']


class FusedParams(Parameters):
Expand Down
6 changes: 4 additions & 2 deletions gnpy/topology/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,8 @@ def propagate(path, req, equipment):
for i, el in enumerate(path):
if isinstance(el, Roadm):
si = el(si, degree=path[i + 1].uid, from_degree=path[i - 1].uid)
roadm_osnr.append(el.get_roadm_path(from_degree=path[i - 1].uid, to_degree=path[i + 1].uid).impairment.osnr)
roadm_osnr.append(el.get_impairment('roadm-osnr', si.frequency,
from_degree=path[i - 1].uid, degree=path[i + 1].uid))
else:
si = el(si)
path[0].update_snr(si.tx_osnr)
Expand Down Expand Up @@ -413,7 +414,8 @@ def propagate_and_optimize_mode(path, req, equipment):
for i, el in enumerate(path):
if isinstance(el, Roadm):
spc_info = el(spc_info, degree=path[i + 1].uid, from_degree=path[i - 1].uid)
roadm_osnr.append(el.get_roadm_path(from_degree=path[i - 1].uid, to_degree=path[i + 1].uid).impairment.osnr)
roadm_osnr.append(el.get_impairment('roadm-osnr', spc_info.frequency,
from_degree=path[i - 1].uid, degree=path[i + 1].uid))
else:
spc_info = el(spc_info)
for this_mode in modes_to_explore:
Expand Down
13 changes: 13 additions & 0 deletions tests/data/eqpt_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,19 @@
"roadm-pmax": 2.5,
"roadm-osnr": 41,
"roadm-noise-figure": 23
}, {
"frequency-range": {
"lower-frequency": 186.3e12,
"upper-frequency": 190.1e12
},
"roadm-pmd": 0,
"roadm-cd": 0,
"roadm-pdl": 0.5,
"roadm-inband-crosstalk": 0,
"roadm-maxloss": 5,
"roadm-pmax": 0,
"roadm-osnr": 35,
"roadm-noise-figure": 6
}]
}, {
"roadm-path-impairments-id": 2,
Expand Down
38 changes: 25 additions & 13 deletions tests/test_roadm_restrictions.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,11 +596,19 @@ def test_roadm_per_degree_impairments(type_variety, from_degree, to_degree, impa
{
"roadm-path-impairments-id": 1,
"roadm-add-path": [{
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-osnr": 41,
}]
}, {
"roadm-path-impairments-id": 3,
"roadm-add-path": [{
"frequency-range": {
"lower-frequency": 191.3e12,
"upper-frequency": 196.1e12
},
"roadm-inband-crosstalk": 0,
"roadm-osnr": 20,
"roadm-noise-figure": 23
Expand Down Expand Up @@ -657,14 +665,15 @@ def test_wrong_roadm_per_degree_impairments(from_degree, to_degree, impairment_i
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):
@pytest.mark.parametrize('path_type, type_variety, expected_pmd, expected_pdl, expected_osnr, freq', [
('express', 'default', 5.0e-12, 0.5, None, [191.3e12]), # roadm instance parameters pre-empts library
('express', 'example_test', 5.0e-12, 0.5, None, [191.3e12]),
('express', 'example_detailed_impairments', 0, 0, None, [191.3e12]), # detailed parameters pre-empts global ones
('add', 'default', 5.0e-12, 0.5, None, [191.3e12]),
('add', 'example_test', 5.0e-12, 0.5, None, [191.3e12]),
('add', 'example_detailed_impairments', 0, 0, 41, [191.3e12]),
('add', 'example_detailed_impairments', [0, 0], [0.5, 0], [35, 41], [188.5e12, 191.3e12])])
def test_impairment_initialization(path_type, type_variety, expected_pmd, expected_pdl, expected_osnr, freq):
"""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
Expand All @@ -687,13 +696,16 @@ def test_impairment_initialization(path_type, type_variety, expected_pmd, expect
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
assert_allclose(roadm.get_impairment('roadm-pmd', freq, from_degree='tata', degree='toto'),
expected_pmd, rtol=1e-12)
assert_allclose(roadm.get_impairment('roadm-pdl', freq, from_degree='tata', degree='toto'),
expected_pdl, rtol=1e-12)
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)
assert_allclose(roadm.get_impairment('roadm-osnr', freq, from_degree='tata', degree='toto'),
roadm.params.add_drop_osnr + lin2db(2), rtol=1e-12)
else:
assert roadm.get_roadm_path(from_degree='tata', to_degree='toto').impairment.osnr == expected_osnr
assert_allclose(roadm.get_impairment('roadm-osnr', freq, from_degree='tata', degree='toto'),
expected_osnr, rtol=1e-12)

0 comments on commit d96e923

Please sign in to comment.