From b810cf84c2fbcf7de98581b5dd19e8c6221e117a Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 20 Jun 2018 14:25:15 +0200 Subject: [PATCH] Power mode implementation Signed-off-by: Jean-Luc Auge --- examples/transmission_main_example.py | 23 ++++++-- gnpy/core/elements.py | 79 +++++++++++++++++++-------- gnpy/core/info.py | 16 ++++-- gnpy/core/network.py | 22 ++++++++ 4 files changed, 105 insertions(+), 35 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 3606ca3a..c39dcc61 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -17,10 +17,9 @@ from logging import getLogger, basicConfig, INFO, ERROR, DEBUG from matplotlib.pyplot import show, axis, figure, title from networkx import (draw_networkx_nodes, draw_networkx_edges, draw_networkx_labels, dijkstra_path) - -from gnpy.core import load_network, build_network +from gnpy.core.network import load_network, build_network, set_roadm_loss, set_edfa_dp from gnpy.core.elements import Transceiver, Fiber, Edfa, Roadm -from gnpy.core.info import SpectralInformation, Channel, Power +from gnpy.core.info import SpectralInformation, Channel, Power, Pref logger = getLogger(__name__) @@ -51,14 +50,26 @@ def plot_results(network, path, source, sink): def main(network, equipment, source, sink): build_network(network, equipment=equipment) path = dijkstra_path(network, source, sink) + gain_mode = False + if gain_mode: + path_amps = [amp for amp in path if isinstance(amp, Edfa)] + set_edfa_dp(network, path_amps) spans = [s.length for s in path if isinstance(s, Fiber)] print(f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {sink.uid}') print(f'\nNow propagating between {source.uid} and {sink.uid}:') - for p in range(0, 1): #change range to sweep results across several powers in dBm - p=db2lin(p)*1e-3 + pref_span0_db = 2 + bounds = range(1, 2) #power sweep + pref_roadm_db = -20 + pref_span0 = db2lin(pref_span0_db)*1e-3 + pref_roadm = db2lin(pref_roadm_db)*1e-3 + for p_db in range(pref_span0_db+bounds.start, pref_span0_db+bounds.stop): #change range to sweep results across several powers in dBm + p = db2lin(p_db)*1e-3 + roadm_loss = pref_span0_db - pref_roadm_db + path_roadms = [roadm for roadm in path if isinstance(roadm, Roadm)] + set_roadm_loss(path_roadms, roadm_loss) spacing = 0.05 # THz - si = SpectralInformation() # SI units: W, Hz + si = SpectralInformation(pref=Pref(pref_span0_db, pref_span0_db)) si = si.update(carriers=[ Channel(f, (191.3 + spacing * f) * 1e12, 32e9, 0.15, Power(p, 0, 0)) for f in range(1,97) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index ae1798ee..47c1c473 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -107,9 +107,14 @@ class Roadm(Node): amplified_spontaneous_emission=pwr.ase/attenuation) yield carrier._replace(power=pwr) + def update_pref(self, pref): + return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) + def __call__(self, spectral_info): carriers = tuple(self.propagate(*spectral_info.carriers)) - return spectral_info.update(carriers=carriers) + pref = self.update_pref(spectral_info.pref) + print('pi p0',pref.pi, pref.p0) + return spectral_info.update(carriers=carriers, pref=pref) FusedParams = namedtuple('FusedParams', 'loss') @@ -138,10 +143,15 @@ class Fused(Node): nonlinear_interference=pwr.nli/attenuation, amplified_spontaneous_emission=pwr.ase/attenuation) yield carrier._replace(power=pwr) + + def update_pref(self, pref): + return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) def __call__(self, spectral_info): carriers = tuple(self.propagate(*spectral_info.carriers)) - return spectral_info.update(carriers=carriers) + pref = self.update_pref(spectral_info.pref) + print('pi',pref.pi) + return spectral_info.update(carriers=carriers, pref=pref) FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units dispersion gamma') @@ -259,9 +269,14 @@ class Fiber(Node): amplified_spontaneous_emission=pwr.ase/self.lin_attenuation) yield carrier._replace(power=pwr) + def update_pref(self, pref): + return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) + def __call__(self, spectral_info): carriers = tuple(self.propagate(*spectral_info.carriers)) - return spectral_info.update(carriers=carriers) + pref = self.update_pref(spectral_info.pref) + print('after fiber pi',pref.pi) + return spectral_info.update(carriers=carriers, pref=pref) # TODO|dutc: eliminate duplication with .equipment.EdfaBase EdfaParams = namedtuple('EdfaParams', @@ -297,7 +312,10 @@ class Edfa(Node): self.gprofile = None self.pin_db = None self.pout_db = None + self.dp_db = None #delta P with Pref (power swwep) in power mode + self.pref_db = None self.passive = False + self.effective_gain = self.operational.gain_target def __repr__(self): return (f'{type(self).__name__}(uid={self.uid!r}, ' @@ -316,13 +334,16 @@ class Edfa(Node): return f'{type(self).__name__} {self.uid}' nf = mean(self.nf) return '\n'.join([f'{type(self).__name__} {self.uid}', - f' type_variety: {self.params.type_variety}', - f' gain (dB): {self.operational.gain_target:.2f}', - f' noise figure (dB): {nf:.2f}', - f' Power In (dBm): {self.pin_db:.2f}', - f' Power Out (dBm): {self.pout_db:.2f}']) + f' type_variety: {self.params.type_variety}', + f' operational gain (dB): {self.operational.gain_target:.2f}', + f' effective gain(dB): {self.effective_gain}', + f' noise figure (dB): {nf:.2f}', + f' Power In (dBm): {self.pin_db:.2f}', + f' Power Out (dBm): {self.pout_db:.2f}', + f' Delta_P (dB): {self.dp_db!r}', + f' Pref (dBm): {self.pref_db!r}']) - def interpol_params(self, frequencies, pin, baud_rates): + def interpol_params(self, frequencies, pin, baud_rates, pref): """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json set the edfa class __init__ None parameters : self.channel_freq, self.nf, self.interpol_dgt and self.interpol_gain_ripple @@ -336,8 +357,15 @@ class Edfa(Node): self.pin_db = lin2db(sum(pin*1e3)) """check power saturation and correct target_gain accordingly:""" - gain_target = min(self.operational.gain_target, self.params.p_max - self.pin_db) - self.operational.gain_target = gain_target + + if self.dp_db is not None: + print('interpolate pi', pref.pi) + print('interpolate dp', self.dp_db) + self.effective_gain = self.dp_db + pref.p0 - pref.pi + self.pref_db = self.dp_db + pref.p0 + else: + self.effective_gain = self.operational.gain_target + self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db) self.nf = self._calc_nf() self.gprofile = self._gain_profile(pin) @@ -353,8 +381,8 @@ class Edfa(Node): False => polynomial fit based on self.params.nf_fit_coeff""" # TODO|jla: TBD alarm rising or input VOA padding in case # gain_min > gain_target TBD: - pad = max(self.params.gain_min - self.operational.gain_target, 0) - gain_target = self.operational.gain_target + pad + pad = max(self.params.gain_min - self.effective_gain, 0) + gain_target = self.effective_gain + pad dg = gain_target - self.params.gain_flatmax if self.params.nf_model: g1a = gain_target - self.params.nf_model.delta_p + dg @@ -473,13 +501,13 @@ class Edfa(Node): # first estimate of Er gain & VOA loss g1st = array(self.interpol_gain_ripple) + self.params.gain_flatmax \ + array(self.interpol_dgt) * dgts1 - voa = lin2db(mean(db2lin(g1st))) - self.operational.gain_target + voa = lin2db(mean(db2lin(g1st))) - self.effective_gain # second estimate of amp ch gain using the channel input profile g2nd = g1st - voa pout_db = lin2db(sum(pin*1e3*db2lin(g2nd))) - dgts2 = self.operational.gain_target - (pout_db - tot_in_power_db) + dgts2 = self.effective_gain - (pout_db - tot_in_power_db) # center estimate of amp ch gain xcent = dgts2 @@ -509,23 +537,23 @@ class Edfa(Node): slope1 = (gavg_low - gavg_cent) / (xlow - xcent) slope2 = (gavg_cent - gavg_high) / (xcent - xhigh) - if abs(self.operational.gain_target - gavg_cent) <= err_tolerance: + if abs(self.effective_gain - gavg_cent) <= err_tolerance: dgts3 = xcent - elif self.operational.gain_target < gavg_cent: - dgts3 = xcent - (gavg_cent - self.operational.gain_target) / slope1 + elif self.effective_gain < gavg_cent: + dgts3 = xcent - (gavg_cent - self.effective_gain) / slope1 else: - dgts3 = xcent + (-gavg_cent + self.operational.gain_target) / slope2 + dgts3 = xcent + (-gavg_cent + self.effective_gain) / slope2 return g1st - voa + array(self.interpol_dgt) * dgts3 - def propagate(self, *carriers): + def propagate(self, pref, *carriers): """add ase noise to the propagating carriers of SpectralInformation""" i = 0 pin = array([c.power.signal+c.power.nli+c.power.ase for c in carriers]) # pin in W freq = array([c.frequency for c in carriers]) brate = array([c.baud_rate for c in carriers]) # interpolate the amplifier vectors with the carriers freq, calculate nf & gain profile - self.interpol_params(freq, pin, brate) + self.interpol_params(freq, pin, brate, pref) gains = db2lin(self.gprofile) carrier_ases = self.noise_profile(brate) @@ -538,6 +566,11 @@ class Edfa(Node): amplified_spontaneous_emission=(pwr.ase+carrier_ase)*gain) yield carrier._replace(power=pwr) + def update_pref(self, pref): + return pref._replace(p_span0=pref.p0, p_spani=pref.pi + self.effective_gain) + def __call__(self, spectral_info): - carriers = tuple(self.propagate(*spectral_info.carriers)) - return spectral_info.update(carriers=carriers) + carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) + pref = self.update_pref(spectral_info.pref) + print('after edfa pi',pref.pi) + return spectral_info.update(carriers=carriers, pref=pref) diff --git a/gnpy/core/info.py b/gnpy/core/info.py index 4f7b6ea7..398581b7 100644 --- a/gnpy/core/info.py +++ b/gnpy/core/info.py @@ -24,9 +24,9 @@ class ConvenienceAccess: kwargs[field] = kwargs.pop(abbrev) return self._replace(**kwargs) - def ptot_dbm(self): - p = array([c.power.signal+c.power.nli+c.power.ase for c in self.carriers]) - return lin2db(sum(p*1e3)) + #def ptot_dbm(self): + # p = array([c.power.signal+c.power.nli+c.power.ase for c in self.carriers]) + # return lin2db(sum(p*1e3)) class Power(namedtuple('Power', 'signal nonlinear_interference amplified_spontaneous_emission'), ConvenienceAccess): @@ -42,11 +42,15 @@ class Channel(namedtuple('Channel', 'channel_number frequency baud_rate roll_off 'ffs': 'frequency', 'freq': 'frequency',} +class Pref(namedtuple('Pref', 'p_span0, p_spani'), ConvenienceAccess): -class SpectralInformation(namedtuple('SpectralInformation', 'carriers'), ConvenienceAccess): + _ABBREVS = {'p0' : 'p_span0', + 'pi' : 'p_spani'} - def __new__(cls, *carriers): - return super().__new__(cls, carriers) +class SpectralInformation(namedtuple('SpectralInformation', 'pref, carriers'), ConvenienceAccess): + + def __new__(cls, pref, *carriers): + return super().__new__(cls, pref, carriers) def create_input_spectral_information(f_min, roll_off, baudrate, power, spacing, nb_channel): diff --git a/gnpy/core/network.py b/gnpy/core/network.py index a8e32ee2..1cc40889 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -76,6 +76,28 @@ def select_edfa(ingress_span_loss, equipment): #chose the amp with the best NF among the acceptable ones: return min(acceptable_edfa_list, key=itemgetter(2))[0] +def set_roadm_loss(path_roadms, roadm_loss): + for roadm in path_roadms: + roadm.loss = roadm_loss + +def set_edfa_dp(network, amps): + prev_dp = 0 + for amp in amps: + next_node = [n for n in network.successors(amp)][0] + prev_node = [n for n in network.predecessors(amp)][0] + prev_node_loss = span_loss(network, prev_node) + if isinstance(next_node, Roadm): #ingress amp: set dp = 0 + dp = 0 + else: + dp = prev_dp + amp.operational.gain_target - prev_node_loss + #print('prev_node', prev_node, prev_node_loss) + #print('amp',amp) + #print('next node', next_node) + #print('gain', amp.operational.gain_target) + #print('edfa dp',prev_dp,dp) + amp.dp_db = dp + prev_dp = dp + def prev_fiber_node_generator(network, node): """fused spans interest: iterate over all predecessors while they are Fiber type"""