add equalization per constant ratio power/slot_width

Constant power per slot_width uses the slot width instead of
baud rate compared to PSD.

This is the equalization used in OpenROADM

add tests for constant power per slot width equalization

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: Ie350e4c15cb6b54c15e418556fe33e72486cb134
This commit is contained in:
EstherLerouzic
2022-10-24 21:41:01 +02:00
parent e9e8956caf
commit 48e3f96967
11 changed files with 199 additions and 97 deletions

View File

@@ -176,36 +176,36 @@ ROADM
The user can only modify the value of existing parameters:
+-----------------------------+-----------+----------------------------------------------------+
| field | type | description |
+=============================+===========+====================================================+
| ``target_pch_out_db`` | (number) | Default :ref:`equalization strategy<equalization>` |
| or | | for this ROADM type. |
| ``target_psd_out_mWperGHz`` | | |
| (mutually exclusive) | | Auto-design sets the ROADM egress channel |
| | | power. This reflects typical control loop |
| | | algorithms that adjust ROADM losses to |
| | | equalize channels (e.g., coming from |
| | | different ingress direction or add ports). |
| | | |
| | | These values are used as defaults when no |
| | | overrides are set per each ``Roadm`` |
| | | element in the network topology. |
+-----------------------------+-----------+----------------------------------------------------+
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
+-----------------------------+-----------+----------------------------------------------------+
| ``pmd`` | (number) | Polarization mode dispersion (PMD). (s) |
+-----------------------------+-----------+----------------------------------------------------+
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
| | strings) | and ``booster_variety_list`` represent |
| | | list of ``type_variety`` amplifiers which |
| | | are allowed for auto-design within ROADM's |
| | | line degrees. |
| | | |
| | | If no booster should be placed on a degree, |
| | | insert a ``Fused`` node on the degree |
| | | output. |
+-----------------------------+-----------+----------------------------------------------------+
+-------------------------------+-----------+----------------------------------------------------+
| field | type | description |
+===============================+===========+====================================================+
| ``target_pch_out_db`` | (number) | Default :ref:`equalization strategy<equalization>` |
| or | | for this ROADM type. |
| ``target_psd_out_mWperGHz`` | | |
| or | | Auto-design sets the ROADM egress channel |
| ``target_out_mWperSlotWidth`` | | power. This reflects typical control loop |
| (mutually exclusive) | | algorithms that adjust ROADM losses to |
| | | equalize channels (e.g., coming from |
| | | different ingress direction or add ports). |
| | | |
| | | These values are used as defaults when no |
| | | overrides are set per each ``Roadm`` |
| | | element in the network topology. |
+-------------------------------+-----------+----------------------------------------------------+
| ``add_drop_osnr`` | (number) | OSNR contribution from the add/drop ports |
+-------------------------------+-----------+----------------------------------------------------+
| ``pmd`` | (number) | Polarization mode dispersion (PMD). (s) |
+-------------------------------+-----------+----------------------------------------------------+
| ``restrictions`` | (dict of | If non-empty, keys ``preamp_variety_list`` |
| | strings) | and ``booster_variety_list`` represent |
| | | list of ``type_variety`` amplifiers which |
| | | are allowed for auto-design within ROADM's |
| | | line degrees. |
| | | |
| | | If no booster should be placed on a degree, |
| | | insert a ``Fused`` node on the degree |
| | | output. |
+-------------------------------+-----------+----------------------------------------------------+
Global parameters
-----------------
@@ -564,6 +564,57 @@ There is no overlap of the occupation and both share the same boundary.
Equalization choices
~~~~~~~~~~~~~~~~~~~~
ROADMs typically equalize the optical power across multiple channels using one of the available equalization strategies — either targeting a specific output power, or a specific power spectral density (PSD).
Both of these strategies can be adjusted by a per-channel offset.
ROADMs typically equalize the optical power across multiple channels using one of the available equalization strategies — either targeting a specific output power, or a specific power spectral density (PSD), or a spectfic power spectral density using slot_width as spectrum width reference (PSW).
All of these strategies can be adjusted by a per-channel power offset.
The equalization strategy can be defined globally per a ROADM model, or per each ROADM instance in the topology, and within a ROADM also on a per-degree basis.
Let's consider some example for the equalization. Suppose that the types of signal to be propagated are the following:
.. code-block:: json
{
"baud_rate": 32e9,
"f_min":191.3e12,
"f_max":192.3e12,
"spacing": 50e9,
"label": 1
},
{
"baud_rate": 64e9,
"f_min":193.3e12,
"f_max":194.3e12,
"spacing": 75e9,
"label": 2
}
with the PSD equalization in a ROADM:
.. code-block:: json
{
"uid": "roadm A",
"type": "Roadm",
"params": {
"target_psd_out_mWperGHz": 3.125e-4,
}
},
This means that power out of the ROADM will be computed as 3.125e-4 * 32 = 0.01 mW ie -20 dBm for label 1 types of carriers
and 3.125e4 * 64 = 0.02 mW ie -16.99 dBm for label2 channels. So a ratio of ~ 3 dB between target powers for these carriers.
With the PSW equalization:
.. code-block:: json
{
"uid": "roadm A",
"type": "Roadm",
"params": {
"target_out_mWperSlotWidth": 2.0e-4,
}
},
the power out of the ROADM will be computed as 2.0e-4 * 50 = 0.01 mW ie -20 dBm for label 1 types of carriers
and 2.0e4 * 75 = 0.015 mW ie -18.24 dBm for label2 channels. So a ratio of ~ 1.76 dB between target powers for these carriers.

