mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-01 02:28:05 +00:00
Power mode implementation
Signed-off-by: Jean-Luc Auge <jeanluc.auge@orange.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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"""
|
||||
|
||||
Reference in New Issue
Block a user