From b0ac41e2d517d4af3556705b55928a96cf8cd26b Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 28 Feb 2025 19:28:44 +0100 Subject: [PATCH] fix: PMD was not correctly read from excel or exported from json Signed-off-by: EstherLerouzic Change-Id: I1069b07dfb62bf94d4f591908c034df4e49ce22a --- gnpy/core/elements.py | 27 +++--- gnpy/core/parameters.py | 5 ++ gnpy/core/utils.py | 49 ++++++++++ gnpy/example-data/edfa_example_network.json | 3 +- gnpy/example-data/meshTopologyExampleV2.xls | Bin 15872 -> 29184 bytes gnpy/tools/convert.py | 85 ++++++++++++------ gnpy/tools/json_io.py | 5 +- .../spectrum1_transmission_main_example | 2 +- .../spectrum2_transmission_main_example | 2 +- tests/invocation/transmission_main_example | 2 +- tests/test_logger.py | 4 +- 11 files changed, 141 insertions(+), 43 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 010e182f..bfdd4788 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -891,18 +891,23 @@ class Fiber(_Node): :return Dict[str, Any]: JSON representation of the fiber. """ + params = { + # have to specify each because namedtupple cannot be updated :( + 'length': round(self.params.length * 1e-3, 6), + 'loss_coef': round(self.params.loss_coef * 1e3, 6), + 'length_units': 'km', + 'att_in': self.params.att_in, + 'con_in': self.params.con_in, + 'con_out': self.params.con_out + } + # Export pmd_coef only if it is not a default from library. + if self.params.pmd_coef_defined: + params['pmd_coef'] = self.params.pmd_coef + return {'uid': self.uid, 'type': type(self).__name__, 'type_variety': self.type_variety, - 'params': { - # have to specify each because namedtupple cannot be updated :( - 'length': round(self.params.length * 1e-3, 6), - 'loss_coef': round(self.params.loss_coef * 1e3, 6), - 'length_units': 'km', - 'att_in': self.params.att_in, - 'con_in': self.params.con_in, - 'con_out': self.params.con_out - }, + 'params': params, 'metadata': { 'location': self.metadata['location']._asdict() } @@ -1025,7 +1030,7 @@ class Fiber(_Node): else: wavelength = c / frequency dispersion = self.params.dispersion + self.params.dispersion_slope * \ - (wavelength - c / self.params.f_dispersion_ref) + (wavelength - c / self.params.f_dispersion_ref) # noqa E127 beta2 = -((c / frequency) ** 2 * dispersion) / (2 * pi * c) return beta2 @@ -1048,7 +1053,7 @@ class Fiber(_Node): dispersion_slope = self.params.dispersion_slope beta2 = self.beta2(frequency) beta3 = (dispersion_slope - (4 * pi * frequency ** 3 / c ** 2) * beta2) / ( - 2 * pi * frequency ** 2 / c) ** 2 + 2 * pi * frequency ** 2 / c) ** 2 # noqa E127 return beta3 def gamma(self, frequency=None): diff --git a/gnpy/core/parameters.py b/gnpy/core/parameters.py index c96772e8..0269208b 100644 --- a/gnpy/core/parameters.py +++ b/gnpy/core/parameters.py @@ -343,6 +343,7 @@ class FiberParams(Parameters): # Polarization Mode Dispersion self._pmd_coef = kwargs['pmd_coef'] # s/sqrt(m) + self._pmd_coef_defined = kwargs.get('pmd_coef_defined', kwargs['pmd_coef'] is True) # Loss Coefficient if isinstance(kwargs['loss_coef'], dict): @@ -428,6 +429,10 @@ class FiberParams(Parameters): def pmd_coef(self): return self._pmd_coef + @property + def pmd_coef_defined(self): + return self._pmd_coef_defined + @property def ref_wavelength(self): return self._ref_wavelength diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 4e7a7c33..463a532a 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -329,6 +329,35 @@ def merge_amplifier_restrictions(dict1, dict2): return copy_dict1 +def use_pmd_coef(dict1: dict, dict2: dict): + """If Fiber dict1 is missing the pmd_coef value then use the one of dict2. + In addition records in "pmd_coef_defined" key the pmd_coef if is was defined in dict1. + + :param dict1: A dictionnary that contains "pmd_coef" key. + :type dict1: dict + :param dict2: Another dictionnary that contains "pmd_coef" key. + :type dict2: dict + + >>> dict1 = {'a': 1, 'pmd_coef': 1.5e-15} + >>> dict2 = {'a': 2, 'pmd_coef': 2e-15} + >>> use_pmd_coef(dict1, dict2) + >>> dict1 + {'a': 1, 'pmd_coef': 1.5e-15, 'pmd_coef_defined': True} + + >>> dict1 = {'a': 1} + >>> use_pmd_coef(dict1, dict2) + >>> dict1 + {'a': 1, 'pmd_coef_defined': False, 'pmd_coef': 2e-15} + """ + if 'pmd_coef' in dict1 and not dict1['pmd_coef'] \ + or ('pmd_coef' not in dict1 and 'pmd_coef' in dict2): + dict1['pmd_coef_defined'] = False + dict1['pmd_coef'] = dict2['pmd_coef'] + elif 'pmd_coef' in dict1 and dict1['pmd_coef']: + dict1['pmd_coef_defined'] = True + # all other case do not need any change + + def silent_remove(this_list, elem): """Remove matching elements from a list without raising ValueError @@ -558,3 +587,23 @@ def transform_data(data: str) -> Union[List[int], None]: if isinstance(data, str): return [int(x) for x in data.split(' | ')] return None + + +def convert_pmd_lineic(pmd: Union[float, None], length: float, length_unit: str) -> Union[float, None]: + """Convert PMD value of the span in ps into pmd_lineic in s/sqrt(km) + + :param pmd: value in ps + :type pmd: Union[float, None] + :param length: value in length_unit + :type length: float + :param length_unit: 'km' or 'm' + :type length_unit: str + :return: lineic PMD s/sqrt(m) + :rtype: Union[float, None] + + >>> convert_pmd_lineic(10, 0.001, 'km') + 1e-11 + """ + if pmd: + return pmd * 1e-12 / sqrt(convert_length(length, length_unit)) + return None diff --git a/gnpy/example-data/edfa_example_network.json b/gnpy/example-data/edfa_example_network.json index 861be903..1a8ed91a 100644 --- a/gnpy/example-data/edfa_example_network.json +++ b/gnpy/example-data/edfa_example_network.json @@ -23,7 +23,8 @@ "length_units": "km", "att_in": 0, "con_in": 0.5, - "con_out": 0.5 + "con_out": 0.5, + "pmd_coef": 3.0e-15 }, "metadata": { "location": { diff --git a/gnpy/example-data/meshTopologyExampleV2.xls b/gnpy/example-data/meshTopologyExampleV2.xls index 0704900e086a6a80074e77968886a5fd51c0b198..93ca00dca2c655458044bf7756267877905e9a1d 100644 GIT binary patch literal 29184 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 literal 15872 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 P|Nri733eO59r^zUnr}Gk diff --git a/gnpy/tools/convert.py b/gnpy/tools/convert.py index 0e6ed51e..d8df962f 100755 --- a/gnpy/tools/convert.py +++ b/gnpy/tools/convert.py @@ -33,7 +33,7 @@ from xlrd.sheet import Sheet from xlrd.biffh import XLRDError from networkx import DiGraph -from gnpy.core.utils import silent_remove, transform_data +from gnpy.core.utils import silent_remove, transform_data, convert_pmd_lineic from gnpy.core.exceptions import NetworkTopologyError from gnpy.core.elements import Edfa, Fused, Fiber @@ -170,7 +170,7 @@ class Link: 'east_lineic': 0.2, 'east_con_in': None, 'east_con_out': None, - 'east_pmd': 0.1, + 'east_pmd': None, 'east_cable': '' } @@ -636,6 +636,62 @@ def create_west_eqpt_element(node: Node, nodes_by_city: Dict[str, Node]) -> dict return eqpt +def create_east_fiber_element(fiber: Node, nodes_by_city: Dict[str, Node]) -> Dict: + """Create fibers json elements for the east direction. + + :param fiber: The Node object representing the equipment. + :type fiber: Node + :param nodes_by_city: A dictionary mapping city names to Node objects. + :type nodes_by_city: Dict[str, Node] + :return: A dictionary representing the west equipment element in JSON format. + :rtype: Dict + """ + fiber_dict = { + 'uid': f'fiber ({fiber.from_city} \u2192 {fiber.to_city})-{fiber.east_cable}', + 'metadata': {'location': midpoint(nodes_by_city[fiber.from_city], + nodes_by_city[fiber.to_city])}, + 'type': 'Fiber', + 'type_variety': fiber.east_fiber, + 'params': { + 'length': round(fiber.east_distance, 3), + 'length_units': fiber.distance_units, + 'loss_coef': fiber.east_lineic, + 'con_in': fiber.east_con_in, + 'con_out': fiber.east_con_out + } + } + if fiber.east_pmd: + fiber_dict['params']['pmd_coef'] = convert_pmd_lineic(fiber.east_pmd, fiber.east_distance, fiber.distance_units) + return fiber_dict + + +def create_west_fiber_element(fiber: Node, nodes_by_city: Dict[str, Node]) -> Dict: + """Create fibers json elements for the west direction. + + :param fiber: The Node object representing the equipment. + :type fiber: Node + :param nodes_by_city: A dictionary mapping city names to Node objects. + :type nodes_by_city: Dict[str, Node] + :return: A dictionary representing the west equipment element in JSON format. + :rtype: Dict + """ + fiber_dict = { + 'uid': f'fiber ({fiber.to_city} \u2192 {fiber.from_city})-{fiber.west_cable}', + 'metadata': {'location': midpoint(nodes_by_city[fiber.from_city], + nodes_by_city[fiber.to_city])}, + 'type': 'Fiber', + 'type_variety': fiber.west_fiber, + 'params': {'length': round(fiber.west_distance, 3), + 'length_units': fiber.distance_units, + 'loss_coef': fiber.west_lineic, + 'con_in': fiber.west_con_in, + 'con_out': fiber.west_con_out} + } + if fiber.west_pmd: + fiber_dict['params']['pmd_coef'] = convert_pmd_lineic(fiber.west_pmd, fiber.west_distance, fiber.distance_units) + return fiber_dict + + def xls_to_json_data(input_filename: Path, filter_region: List[str] = None) -> dict: """Read the Excel sheets and produce the JSON dict in GNPy format (legacy). @@ -698,29 +754,8 @@ def xls_to_json_data(input_filename: Path, filter_region: List[str] = None) -> d 'longitude': x.longitude}}, 'type': 'Fused'} for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] - + [{'uid': f'fiber ({x.from_city} \u2192 {x.to_city})-{x.east_cable}', - 'metadata': {'location': midpoint(nodes_by_city[x.from_city], - nodes_by_city[x.to_city])}, - 'type': 'Fiber', - 'type_variety': x.east_fiber, - 'params': {'length': round(x.east_distance, 3), - 'length_units': x.distance_units, - 'loss_coef': x.east_lineic, - 'con_in': x.east_con_in, - 'con_out': x.east_con_out} - } - for x in links] - + [{'uid': f'fiber ({x.to_city} \u2192 {x.from_city})-{x.west_cable}', - 'metadata': {'location': midpoint(nodes_by_city[x.from_city], - nodes_by_city[x.to_city])}, - 'type': 'Fiber', - 'type_variety': x.west_fiber, - 'params': {'length': round(x.west_distance, 3), - 'length_units': x.distance_units, - 'loss_coef': x.west_lineic, - 'con_in': x.west_con_in, - 'con_out': x.west_con_out} - } for x in links] + + [create_east_fiber_element(x, nodes_by_city) for x in links] + + [create_west_fiber_element(x, nodes_by_city) for x in links] + [{'uid': f'west edfa in {x.city}', 'metadata': {'location': {'city': x.city, 'region': x.region, diff --git a/gnpy/tools/json_io.py b/gnpy/tools/json_io.py index f36e71cf..25d96439 100644 --- a/gnpy/tools/json_io.py +++ b/gnpy/tools/json_io.py @@ -23,7 +23,7 @@ from gnpy.core.equipment import trx_mode_params, find_type_variety from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError, ServiceError from gnpy.core.science_utils import estimate_nf_model from gnpy.core.info import Carrier -from gnpy.core.utils import automatic_nch, automatic_fmax, merge_amplifier_restrictions, dbm2watt +from gnpy.core.utils import automatic_nch, automatic_fmax, merge_amplifier_restrictions, dbm2watt, use_pmd_coef from gnpy.core.parameters import DEFAULT_RAMAN_COEFFICIENT, EdfaParams, MultiBandParams, DEFAULT_EDFA_CONFIG from gnpy.topology.request import PathRequest, Disjunction, compute_spectrum_slot_vs_bandwidth, ResultElement from gnpy.topology.spectrum_assignment import mvalue_to_slots @@ -687,6 +687,9 @@ def network_from_json(json_data: dict, equipment: dict) -> DiGraph: if not extra_params: msg = f'ROADM {el_config["uid"]}: invalid equalization settings' raise ConfigurationError(msg) + # use temp pmd_coef if it exists else use the default one from library and keep this knowledge in + # pmd_coef_defined + use_pmd_coef(temp, extra_params) temp = merge_amplifier_restrictions(temp, extra_params) el_config['params'] = temp el_config['type_variety'] = variety diff --git a/tests/invocation/spectrum1_transmission_main_example b/tests/invocation/spectrum1_transmission_main_example index d38d3fe8..33ddb28d 100644 --- a/tests/invocation/spectrum1_transmission_main_example +++ b/tests/invocation/spectrum1_transmission_main_example @@ -101,7 +101,7 @@ Transceiver trx Lorient_KMA OSNR ASE (0.1nm, dB): 23.89 OSNR ASE (signal bw, dB): 19.81 CD (ps/nm): 2171.00 - PMD (ps): 0.46 + PMD (ps): 3.03 PDL (dB): 0.00 Latency (ms): 0.64 Actual pch out (dBm): 0.00 diff --git a/tests/invocation/spectrum2_transmission_main_example b/tests/invocation/spectrum2_transmission_main_example index 727764d3..d21bd8da 100644 --- a/tests/invocation/spectrum2_transmission_main_example +++ b/tests/invocation/spectrum2_transmission_main_example @@ -101,7 +101,7 @@ Transceiver trx Lorient_KMA OSNR ASE (0.1nm, dB): mode_1: 23.91, mode_2: 23.87 OSNR ASE (signal bw, dB): mode_1: 19.83, mode_2: 16.78 CD (ps/nm): 2171.00 - PMD (ps): 0.46 + PMD (ps): 3.03 PDL (dB): 0.00 Latency (ms): 0.64 Actual pch out (dBm): mode_1: 0.00, mode_2: 0.00 diff --git a/tests/invocation/transmission_main_example b/tests/invocation/transmission_main_example index a6dc6e2c..fc4c542e 100644 --- a/tests/invocation/transmission_main_example +++ b/tests/invocation/transmission_main_example @@ -52,7 +52,7 @@ Transceiver Site_B OSNR ASE (0.1nm, dB): 33.30 OSNR ASE (signal bw, dB): 29.21 CD (ps/nm): 1336.00 - PMD (ps): 0.36 + PMD (ps): 0.85 PDL (dB): 0.00 Latency (ms): 0.39 Actual pch out (dBm): 0.00 diff --git a/tests/test_logger.py b/tests/test_logger.py index 7e9f9064..3aff1f42 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -377,8 +377,8 @@ def wrong_element(): "expected_msg": "Config error in fiber (ILA2 → ILA1): " + "Fiber configurations json must include \'length_units\'. Configuration: " + "{\'length\': 100.0, \'loss_coef\': 0.2, \'att_in\': 0, \'con_in\': 0, \'con_out\': 0, " - + "\'type_variety\': \'SSMF\', \'dispersion\': 1.67e-05, \'effective_area\': 8.3e-11, " - + "\'pmd_coef\': 1.265e-15}" + + "\'pmd_coef_defined': False, \'pmd_coef\': 1.265e-15, " + + "\'type_variety\': \'SSMF\', \'dispersion\': 1.67e-05, \'effective_area\': 8.3e-11}" }) return data