View File

@@ -21,16 +21,18 @@ instance as a result.
"""
from numpy import abs, array, errstate, ones, interp, mean, pi, polyfit, polyval, sum, sqrt, log10, exp, asarray, full,\
squeeze, zeros, append, flip, outer, minimum
squeeze, zeros, append, flip, outer, ndarray
from scipy.constants import h, c
from scipy.interpolate import interp1d
from collections import namedtuple
from typing import Union
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum, per_label_average, pretty_summary_print, \
watt2dbm, psd2powerdbm, power_dbm_to_psd_mw_ghz
watt2dbm, psd2powerdbm
from gnpy.core.parameters import RoadmParams, FusedParams, FiberParams, PumpParams, EdfaParams, EdfaOperational
from gnpy.core.science_utils import NliSolver, RamanSolver
from gnpy.core.info import SpectralInformation
from gnpy.core.info import SpectralInformation, ReferenceCarrier
from gnpy.core.exceptions import NetworkTopologyError, SpectrumError
@@ -235,9 +237,10 @@ class Roadm(_Node):
# target for equalization for the ROADM only one must be not None
self.target_pch_out_dbm = self.params.target_pch_out_db
self.target_psd_out_mWperGHz = self.params.target_psd_out_mWperGHz
# per degree equalization that overrides the ROADM equalization if not None
self.target_out_mWperSlotWidth = self.params.target_out_mWperSlotWidth
self.per_degree_pch_out_dbm = self.params.per_degree_pch_out_db
self.per_degree_pch_psd = self.params.per_degree_pch_psd
self.per_degree_pch_psw = self.params.per_degree_pch_psw
@property
def to_json(self):
@@ -245,6 +248,8 @@ class Roadm(_Node):
equalisation, value = 'target_pch_out_db', self.target_pch_out_dbm
elif self.target_psd_out_mWperGHz is not None:
equalisation, value = 'target_psd_out_mWperGHz', self.target_psd_out_mWperGHz
elif self.target_out_mWperSlotWidth is not None:
equalisation, value = 'target_out_mWperSlotWidth', self.target_out_mWperSlotWidth
else:
assert False, 'There must be one default equalization defined in ROADM'
to_json = {
@@ -263,6 +268,8 @@ class Roadm(_Node):
to_json['params']['per_degree_pch_out_db'] = self.per_degree_pch_out_dbm
if self.per_degree_pch_psd:
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
return to_json
def __repr__(self):
@@ -278,7 +285,8 @@ class Roadm(_Node):
f' reference pch out (dBm): {self.ref_pch_out_dbm:.2f}',
f' actual pch out (dBm): {total_pch}'])
def get_roadm_target_power(self, reference_baudrate=None, spectral_info=None):
def get_roadm_target_power(self, ref_carrier: ReferenceCarrier = None,
spectral_info: SpectralInformation = None) -> Union[int, float, ndarray]:
"""Computes the power in dBm for a reference carrier or for a spectral information.
power is computed based on equalization target.
if spectral_info baud_rate is baud_rate = [32e9, 42e9, 64e9, 42e9, 32e9], and
@@ -294,22 +302,28 @@ class Roadm(_Node):
return full(len(spectral_info.channel_number), self.target_pch_out_dbm)
if self.target_psd_out_mWperGHz is not None:
return psd2powerdbm(self.target_psd_out_mWperGHz, spectral_info.baud_rate)
if self.target_out_mWperSlotWidth is not None:
return psd2powerdbm(self.target_out_mWperSlotWidth, spectral_info.slot_width)
else:
if self.target_pch_out_dbm is not None:
return self.target_pch_out_dbm
if self.target_psd_out_mWperGHz is not None:
return psd2powerdbm(self.target_psd_out_mWperGHz, reference_baudrate)
return psd2powerdbm(self.target_psd_out_mWperGHz, ref_carrier.baud_rate)
if self.target_out_mWperSlotWidth is not None:
return psd2powerdbm(self.target_out_mWperSlotWidth, ref_carrier.slot_width)
return None
def get_per_degree_ref_power(self, degree, reference_baudrate):
def get_per_degree_ref_power(self, degree, ref_carrier):
"""Get the target power in dBm out of ROADM degree for the reference bandwidth
If no equalization is defined on this degree use the ROADM level one.
"""
if degree in self.per_degree_pch_out_dbm:
return self.per_degree_pch_out_dbm[degree]
elif degree in self.per_degree_pch_psd:
return psd2powerdbm(self.per_degree_pch_psd[degree], reference_baudrate)
return self.get_roadm_target_power(reference_baudrate=reference_baudrate)
return psd2powerdbm(self.per_degree_pch_psd[degree], ref_carrier.baud_rate)
elif degree in self.per_degree_pch_psw:
return psd2powerdbm(self.per_degree_pch_psw[degree], ref_carrier.slot_width)
return self.get_roadm_target_power(ref_carrier)
def get_per_degree_power(self, degree, spectral_info):
"""Get the target power in dBm out of ROADM degree for the spectral information
@@ -332,7 +346,7 @@ class Roadm(_Node):
# TODO maybe add a minimum loss for the ROADM
# find the target power for the reference carrier
ref_per_degree_pch = self.get_per_degree_ref_power(degree, spectral_info.pref.ref_carrier.baud_rate)
ref_per_degree_pch = self.get_per_degree_ref_power(degree, spectral_info.pref.ref_carrier)
# find the target powers for each signal carrier
per_degree_pch = self.get_per_degree_power(degree, spectral_info=spectral_info)

View File

@@ -371,8 +371,15 @@ class ReferenceCarrier:
experienced by p_span_i in Roadm element.
Baud rate is required to find the target power in constant PSD: power = PSD_target * baud_rate.
For example, if target PSD is 3.125e4mW/GHz and reference carrier type a 32 GBaud channel then
For example, if target PSD is 3.125e4mW/GHz and reference carrier type a 32 GBaud channel then
output power should be -20 dBm and for a 64 GBaud channel power target would need 3 dB more: -17 dBm.
Slot width is required to find the target power in constant PSW (constant power per slot width equalization):
power = PSW_target * slot_width.
For example, if target PSW is 2e4mW/GHz and reference carrier type a 32 GBaud channel in a 50GHz slot width then
output power should be -20 dBm and for a 64 GBaud channel in a 75 GHz slot width, power target would be -18.24 dBm.
Other attributes (like slot_width or roll-off) may be added there for future equalization purpose.
"""
baud_rate: float
slot_width: float

