From e143d253391ae03e56c5a3f7e49f68d2267c7793 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 5 Aug 2021 15:07:35 +0200 Subject: [PATCH] Add a user defined initial spectrum in propagation functions A new function is added to build spectrum information based on the actual mixture of channels to be simulated (baud rate, slot width, power per frequency). Propagation function is changed so that, if the user defines a specific distribution, then it uses it, else it uses as before, all identical channels based on the initial request. In this case, as before this change, we assume full load, with same channel for the spectral info and not the resulting mixt of channels after routing. Signed-off-by: EstherLerouzic Change-Id: Icf56396837b77009e98accd27fcebd2dded6d112 --- gnpy/core/info.py | 48 ++++++++++++++++++++++++++++++++++++++++ gnpy/topology/request.py | 35 +++++++++++++++++++++++------ 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/gnpy/core/info.py b/gnpy/core/info.py index 3fbb52e1..f5a58e65 100644 --- a/gnpy/core/info.py +++ b/gnpy/core/info.py @@ -12,6 +12,7 @@ from __future__ import annotations from collections import namedtuple from collections.abc import Iterable from typing import Union +from dataclasses import dataclass from numpy import argsort, mean, array, append, ones, ceil, any, zeros, outer, full, ndarray, asarray from gnpy.core.utils import automatic_nch, db2lin, watt2dbm @@ -292,3 +293,50 @@ def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power, return create_arbitrary_spectral_information(frequency, slot_width=spacing, signal=power, baud_rate=baud_rate, roll_off=roll_off, delta_pdb_per_channel=delta_pdb_per_channel, ref_power=Pref(p_span0=p_span0, p_spani=p_spani)) + + +def carriers_to_spectral_information(initial_spectrum: dict[Union[int, float], Carrier], + ref_carrier: ReferenceCarrier) -> SpectralInformation: + """Initial spectrum is a dict with key = carrier frequency, and value a Carrier object. + :param initial_spectrum: indexed by frequency in Hz, with power offset (delta_pdb), baudrate, slot width + and roll off. + :param ref_carrier: reference carrier (baudrate and power) used for the reference channel + """ + frequency = list(initial_spectrum.keys()) + signal = [ref_carrier.req_power * db2lin(c.delta_pdb) for c in initial_spectrum.values()] + roll_off = [c.roll_off for c in initial_spectrum.values()] + baud_rate = [c.baud_rate for c in initial_spectrum.values()] + delta_pdb_per_channel = array([c.delta_pdb for c in initial_spectrum.values()]) + slot_width = [c.slot_width for c in initial_spectrum.values()] + p_span0 = watt2dbm(ref_carrier.req_power) + p_spani = watt2dbm(ref_carrier.req_power) + return create_arbitrary_spectral_information(frequency=frequency, signal=signal, baud_rate=baud_rate, + slot_width=slot_width, roll_off=roll_off, + delta_pdb_per_channel=delta_pdb_per_channel, + ref_power=Pref(p_span0=p_span0, p_spani=p_spani)) + + +@dataclass +class Carrier: + """One channel in the initial mixed-type spectrum definition, each type being defined by + its delta_pdb (power offset with respect to reference power), baud rate, slot_width, roll_off + and tx_osnr. delta_pdb offset is applied to target power out of Roadm. + """ + delta_pdb: float + baud_rate: float + slot_width: float + roll_off: float + + +@dataclass +class ReferenceCarrier: + """Reference channel is used during autodesign to determine target power + based on power spectral density values during propagation in ROADMs for equalization purpose. + It is also required to correctly compute the loss experienced by p_span_i in Roadm element. + + In typical scenarios, users would pick a 32 GBaud channel at 0dBm, which will + neatly lead to the same power spectral density for a 64 GBaud channel at 3 dBm. + Other attributes (like slot_width or roll-off) may be added there for future equalization purpose. + """ + baud_rate: float + req_power: float diff --git a/gnpy/topology/request.py b/gnpy/topology/request.py index c95d8592..65ec5dd2 100644 --- a/gnpy/topology/request.py +++ b/gnpy/topology/request.py @@ -23,7 +23,7 @@ from networkx.utils import pairwise from numpy import mean, argmin from gnpy.core.elements import Transceiver, Roadm from gnpy.core.utils import lin2db -from gnpy.core.info import create_input_spectral_information +from gnpy.core.info import create_input_spectral_information, carriers_to_spectral_information, ReferenceCarrier from gnpy.core.exceptions import ServiceError, DisjunctionError import gnpy.core.ansi_escapes as ansi_escapes from copy import deepcopy @@ -72,6 +72,7 @@ class PathRequest: if params.effective_freq_slot is not None: self.N = params.effective_freq_slot['N'] self.M = params.effective_freq_slot['M'] + self.initial_spectrum = None def __str__(self): return '\n\t'.join([f'{type(self).__name__} {self.request_id}', @@ -339,10 +340,24 @@ def compute_constrained_path(network, req): return total_path +def ref_carrier(req_power, equipment): + """Create a reference carier based SI information with the specified request's power: + 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, req_power=req_power) + + def propagate(path, req, equipment): - si = create_input_spectral_information( - req.f_min, req.f_max, req.roll_off, req.baud_rate, - req.power, req.spacing) + """ propagates signals in each element according to initial spectrum set by user + """ + if req.initial_spectrum is not None: + si = carriers_to_spectral_information(initial_spectrum=req.initial_spectrum, + ref_carrier=ref_carrier(req.power, equipment)) + else: + si = create_input_spectral_information( + req.f_min, req.f_max, req.roll_off, req.baud_rate, + req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): si = el(si, degree=path[i+1].uid) @@ -378,9 +393,15 @@ def propagate_and_optimize_mode(path, req, equipment): # step2: computes propagation for each baudrate: stop and select the first that passes # TODO: the case of roll of is not included: for now use SI one # TODO: if the loop in mode optimization does not have a feasible path, then bugs - spc_info = create_input_spectral_information(req.f_min, req.f_max, - equipment['SI']['default'].roll_off, - this_br, req.power, req.spacing) + if req.initial_spectrum is not None: + # this case is not yet handled: spectrum can not be defined for the path-request-run function + # and this function is only called in this case. so coming here should not be considered yet. + msg = f'Request: {req.request_id} contains a unexpected initial_spectrum.' + LOGGER.critical(msg) + raise ServiceError(msg) + spc_info = create_input_spectral_information(f_min=req.f_min, f_max=req.f_max, + roll_off=equipment['SI']['default'].roll_off, + baud_rate=this_br, power=req.power, spacing=req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): spc_info = el(spc_info, degree=path[i+1].uid)