Power mode implementation

Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
This commit is contained in:
Jean-Luc Auge
2018-06-20 14:25:15 +02:00
parent 6ead8e391b
commit b810cf84c2
4 changed files with 105 additions and 35 deletions

View File

@@ -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)

View File

@@ -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')
@@ -139,9 +144,14 @@ class Fused(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',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}, '
@@ -317,12 +335,15 @@ class Edfa(Node):
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' 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' 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)

View File

@@ -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):

View File

@@ -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"""