View File

@@ -12,6 +12,7 @@ from operator import attrgetter
from gnpy.core import ansi_escapes, elements
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
from gnpy.core.utils import round2float, convert_length
from gnpy.core.info import ReferenceCarrier
from collections import namedtuple
@@ -237,7 +238,8 @@ def set_egress_amplifier(network, this_node, equipment, pref_ch_db, pref_total_d
""" this node can be a transceiver or a ROADM (same function called in both cases)
"""
power_mode = equipment['Span']['default'].power_mode
reference_baudrate = equipment['SI']['default'].baud_rate
ref_carrier = ReferenceCarrier(baud_rate=equipment['SI']['default'].baud_rate,
slot_width=equipment['SI']['default'].spacing)
next_oms = (n for n in network.successors(this_node) if not isinstance(n, elements.Transceiver))
for oms in next_oms:
# go through all the OMS departing from the ROADM
@@ -247,8 +249,7 @@ def set_egress_amplifier(network, this_node, equipment, pref_ch_db, pref_total_d
this_node_out_power = 0.0 # default value if this_node is a transceiver
if isinstance(this_node, elements.Roadm):
# get target power out from ROADM for the reference carrier based on equalization settings
this_node_out_power = this_node.get_per_degree_ref_power(degree=node.uid,
reference_baudrate=reference_baudrate)
this_node_out_power = this_node.get_per_degree_ref_power(degree=node.uid, ref_carrier=ref_carrier)
# use the target power on this degree
prev_dp = this_node_out_power - pref_ch_db
dp = prev_dp
@@ -333,22 +334,24 @@ def set_egress_amplifier(network, this_node, equipment, pref_ch_db, pref_total_d
def set_roadm_per_degree_targets(roadm, network):
"""Set target powers/PSD on all degrees
This is needed to populate per_degree_pch_out_dbm or per_degree_pch_psd dicts when they are
not initialized by users.
This is needed to populate per_degree_pch_out_dbm or per_degree_pch_psd or per_degree_pch_psw dicts when
they are not initialized by users.
"""
next_oms = (n for n in network.successors(roadm) if not isinstance(n, elements.Transceiver))
for node in next_oms:
# go through all the OMS departing from the ROADM
if node.uid not in roadm.per_degree_pch_out_dbm and node.uid not in roadm.per_degree_pch_psd:
if node.uid not in roadm.per_degree_pch_out_dbm and node.uid not in roadm.per_degree_pch_psd and \
node.uid not in roadm.per_degree_pch_psw:
# if no target power is defined on this degree or no per degree target power is given use the global one
if roadm.params.target_pch_out_db:
roadm.per_degree_pch_out_dbm[node.uid] = roadm.params.target_pch_out_db
elif roadm.params.target_psd_out_mWperGHz:
roadm.per_degree_pch_psd[node.uid] = roadm.params.target_psd_out_mWperGHz
elif roadm.params.target_out_mWperSlotWidth:
roadm.per_degree_pch_psw[node.uid] = roadm.params.target_out_mWperSlotWidth
else:
raise ConfigurationError(roadm.uid,
'needs a target_pch_out_db or a target_psd_out_mWperGHz')
raise ConfigurationError(roadm.uid, 'needs an equalization target')
def add_roadm_booster(network, roadm):

View File

@@ -90,13 +90,15 @@ class RoadmParams(Parameters):
def __init__(self, **kwargs):
self.target_pch_out_db = kwargs.get('target_pch_out_db')
self.target_psd_out_mWperGHz = kwargs.get('target_psd_out_mWperGHz')
equalisation_type = ['target_pch_out_db', 'target_psd_out_mWperGHz']
self.target_out_mWperSlotWidth = kwargs.get('target_out_mWperSlotWidth')
equalisation_type = ['target_pch_out_db', 'target_psd_out_mWperGHz', 'target_out_mWperSlotWidth']
temp = [kwargs.get(k) is not None for k in equalisation_type]
if sum(temp) > 1:
raise ParametersError('ROADM config contains more than one equalisation type.'
+ 'Please choose only one', kwargs)
self.per_degree_pch_out_db = kwargs.get('per_degree_pch_out_db', {})
self.per_degree_pch_psd = kwargs.get('per_degree_psd_out_mWperGHz', {})
self.per_degree_pch_psw = kwargs.get('per_degree_psd_out_mWperSlotWidth', {})
try:
self.add_drop_osnr = kwargs['add_drop_osnr']
self.pmd = kwargs['pmd']

View File

@@ -105,19 +105,19 @@ class Roadm(_JsonThing):
def __init__(self, **kwargs):
# If equalization is not defined in equipment, then raise an error.
# else use the one defined in equipment. Only one type of equalization
# must be defined: power (target_pch_out_db) or PSD (target_psd_out_mWperGHz)
equalisation_type = ['target_pch_out_db', 'target_psd_out_mWperGHz']
temp = [k in kwargs for k in equalisation_type]
if sum(temp) > 1:
# Only one type of equalization must be defined.
allowed_equalisations = ['target_pch_out_db', 'target_psd_out_mWperGHz', 'target_out_mWperSlotWidth']
requested_eq_mask = [eq in kwargs for eq in allowed_equalisations]
if sum(requested_eq_mask) > 1:
raise EquipmentConfigError('Only one equalization type should be set in ROADM, found: '
+ ', '.join(temp))
for key in equalisation_type:
+ ', '.join(eq for eq in allowed_equalisations if eq in kwargs))
if not any(requested_eq_mask):
raise EquipmentConfigError('No equalization type set in ROADM')
for key in allowed_equalisations:
if key in kwargs:
setattr(self, key, kwargs[key])
break
if not any(temp):
raise EquipmentConfigError('At least one default equalization type should be set in ROADM')
self.update_attr(self.default_values, kwargs, 'Roadm')
@@ -486,7 +486,7 @@ def network_from_json(json_data, equipment):
# if more than one equalization was defined in element config, then raise an error
extra_params = merge_equalization(temp, extra_params)
if not extra_params:
raise ConfigurationError(f'ROADM {el_config["uid"]} has incorrect configuration, check the equalization settings')
raise ConfigurationError(f'ROADM {el_config["uid"]}: invalid equalization settings')
temp = merge_amplifier_restrictions(temp, extra_params)
el_config['params'] = temp
el_config['type_variety'] = variety
@@ -700,9 +700,10 @@ def merge_equalization(params, extra_params):
"""params contains ROADM element config and extra_params default values from equipment library.
If equalization is not defined in ROADM element use the one defined in equipment library.
Only one type of equalization must be defined: power (target_pch_out_db) or PSD (target_psd_out_mWperGHz)
or PSW (target_out_mWperSlotWidth)
params and extra_params are dict
"""
equalization_types = ['target_pch_out_db', 'target_psd_out_mWperGHz']
equalization_types = ['target_pch_out_db', 'target_psd_out_mWperGHz', 'target_out_mWperSlotWidth']
roadm_equalizations = find_equalisation(params, equalization_types)
if sum(roadm_equalizations.values()) > 1:
# if ROADM config contains more than one equalization type then this is an error

View File

@@ -345,7 +345,8 @@ def ref_carrier(equipment):
req_power records the power in W that the user has defined for a given request
(which might be different from the one used for the design).
"""
return ReferenceCarrier(baud_rate=equipment['SI']['default'].baud_rate)
return ReferenceCarrier(baud_rate=equipment['SI']['default'].baud_rate,
slot_width=equipment['SI']['default'].spacing)
def propagate(path, req, equipment):

View File

@@ -74,7 +74,8 @@ def si(nch_and_spacing, bw):
f_min = 191.3e12
f_max = automatic_fmax(f_min, spacing, nb_channel)
return create_input_spectral_information(f_min=f_min, f_max=f_max, roll_off=0.15, baud_rate=bw, power=1e-3,
spacing=spacing, tx_osnr=40.0, ref_carrier=ReferenceCarrier(baud_rate=32e9))
spacing=spacing, tx_osnr=40.0,
ref_carrier=ReferenceCarrier(baud_rate=32e9, slot_width=50e9))
@pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)])

View File

@@ -33,11 +33,16 @@ NETWORK_FILENAME = TEST_DIR / 'data/testTopology_expected.json'
[('east edfa in Lannion_CAS to Morlaix', 'target_pch_out_db', -20, -20, [-20, -20, -20, -20, -20]),
('east edfa in Lannion_CAS to Morlaix', 'target_psd_out_mWperGHz', 5e-4, -17.9588,
[-17.9588, -16.7778, -14.9485, -16.7778, -17.9588]),
('east edfa in Lannion_CAS to Morlaix', 'target_out_mWperSlotWidth', 3e-4, -18.2390,
[-19.4885, -18.2390, -16.4781, -18.2390, -19.4885]),
('east edfa in Lannion_CAS to Corlay', 'target_pch_out_db', -20, -16, [-16, -16, -16, -16, -16]),
('east edfa in Lannion_CAS to Corlay', 'target_psd_out_mWperGHz', 5e-4, -16, [-16, -16, -16, -16, -16]),
('east edfa in Lannion_CAS to Corlay', 'target_out_mWperSlotWidth', 5e-4, -16, [-16, -16, -16, -16, -16]),
('east edfa in Lannion_CAS to Stbrieuc', 'target_pch_out_db', -20, -17.16699,
[-17.16698771, -15.98599459, -14.15668776, -15.98599459, -17.16698771]),
('east edfa in Lannion_CAS to Stbrieuc', 'target_psd_out_mWperGHz', 5e-4, -17.16699,
[-17.16698771, -15.98599459, -14.15668776, -15.98599459, -17.16698771]),
('east edfa in Lannion_CAS to Stbrieuc', 'target_out_mWperSlotWidth', 5e-4, -17.16699,
[-17.16698771, -15.98599459, -14.15668776, -15.98599459, -17.16698771])])
@pytest.mark.parametrize('delta_pdb_per_channel', [[0, 0, 0, 0, 0], [1, 3, 0, -5, 0]])
def test_equalization_combination_degree(delta_pdb_per_channel, degree, equalization_type, target,
@@ -45,6 +50,7 @@ def test_equalization_combination_degree(delta_pdb_per_channel, degree, equaliza
"""Check that ROADM correctly computes power of thr reference channel based on different
combination of equalization for ROADM and per degree
"""
roadm_config = {
"uid": "roadm Lannion_CAS",
"params": {
@@ -69,7 +75,7 @@ def test_equalization_combination_degree(delta_pdb_per_channel, degree, equaliza
slot_width = array([37.5e9, 50e9, 75e9, 50e9, 37.5e9])
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
ref_carrier = ReferenceCarrier(baud_rate=32e9)
ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
pref = Pref(p_span0=0, p_spani=0, ref_carrier=ref_carrier)
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
signal=signal, baud_rate=baud_rate, roll_off=0.15,
@@ -95,7 +101,8 @@ def test_equalization_combination_degree(delta_pdb_per_channel, degree, equaliza
assert_allclose(expected_si, roadm.get_per_degree_power(degree, spectral_info=si), rtol=1e-3)
def test_wrong_element_config():
@pytest.mark.parametrize('equalization_type', ["target_psd_out_mWperGHz", "target_out_mWperSlotWidth"])
def test_wrong_element_config(equalization_type):
"""Check that 2 equalization correcty raise a config error
"""
roadm_config = {
@@ -103,7 +110,7 @@ def test_wrong_element_config():
"params": {
"per_degree_pch_out_db": {},
"target_pch_out_db": -20,
"target_psd_out_mWperGHz": 3.125e-4,
equalization_type: 3.125e-4,
"add_drop_osnr": 38,
"pmd": 0,
"pdl": 0,
@@ -152,6 +159,7 @@ def test_merge_equalization():
roadm = [n for n in network.nodes()][0]
assert roadm.target_pch_out_dbm is None
assert roadm.target_psd_out_mWperGHz == 3.125e-4
assert roadm.target_out_mWperSlotWidth is None
json_data = {
"elements": [{
"uid": "roadm Brest_KLA",
@@ -163,6 +171,7 @@ def test_merge_equalization():
roadm = [n for n in network.nodes()][0]
assert roadm.target_pch_out_dbm == -18
assert roadm.target_psd_out_mWperGHz is None
assert roadm.target_out_mWperSlotWidth is None
json_data = {
"elements": [{
"uid": "roadm Brest_KLA",
@@ -174,6 +183,19 @@ def test_merge_equalization():
roadm = [n for n in network.nodes()][0]
assert roadm.target_pch_out_dbm is None
assert roadm.target_psd_out_mWperGHz == 5e-4
assert roadm.target_out_mWperSlotWidth is None
json_data = {
"elements": [{
"uid": "roadm Brest_KLA",
"type": "Roadm",
"params": {"target_out_mWperSlotWidth": 3e-4}}],
"connections": []
}
network = network_from_json(json_data, equipment)
roadm = [n for n in network.nodes()][0]
assert roadm.target_pch_out_dbm is None
assert roadm.target_psd_out_mWperGHz is None
assert roadm.target_out_mWperSlotWidth == 3e-4
@pytest.mark.parametrize('target_out, delta_pdb_per_channel, correction',
@@ -191,7 +213,7 @@ def test_low_input_power(target_out, delta_pdb_per_channel, correction):
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
target = target_out + array(delta_pdb_per_channel)
ref_carrier = ReferenceCarrier(baud_rate=32e9)
ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
pref = Pref(p_span0=0, p_spani=-20, ref_carrier=ref_carrier)
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
signal=signal, baud_rate=baud_rate, roll_off=0.15,
@@ -243,7 +265,7 @@ def test_2low_input_power(target_out, delta_pdb_per_channel, correction):
baud_rate = array([32e9, 42e9, 64e9, 42e9, 32e9])
signal = dbm2watt(array([-20.0, -18.0, -22.0, -25.0, -16.0]))
target = psd2powerdbm(target_out, baud_rate) + array(delta_pdb_per_channel)
ref_carrier = ReferenceCarrier(baud_rate=32e9)
ref_carrier = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
pref = Pref(p_span0=0, p_spani=-20, ref_carrier=ref_carrier)
si = create_arbitrary_spectral_information(frequency=frequency, slot_width=slot_width,
signal=signal, baud_rate=baud_rate, roll_off=0.15,
@@ -381,30 +403,23 @@ def test_initial_spectrum_not_identical():
assert_raises(AssertionError, assert_array_equal, infos_expected.number_of_channels, infos_actual.number_of_channels)
@pytest.mark.parametrize('with_initial_spectrum', [None, 0, +2, -0.5])
def test_target_psd_out_mwperghz(with_initial_spectrum):
""" checks that if target_psd_out_mWperGHz is defined, it is used as equalization, and it gives same result if
computed target is the same
@pytest.mark.parametrize('equalization, target_value', [
('target_out_mWperSlotWidth', power_dbm_to_psd_mw_ghz(-20, 50e9)),
('target_psd_out_mWperGHz', power_dbm_to_psd_mw_ghz(-20, 32e9))])
@pytest.mark.parametrize('power_dbm', [0, 2, -0.5])
def test_target_psd_or_psw(power_dbm, equalization, target_value):
""" checks that if target_out_mWperSlotWidth or target_psd_out_mWperGHz is defined, it is used as equalization
and it gives same result if computed target is the same
"""
equipment = load_equipment(EQPT_FILENAME)
network = net_setup(equipment)
req = create_voyager_req(equipment, 'trx Brest_KLA', 'trx Vannes_KBE', False, ['trx Vannes_KBE'], ['STRICT'],
'mode 1', 50e9, with_initial_spectrum)
if with_initial_spectrum:
temp = [{
"f_min": 191.35e12, # align f_min , f_max on Voyager f_min, f_max and not SI !
"f_max": 196.05e12,
"baud_rate": req.baud_rate,
"slot_width": 50e9,
"roll_off": 0.15,
"tx_osnr": 40
}]
req.initial_spectrum = _spectrum_from_json(temp)
'mode 1', 50e9, power_dbm)
path = compute_constrained_path(network, req)
infos_expected = propagate(path, req, equipment)
# change default equalization to power spectral density
delattr(equipment['Roadm']['default'], 'target_pch_out_db')
setattr(equipment['Roadm']['default'], 'target_psd_out_mWperGHz', power_dbm_to_psd_mw_ghz(-20, 32e9))
setattr(equipment['Roadm']['default'], equalization, target_value)
# create a second instance with this roadm settings,
network2 = net_setup(equipment)
path2 = compute_constrained_path(network2, req)
@@ -470,11 +485,12 @@ def test_target_psd_out_mwperghz_deltap(deltap):
assert expected_gain == pytest.approx(actual_gain, rel=1e-3)
@pytest.mark.parametrize('equalization', ['target_psd_out_mWperGHz', 'target_out_mWperSlotWidth'])
@pytest.mark.parametrize('case', ['SI', 'nodes'])
@pytest.mark.parametrize('deltap', [0, +2, -0.5])
@pytest.mark.parametrize('target', [-20, -21, -18])
@pytest.mark.parametrize('mode, slot_width', (['mode 1', 50e9], ['mode 2', 75e9]))
def test_equalization(case, deltap, target, mode, slot_width):
def test_equalization(case, deltap, target, mode, slot_width, equalization):
"""check that power target on roadm is correct for these cases; check on booster
- SI : target_pch_out_db / target_psd_out_mWperGHz
- node : target_pch_out_db / target_psd_out_mWperGHz
@@ -493,17 +509,16 @@ def test_equalization(case, deltap, target, mode, slot_width):
# boosters = ['east edfa in Brest_KLA to Quimper', 'east edfa in Lorient_KMA to Loudeac',
# 'east edfa in Lannion_CAS to Stbrieuc']
target_psd = power_dbm_to_psd_mw_ghz(target, 32e9)
ref = ReferenceCarrier(baud_rate=32e9)
ref = ReferenceCarrier(baud_rate=32e9, slot_width=50e9)
if case == 'SI':
delattr(equipment['Roadm']['default'], 'target_pch_out_db')
setattr(equipment['Roadm']['default'], 'target_psd_out_mWperGHz',
target_psd)
setattr(equipment['Roadm']['default'], equalization, target_psd)
network = net_setup(equipment)
elif case == 'nodes':
json_data = load_json(NETWORK_FILENAME)
for el in json_data['elements']:
if el['uid'] in roadms:
el['params'] = {'target_psd_out_mWperGHz': target_psd}
el['params'] = {equalization: target_psd}
network = network_from_json(json_data, equipment)
spectrum = equipment['SI']['default']
p_db = spectrum.power_dbm
@@ -514,6 +529,9 @@ def test_equalization(case, deltap, target, mode, slot_width):
for roadm in pw_roadms:
assert roadm.target_psd_out_mWperGHz is None
assert roadm.target_pch_out_dbm == target
for roadm in [r for r in network.nodes() if r.uid in roadms and isinstance(r, Roadm)]:
assert roadm.target_pch_out_dbm is None
assert getattr(roadm, equalization) == target_psd
path = compute_constrained_path(network, req)
si = create_input_spectral_information(
f_min=req.f_min, f_max=req.f_max, roll_off=req.roll_off, baud_rate=req.baud_rate, power=req.power,
@@ -522,8 +540,12 @@ def test_equalization(case, deltap, target, mode, slot_width):
if isinstance(el, Roadm):
si = el(si, degree=path[i + 1].uid)
if case in ['SI', 'nodes', 'degrees']:
assert_allclose(power_dbm_to_psd_mw_ghz(watt2dbm(si.signal + si.ase + si.nli), si.baud_rate),
target_psd, rtol=1e-3)
if equalization == 'target_psd_out_mWperGHz':
assert_allclose(power_dbm_to_psd_mw_ghz(watt2dbm(si.signal + si.ase + si.nli), si.baud_rate),
target_psd, rtol=1e-3)
if equalization == 'target_out_mWperSlotWidth':
assert_allclose(power_dbm_to_psd_mw_ghz(watt2dbm(si.signal + si.ase + si.nli), si.slot_width),
target_psd, rtol=1e-3)
else:
si = el(si)
print(el.uid)

View File

@@ -47,7 +47,7 @@ def propagation(input_power, con_in, con_out, dest):
spacing = 50e9 # THz
si = create_input_spectral_information(f_min=191.3e12, f_max=191.3e12 + 79 * spacing, roll_off=0.15,
baud_rate=32e9, power=p, spacing=spacing, tx_osnr=None,
ref_carrier=ReferenceCarrier(baud_rate=32e9))
ref_carrier=ReferenceCarrier(baud_rate=32e9, slot_width=50e9))
source = next(transceivers[uid] for uid in transceivers if uid == 'trx A')
sink = next(transceivers[uid] for uid in transceivers if uid == dest)
path = dijkstra_path(network, source, sink)

View File

@@ -29,7 +29,7 @@ def test_fiber():
# fix grid spectral information generation
spectral_info_input = create_input_spectral_information(f_min=191.3e12, f_max=196.1e12, roll_off=0.15,
baud_rate=32e9, power=1e-3, spacing=50e9, tx_osnr=40.0,
ref_carrier=ReferenceCarrier(baud_rate=32e9))
ref_carrier=ReferenceCarrier(baud_rate=32e9, slot_width=50e9))
# propagation
spectral_info_out = fiber(spectral_info_input)
@@ -69,7 +69,7 @@ def test_raman_fiber():
# spectral information generation
spectral_info_input = create_input_spectral_information(f_min=191.3e12, f_max=196.1e12, roll_off=0.15,
baud_rate=32e9, power=1e-3, spacing=50e9, tx_osnr=40.0,
ref_carrier=ReferenceCarrier(baud_rate=32e9))
ref_carrier=ReferenceCarrier(baud_rate=32e9, slot_width=50e9))
SimParams.set_params(load_json(TEST_DIR / 'data' / 'sim_params.json'))
fiber = RamanFiber(**load_json(TEST_DIR / 'data' / 'test_science_utils_fiber_config.json'))
@@ -107,7 +107,7 @@ def test_fiber_lumped_losses_srs(set_sim_params):
# spectral information generation
spectral_info_input = create_input_spectral_information(f_min=191.3e12, f_max=196.1e12, roll_off=0.15,
baud_rate=32e9, power=1e-3, spacing=50e9, tx_osnr=40.0,
ref_carrier=ReferenceCarrier(baud_rate=32e9))
ref_carrier=ReferenceCarrier(baud_rate=32e9, slot_width=50e9))
SimParams.set_params(load_json(TEST_DIR / 'data' / 'sim_params.json'))
fiber = Fiber(**load_json(TEST_DIR / 'data' / 'test_lumped_losses_raman_fiber_config.json'))