mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2026-01-27 10:21:48 +00:00
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:
115
docs/json.rst
115
docs/json.rst
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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']
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'))
|
||||
|
||||
Reference in New Issue
Block a user