Refactoring with some incompatible changes

Please be advised that there were incompatible changes in the Raman
options, including a `s/phase_shift_tollerance/phase_shift_tolerance/`.

Signed-off-by: AndreaDAmico <andrea.damico@polito.it>
Co-authored-by: EstherLerouzic <esther.lerouzic@orange.com>
Co-authored-by: Jan Kundrát <jan.kundrat@telecominfraproject.com>
This commit is contained in:
AndreaDAmico
2019-12-16 18:45:10 +01:00
committed by Jan Kundrát
parent 2960d307fa
commit 80eced85ec
16 changed files with 3394 additions and 7366 deletions

View File

@@ -412,7 +412,6 @@ parameters:
"type_variety": "SSMF",
"params":
{
"type_variety": "SSMF",
"length": 120.0,
"loss_coef": 0.2,
"length_units": "km",

View File

@@ -1,5 +1,4 @@
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
@@ -9,6 +8,7 @@
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
"phase_shift_tolerance": 0.1,
"computed_channels": [1, 18, 37, 56, 75]
}
}

View File

@@ -22,11 +22,14 @@ from numpy import linspace, mean, log10
from matplotlib.pyplot import show, axis, figure, title, text
from networkx import (draw_networkx_nodes, draw_networkx_edges,
draw_networkx_labels, dijkstra_path)
from gnpy.core.network import load_network, build_network, save_network, load_sim_params, configure_network
from gnpy.core.network import load_network, build_network, save_network
from gnpy.core.elements import Transceiver, Fiber, RamanFiber, Edfa, Roadm
from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref
from gnpy.core.request import Path_request, RequestParams, compute_constrained_path, propagate2
from gnpy.core.exceptions import ConfigurationError, EquipmentConfigError, NetworkTopologyError
from gnpy.core.parameters import SimParams
from gnpy.core.science_utils import Simulation
from gnpy.core.utils import load_json
import gnpy.core.ansi_escapes as ansi_escapes
logger = getLogger(__name__)
@@ -126,14 +129,16 @@ def main(network, equipment, source, destination, sim_params, req=None):
build_network(network, equipment, pref_ch_db, pref_total_db)
path = compute_constrained_path(network, req)
if len([s.length for s in path if isinstance(s, RamanFiber)]):
if sim_params:
Simulation.set_params(sim_params)
if len([s.params.length for s in path if isinstance(s, RamanFiber)]):
if sim_params is None:
print(f'{ansi_escapes.red}Invocation error:{ansi_escapes.reset} '
f'RamanFiber requires passing simulation params via --sim-params')
exit(1)
configure_network(network, sim_params)
spans = [s.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
spans = [s.params.length for s in path if isinstance(s, RamanFiber) or isinstance(s, Fiber)]
print(f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} '
f'and {destination.uid}')
print(f'\nNow propagating between {source.uid} and {destination.uid}:')
@@ -215,7 +220,7 @@ if __name__ == '__main__':
try:
equipment = load_equipment(args.equipment)
network = load_network(args.filename, equipment, args.names_matching)
sim_params = load_sim_params(args.sim_params) if args.sim_params is not None else None
sim_params = SimParams(**load_json(args.sim_params)) if args.sim_params is not None else None
except EquipmentConfigError as e:
print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}')
exit(1)

View File

@@ -18,15 +18,16 @@ Network elements MUST implement two attributes .uid and .name representing a
unique identifier and a printable name.
'''
from numpy import abs, arange, array, exp, divide, errstate
from numpy import abs, arange, array, exp, divide, errstate, ones, squeeze
from numpy import interp, log10, mean, pi, polyfit, polyval, sum
from scipy.constants import c, h
from collections import namedtuple
from gnpy.core.node import Node
from gnpy.core.units import UNITS
from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum
from gnpy.core.science_utils import propagate_raman_fiber, _psi
from gnpy.core.parameters import FiberParams, PumpParams
from gnpy.core.science_utils import NliSolver, RamanSolver, propagate_raman_fiber, _psi
class Transceiver(Node):
def __init__(self, *args, **kwargs):
@@ -224,37 +225,15 @@ class Fused(Node):
pref = self.update_pref(spectral_info.pref)
return spectral_info._replace(carriers=carriers, pref=pref)
FiberParams = namedtuple('FiberParams', 'type_variety length loss_coef length_units \
att_in con_in con_out dispersion gamma')
class Fiber(Node):
def __init__(self, *args, params=None, **kwargs):
if params is None:
if not params:
params = {}
if 'con_in' not in params:
# if not defined in the network json connector loss in/out
# the None value will be updated in network.py[build_network]
# with default values from eqpt_config.json[Spans]
params['con_in'] = None
params['con_out'] = None
if 'att_in' not in params:
#fixed attenuator for padding
params['att_in'] = 0
super().__init__(*args, params=FiberParams(**params), **kwargs)
self.type_variety = self.params.type_variety
self.length = self.params.length * UNITS[self.params.length_units] # in m
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
self.att_in = self.params.att_in
self.con_in = self.params.con_in
self.con_out = self.params.con_out
self.dispersion = self.params.dispersion # s/m/m
self.gamma = self.params.gamma # 1/W/m
self.pch_out_db = None
self.carriers_in = None
self.carriers_out = None
# TODO|jla: discuss factor 2 in the linear lineic attenuation
self.nli_solver = NliSolver(self)
@property
def to_json(self):
@@ -263,13 +242,12 @@ class Fiber(Node):
'type_variety' : self.type_variety,
'params' : {
#have to specify each because namedtupple cannot be updated :(
'type_variety' : self.type_variety,
'length' : self.length/UNITS[self.params.length_units],
'loss_coef' : self.loss_coef*1e3,
'length_units' : self.params.length_units,
'att_in' : self.att_in,
'con_in' : self.con_in,
'con_out' : self.con_out
'length' : round(self.params.length * 1e-3, 6),
'loss_coef' : self.params.loss_coef * 1e3,
'length_units' : 'km',
'att_in' : self.params.att_in,
'con_in' : self.params.con_in,
'con_out' : self.params.con_out
},
'metadata' : {
'location': self.metadata['location']._asdict()
@@ -277,48 +255,36 @@ class Fiber(Node):
}
def __repr__(self):
return f'{type(self).__name__}(uid={self.uid!r}, length={round(self.length*1e-3,1)!r}km, loss={round(self.loss,1)!r}dB)'
return f'{type(self).__name__}(uid={self.uid!r}, ' \
f'length={round(self.params.length * 1e-3,1)!r}km, ' \
f'loss={round(self.loss,1)!r}dB)'
def __str__(self):
return '\n'.join([f'{type(self).__name__} {self.uid}',
f' type_variety: {self.type_variety}',
f' length (km): {round(self.length*1e-3):.2f}',
f' pad att_in (dB): {self.att_in:.2f}',
f' length (km): '
f'{round(self.params.length * 1e-3):.2f}',
f' pad att_in (dB): {self.params.att_in:.2f}',
f' total loss (dB): {self.loss:.2f}',
f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})',
f' (includes conn loss (dB) in: {self.params.con_in:.2f} out: {self.params.con_out:.2f})',
f' (conn loss out includes EOL margin defined in eqpt_config.json)',
f' pch out (dBm): {self.pch_out_db!r}'])
@property
def fiber_loss(self):
"""Fiber loss in dB, not including padding attenuator"""
return self.loss_coef * self.length + self.con_in + self.con_out
return self.params.loss_coef * self.params.length + self.params.con_in + self.params.con_out
@property
def loss(self):
"""total loss including padding att_in: useful for polymorphism with roadm loss"""
return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
return self.params.loss_coef * self.params.length + self.params.con_in\
+ self.params.con_out + self.params.att_in
@property
def passive(self):
return True
@property
def lin_attenuation(self):
return db2lin(self.length * self.loss_coef)
@property
def effective_length(self):
_, alpha = self.dbkm_2_lin()
leff = (1 - exp(-2 * alpha * self.length)) / (2 * alpha)
return leff
@property
def asymptotic_length(self):
_, alpha = self.dbkm_2_lin()
aleff = 1 / (2 * alpha)
return aleff
def carriers(self, loc, attr):
"""retrieve carriers information
@@ -335,25 +301,27 @@ class Fiber(Node):
else:
yield c.power._asdict().get(attr, None)
def beta2(self, ref_wavelength=1550e-9):
"""Returns beta2 from dispersion parameter.
Dispersion is entered in ps/nm/km.
Disperion can be a numpy array or a single value.
def alpha(self, frequencies):
""" It returns the values of the series expansion of attenuation coefficient alpha(f) for all f in frequencies
:param ref_wavelength: can be a numpy array; default: 1550nm
:param frequencies: frequencies of series expansion [Hz]
:return: alpha: power attenuation coefficient for f in frequencies [Neper/m]
"""
# TODO|jla: discuss beta2 as method or attribute
D = abs(self.dispersion)
b2 = (ref_wavelength ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
return b2 # s/Hz/m
if type(self.params.loss_coef) == dict:
alpha = interp(frequencies, self.params.f_loss_ref, self.params.lin_loss_exp)
else:
alpha = self.params.lin_loss_exp * ones(frequencies.shape)
def dbkm_2_lin(self):
"""calculates the linear loss coefficient"""
# linear loss coefficient in dB/km^-1
alpha_pcoef = self.loss_coef
# linear loss field amplitude coefficient in m^-1
alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
return alpha_pcoef, alpha_acoef
return alpha
def alpha0(self, f_ref=193.5e12):
""" It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the
reference frequency f_ref
:param f_ref: reference frequency of series expansion [Hz]
:return: alpha0: power attenuation coefficient in f_ref [Neper/m]
"""
return self.alpha(f_ref * ones(1))[0]
def _gn_analytic(self, carrier, *carriers):
"""Computes the nonlinear interference power on a single carrier.
@@ -366,12 +334,13 @@ class Fiber(Node):
g_nli = 0
for interfering_carrier in carriers:
psi = _psi(carrier, interfering_carrier, beta2=self.beta2(), asymptotic_length=self.asymptotic_length)
psi = _psi(carrier, interfering_carrier, beta2=self.params.beta2,
asymptotic_length=self.params.asymptotic_length)
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
* (carrier.power.signal/carrier.baud_rate) * psi
g_nli *= (16 / 27) * (self.gamma * self.effective_length)**2 \
/ (2 * pi * abs(self.beta2()) * self.asymptotic_length)
g_nli *= (16 / 27) * (self.params.gamma * self.params.effective_length)**2 \
/ (2 * pi * abs(self.params.beta2) * self.params.asymptotic_length)
carrier_nli = carrier.baud_rate * g_nli
return carrier_nli
@@ -379,7 +348,7 @@ class Fiber(Node):
def propagate(self, *carriers):
# apply connector_att_in on all carriers before computing gn analytics premiere partie pas bonne
attenuation = db2lin(self.con_in + self.att_in)
attenuation = db2lin(self.params.con_in + self.params.att_in)
chan = []
for carrier in carriers:
@@ -393,13 +362,13 @@ class Fiber(Node):
carriers = tuple(f for f in chan)
# propagate in the fiber and apply attenuation out
attenuation = db2lin(self.con_out)
attenuation = db2lin(self.params.con_out)
for carrier in carriers:
pwr = carrier.power
carrier_nli = self._gn_analytic(carrier, *carriers)
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
nli=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
ase=pwr.ase/self.lin_attenuation/attenuation)
pwr = pwr._replace(signal=pwr.signal/self.params.lin_attenuation/attenuation,
nli=(pwr.nli+carrier_nli)/self.params.lin_attenuation/attenuation,
ase=pwr.ase/self.params.lin_attenuation/attenuation)
yield carrier._replace(power=pwr)
def update_pref(self, pref):
@@ -413,46 +382,16 @@ class Fiber(Node):
self.carriers_out = carriers
return spectral_info._replace(carriers=carriers, pref=pref)
RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \
att_in con_in con_out dispersion gamma raman_efficiency')
class RamanFiber(Fiber):
def __init__(self, *args, params=None, **kwargs):
if params is None:
params = {}
if 'con_in' not in params:
# if not defined in the network json connector loss in/out
# the None value will be updated in network.py[build_network]
# with default values from eqpt_config.json[Spans]
params['con_in'] = None
params['con_out'] = None
if 'att_in' not in params:
#fixed attenuator for padding
params['att_in'] = 0
# TODO: can we re-use the Fiber constructor in a better way?
Node.__init__(self, *args, params=RamanFiberParams(**params), **kwargs)
self.type_variety = self.params.type_variety
self.length = self.params.length * UNITS[self.params.length_units] # in m
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
self.att_in = self.params.att_in
self.con_in = self.params.con_in
self.con_out = self.params.con_out
self.dispersion = self.params.dispersion # s/m/m
self.gamma = self.params.gamma # 1/W/m
self.pch_out_db = None
self.carriers_in = None
self.carriers_out = None
# TODO|jla: discuss factor 2 in the linear lineic attenuation
@property
def sim_params(self):
return self._sim_params
@sim_params.setter
def sim_params(self, sim_params=None):
self._sim_params = sim_params
super().__init__(*args, params=params, **kwargs)
if self.operational and 'raman_pumps' in self.operational:
self.raman_pumps = tuple(PumpParams(p['power'], p['frequency'], p['propagation_direction'])
for p in self.operational['raman_pumps'])
else:
self.raman_pumps = None
self.raman_solver = RamanSolver(self)
def update_pref(self, pref, *carriers):
pch_out_db = lin2db(mean([carrier.power.signal for carrier in carriers])) + 30
@@ -488,13 +427,11 @@ class EdfaParams:
# self.allowed_for_design = None
def update_params(self, kwargs):
for k,v in kwargs.items() :
setattr(self, k, update_params(**v)
if isinstance(v, dict) else v)
for k, v in kwargs.items():
setattr(self, k, self.update_params(**v) if isinstance(v, dict) else v)
class EdfaOperational:
default_values = \
{
default_values = {
'gain_target': None,
'delta_p': None,
'out_voa': None,

View File

@@ -17,3 +17,8 @@ class EquipmentConfigError(ConfigurationError):
class NetworkTopologyError(ConfigurationError):
'''Topology of user-provided network is wrong'''
class ParametersError(ConfigurationError):
'''Incomplete or wrong configurations within parameters json'''

View File

@@ -20,7 +20,6 @@ from gnpy.core.elements import Fiber, Edfa, Transceiver, Roadm, Fused, RamanFibe
from gnpy.core.equipment import edfa_nf
from gnpy.core.exceptions import ConfigurationError, NetworkTopologyError
from gnpy.core.units import UNITS
from gnpy.core.science_utils import SimParams
from gnpy.core.utils import (load_json, save_json, round2float, db2lin, merge_amplifier_restrictions)
from collections import namedtuple
@@ -56,9 +55,10 @@ def network_from_json(json_data, equipment):
temp = el_config.setdefault('params', {})
temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
el_config['params'] = temp
elif typ in ['Edfa', 'Fiber']: # catch it now because the code will crash later!
el_config['type_variety'] = variety
elif typ in ['Edfa', 'Fiber', 'RamanFiber']: # catch it now because the code will crash later!
raise ConfigurationError(f'The {typ} of variety type {variety} was not recognized:'
'\nplease check it is properly defined in the eqpt_config json file')
'\nplease check it is properly defined in the eqpt_config json file')
cls = getattr(elements, typ)
el = cls(**el_config)
g.add_node(el)
@@ -440,7 +440,7 @@ def calculate_new_length(fiber_length, bounds, target_length):
def split_fiber(network, fiber, bounds, target_length, equipment):
new_length, n_spans = calculate_new_length(fiber.length, bounds, target_length)
new_length, n_spans = calculate_new_length(fiber.params.length, bounds, target_length)
if n_spans == 1:
return
@@ -452,17 +452,15 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
network.remove_node(fiber)
fiber_params = fiber.params._asdict()
fiber_params['length'] = new_length / UNITS[fiber.params.length_units]
fiber_params['con_in'] = fiber.con_in
fiber_params['con_out'] = fiber.con_out
fiber.params.length = new_length
f = interp1d([prev_node.lng, next_node.lng], [prev_node.lat, next_node.lat])
xpos = [prev_node.lng + (next_node.lng - prev_node.lng) * (n+1)/(n_spans+1) for n in range(n_spans)]
ypos = f(xpos)
for span, lng, lat in zip(range(n_spans), xpos, ypos):
new_span = Fiber(uid = f'{fiber.uid}_({span+1}/{n_spans})',
metadata = {
new_span = Fiber(uid=f'{fiber.uid}_({span+1}/{n_spans})',
type_variety=fiber.type_variety,
metadata={
'location': {
'latitude': lat,
'longitude': lng,
@@ -470,8 +468,8 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
'region': fiber.loc.region,
}
},
params = fiber_params)
if isinstance(prev_node,Fiber):
params=fiber.params.asdict())
if isinstance(prev_node, Fiber):
edgeweight = prev_node.params.length
else:
edgeweight = 0.01
@@ -485,11 +483,11 @@ def split_fiber(network, fiber, bounds, target_length, equipment):
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
for fiber in fibers:
if fiber.con_in is None: fiber.con_in = default_con_in
if fiber.con_out is None: fiber.con_out = default_con_out
if fiber.params.con_in is None: fiber.params.con_in = default_con_in
if fiber.params.con_out is None: fiber.params.con_out = default_con_out
next_node = next(n for n in network.successors(fiber))
if not isinstance(next_node, Fused):
fiber.con_out += EOL
fiber.params.con_out += EOL
def add_fiber_padding(network, fibers, padding):
"""last_fibers = (fiber for n in network.nodes()
@@ -509,10 +507,10 @@ def add_fiber_padding(network, fibers, padding):
# in order to support no booster , fused might be placed
# just after a roadm: need to check that first_fiber is really a fiber
if isinstance(first_fiber,Fiber):
if first_fiber.att_in is None:
first_fiber.att_in = padding - this_span_loss
if first_fiber.params.att_in is None:
first_fiber.params.att_in = padding - this_span_loss
else:
first_fiber.att_in = first_fiber.att_in + padding - this_span_loss
first_fiber.params.att_in = first_fiber.params.att_in + padding - this_span_loss
def build_network(network, equipment, pref_ch_db, pref_total_db):
default_span_data = equipment['Span']['default']
@@ -547,12 +545,3 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
trx = [t for t in network.nodes() if isinstance(t, Transceiver)]
for t in trx:
set_egress_amplifier(network, t, equipment, pref_total_db)
def load_sim_params(filename):
sim_params = load_json(filename)
return SimParams(params=sim_params)
def configure_network(network, sim_params):
for node in network.nodes:
if isinstance(node, RamanFiber):
node.sim_params = sim_params

View File

@@ -26,7 +26,7 @@ class Location(namedtuple('Location', 'latitude longitude city region')):
return super().__new__(cls, latitude, longitude, city, region)
class Node:
def __init__(self, uid, name=None, params=None, metadata=None, operational=None):
def __init__(self, uid, name=None, params=None, metadata=None, operational=None, type_variety=None):
if name is None:
name = uid
self.uid, self.name = uid, name
@@ -35,6 +35,8 @@ class Node:
if metadata and not isinstance(metadata.get('location'), Location):
metadata['location'] = Location(**metadata.pop('location', {}))
self.params, self.metadata, self.operational = params, metadata, operational
if type_variety:
self.type_variety = type_variety
@property
def coords(self):

View File

@@ -1,6 +1,5 @@
import numpy as np
from operator import attrgetter
from collections import namedtuple
from logging import getLogger
import scipy.constants as ph
from scipy.integrate import solve_bvp
@@ -9,154 +8,19 @@ from scipy.interpolate import interp1d
from scipy.optimize import OptimizeResult
from gnpy.core.utils import db2lin
from gnpy.core.parameters import SimParams
logger = getLogger(__name__)
class RamanParams():
def __init__(self, params):
self._flag_raman = params['flag_raman']
self._space_resolution = params['space_resolution']
self._tolerance = params['tolerance']
@property
def flag_raman(self):
return self._flag_raman
@property
def space_resolution(self):
return self._space_resolution
@property
def tolerance(self):
return self._tolerance
class NLIParams():
def __init__(self, params):
self._nli_method_name = params['nli_method_name']
self._wdm_grid_size = params['wdm_grid_size']
self._dispersion_tolerance = params['dispersion_tolerance']
self._phase_shift_tollerance = params['phase_shift_tollerance']
self._f_cut_resolution = None
self._f_pump_resolution = None
@property
def nli_method_name(self):
return self._nli_method_name
@property
def wdm_grid_size(self):
return self._wdm_grid_size
@property
def dispersion_tolerance(self):
return self._dispersion_tolerance
@property
def phase_shift_tollerance(self):
return self._phase_shift_tollerance
@property
def f_cut_resolution(self):
return self._f_cut_resolution
@f_cut_resolution.setter
def f_cut_resolution(self, f_cut_resolution):
self._f_cut_resolution = f_cut_resolution
@property
def f_pump_resolution(self):
return self._f_pump_resolution
@f_pump_resolution.setter
def f_pump_resolution(self, f_pump_resolution):
self._f_pump_resolution = f_pump_resolution
class SimParams():
def __init__(self, params):
self._raman_computed_channels = params['raman_computed_channels']
self._raman_params = RamanParams(params=params['raman_parameters'])
self._nli_params = NLIParams(params=params['nli_parameters'])
@property
def raman_computed_channels(self):
return self._raman_computed_channels
@property
def raman_params(self):
return self._raman_params
@property
def nli_params(self):
return self._nli_params
class FiberParams():
def __init__(self, fiber):
self._loss_coef = 2 * fiber.dbkm_2_lin()[1]
self._length = fiber.length
self._gamma = fiber.gamma
self._beta2 = fiber.beta2()
self._beta3 = fiber.beta3 if hasattr(fiber, 'beta3') else 0
self._f_ref_beta = fiber.f_ref_beta if hasattr(fiber, 'f_ref_beta') else 0
self._raman_efficiency = fiber.params.raman_efficiency
self._temperature = fiber.operational['temperature']
@property
def loss_coef(self):
return self._loss_coef
@property
def length(self):
return self._length
@property
def gamma(self):
return self._gamma
@property
def beta2(self):
return self._beta2
@property
def beta3(self):
return self._beta3
@property
def f_ref_beta(self):
return self._f_ref_beta
@property
def raman_efficiency(self):
return self._raman_efficiency
@property
def temperature(self):
return self._temperature
def alpha0(self, f_ref=193.5e12):
""" It returns the zero element of the series expansion of attenuation coefficient alpha(f) in the
reference frequency f_ref
:param f_ref: reference frequency of series expansion [Hz]
:return: alpha0: power attenuation coefficient in f_ref [Neper/m]
"""
if not hasattr(self.loss_coef, 'alpha_power'):
alpha0 = self.loss_coef
else:
alpha_interp = interp1d(self.loss_coef['frequency'],
self.loss_coef['alpha_power'])
alpha0 = alpha_interp(f_ref)
return alpha0
pump = namedtuple('RamanPump', 'power frequency propagation_direction')
def propagate_raman_fiber(fiber, *carriers):
sim_params = fiber.sim_params
raman_params = fiber.sim_params.raman_params
nli_params = fiber.sim_params.nli_params
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
raman_params = sim_params.raman_params
nli_params = sim_params.nli_params
# apply input attenuation to carriers
attenuation_in = db2lin(fiber.con_in + fiber.att_in)
attenuation_in = db2lin(fiber.params.con_in + fiber.params.att_in)
chan = []
for carrier in carriers:
pwr = carrier.power
@@ -166,36 +30,32 @@ def propagate_raman_fiber(fiber, *carriers):
carrier = carrier._replace(power=pwr)
chan.append(carrier)
carriers = tuple(f for f in chan)
fiber_params = FiberParams(fiber)
# evaluate fiber attenuation involving also SRS if required by sim_params
if 'raman_pumps' in fiber.operational:
raman_pumps = tuple(pump(p['power'], p['frequency'], p['propagation_direction'])
for p in fiber.operational['raman_pumps'])
else:
raman_pumps = None
raman_solver = RamanSolver(raman_params=raman_params, fiber_params=fiber_params)
stimulated_raman_scattering = raman_solver.stimulated_raman_scattering(carriers=carriers,
raman_pumps=raman_pumps)
raman_solver = fiber.raman_solver
raman_solver.carriers = carriers
raman_solver.raman_pumps = fiber.raman_pumps
stimulated_raman_scattering = raman_solver.stimulated_raman_scattering
fiber_attenuation = (stimulated_raman_scattering.rho[:, -1])**-2
if not raman_params.flag_raman:
fiber_attenuation = tuple(fiber.lin_attenuation for _ in carriers)
fiber_attenuation = tuple(fiber.params.lin_attenuation for _ in carriers)
# evaluate Raman ASE noise if required by sim_params and if raman pumps are present
if raman_params.flag_raman and raman_pumps:
if raman_params.flag_raman and fiber.raman_pumps:
raman_ase = raman_solver.spontaneous_raman_scattering.power[:, -1]
else:
raman_ase = tuple(0 for _ in carriers)
# evaluate nli and propagate in fiber
attenuation_out = db2lin(fiber.con_out)
nli_solver = NliSolver(nli_params=nli_params, fiber_params=fiber_params)
attenuation_out = db2lin(fiber.params.con_out)
nli_solver = fiber.nli_solver
nli_solver.stimulated_raman_scattering = stimulated_raman_scattering
nli_frequencies = []
computed_nli = []
for carrier in (c for c in carriers if c.channel_number in sim_params.raman_computed_channels):
resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber_params)
for carrier in (c for c in carriers if c.channel_number in sim_params.nli_params.computed_channels):
resolution_param = frequency_resolution(carrier, carriers, sim_params, fiber)
f_cut_resolution, f_pump_resolution, _, _ = resolution_param
nli_params.f_cut_resolution = f_cut_resolution
nli_params.f_pump_resolution = f_pump_resolution
@@ -212,7 +72,8 @@ def propagate_raman_fiber(fiber, *carriers):
new_carriers.append(carrier._replace(power=pwr))
return new_carriers
def frequency_resolution(carrier, carriers, sim_params, fiber_params):
def frequency_resolution(carrier, carriers, sim_params, fiber):
def _get_freq_res_k_phi(delta_count, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol):
res_phi = _get_freq_res_phase_rotation(delta_count, grid_size, delta_z, beta2, phi_tol)
res_k = _get_freq_res_dispersion_attenuation(delta_count, grid_size, alpha0, beta2, k_tol)
@@ -228,10 +89,10 @@ def frequency_resolution(carrier, carriers, sim_params, fiber_params):
grid_size = sim_params.nli_params.wdm_grid_size
delta_z = sim_params.raman_params.space_resolution
alpha0 = fiber_params.alpha0()
beta2 = fiber_params.beta2
alpha0 = fiber.alpha0()
beta2 = fiber.params.beta2
k_tol = sim_params.nli_params.dispersion_tolerance
phi_tol = sim_params.nli_params.phase_shift_tollerance
phi_tol = sim_params.nli_params.phase_shift_tolerance
f_pump_resolution, method_f_pump, res_dict_pump = \
_get_freq_res_k_phi(0, grid_size, alpha0, delta_z, beta2, k_tol, phi_tol)
f_cut_resolution = {}
@@ -247,6 +108,7 @@ def frequency_resolution(carrier, carriers, sim_params, fiber_params):
res_dict_cut[delta_number] = res_dict
return [f_cut_resolution, f_pump_resolution, (method_f_cut, method_f_pump), (res_dict_cut, res_dict_pump)]
def raised_cosine_comb(f, *carriers):
""" Returns an array storing the PSD of a WDM comb of raised cosine shaped
channels at the input frequencies defined in array f
@@ -270,30 +132,59 @@ def raised_cosine_comb(f, *carriers):
np.where(tf > 0, 1., 0.) * np.where(np.abs(ff) <= stopband, 1., 0.)) + psd
return psd
class Simulation:
_shared_dict = {}
def __init__(self):
if type(self) == Simulation:
raise NotImplementedError('Simulation cannot be instatiated')
@classmethod
def set_params(cls, sim_params):
cls._shared_dict['sim_params'] = sim_params
@classmethod
def get_simulation(cls):
self = cls.__new__(cls)
return self
@property
def sim_params(self):
return self._shared_dict['sim_params']
class SpontaneousRamanScattering:
def __init__(self, frequency, z, power):
self.frequency = frequency
self.z = z
self.power = power
class StimulatedRamanScattering:
def __init__(self, frequency, z, rho, power):
self.frequency = frequency
self.z = z
self.rho = rho
self.power = power
class RamanSolver:
def __init__(self, raman_params=None, fiber_params=None):
""" Initialize the fiber object with its physical parameters
:param length: fiber length in m.
:param alphap: fiber power attenuation coefficient vs frequency in 1/m. numpy array
:param freq_alpha: frequency axis of alphap in Hz. numpy array
:param cr_raman: Raman efficiency vs frequency offset in 1/W/m. numpy array
:param freq_cr: reference frequency offset axis for cr_raman. numpy array
:param raman_params: namedtuple containing the solver parameters (optional).
def __init__(self, fiber=None):
""" Initialize the Raman solver object.
:param fiber: instance of elements.py/Fiber.
:param carriers: tuple of carrier objects
:param raman_pumps: tuple containing pumps characteristics
"""
self.fiber_params = fiber_params
self.raman_params = raman_params
self._fiber = fiber
self._carriers = None
self._raman_pumps = None
self._stimulated_raman_scattering = None
self._spontaneous_raman_scattering = None
@property
def fiber_params(self):
return self._fiber_params
@fiber_params.setter
def fiber_params(self, fiber_params):
self._stimulated_raman_scattering = None
self._fiber_params = fiber_params
def fiber(self):
return self._fiber
@property
def carriers(self):
@@ -301,11 +192,8 @@ class RamanSolver:
@carriers.setter
def carriers(self, carriers):
"""
:param carriers: tuple of namedtuples containing information about carriers
:return:
"""
self._carriers = carriers
self._spontaneous_raman_scattering = None
self._stimulated_raman_scattering = None
@property
@@ -318,62 +206,44 @@ class RamanSolver:
self._stimulated_raman_scattering = None
@property
def raman_params(self):
return self._raman_params
@raman_params.setter
def raman_params(self, raman_params):
"""
:param raman_params: namedtuple containing the solver parameters (optional).
:return:
"""
self._raman_params = raman_params
self._stimulated_raman_scattering = None
self._spontaneous_raman_scattering = None
def stimulated_raman_scattering(self):
if self._stimulated_raman_scattering is None:
self.calculate_stimulated_raman_scattering(self.carriers, self.raman_pumps)
return self._stimulated_raman_scattering
@property
def spontaneous_raman_scattering(self):
if self._spontaneous_raman_scattering is None:
# SET STUFF
loss_coef = self.fiber_params.loss_coef
raman_efficiency = self.fiber_params.raman_efficiency
temperature = self.fiber_params.temperature
carriers = self.carriers
raman_pumps = self.raman_pumps
logger.debug('Start computing fiber Spontaneous Raman Scattering')
power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps)
if not hasattr(loss_coef, 'alpha_power'):
alphap_fiber = loss_coef * np.ones(freq_array.shape)
else:
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
alphap_fiber = interp_alphap(freq_array)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
# z propagation axis
z_array = self._stimulated_raman_scattering.z
ase_bc = np.zeros(freq_array.shape)
# calculate ase power
spontaneous_raman_scattering = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
alphap_fiber, freq_array, cr, freq_diff, ase_bc,
bn_array, temperature)
setattr(spontaneous_raman_scattering, 'frequency', freq_array)
setattr(spontaneous_raman_scattering, 'z', z_array)
setattr(spontaneous_raman_scattering, 'power', spontaneous_raman_scattering.x)
delattr(spontaneous_raman_scattering, 'x')
logger.debug(spontaneous_raman_scattering.message)
self._spontaneous_raman_scattering = spontaneous_raman_scattering
self.calculate_spontaneous_raman_scattering(self.carriers, self.raman_pumps)
return self._spontaneous_raman_scattering
def calculate_spontaneous_raman_scattering(self, carriers, raman_pumps):
raman_efficiency = self.fiber.params.raman_efficiency
temperature = self.fiber.operational['temperature']
logger.debug('Start computing fiber Spontaneous Raman Scattering')
power_spectrum, freq_array, prop_direct, bn_array = self._compute_power_spectrum(carriers, raman_pumps)
alphap_fiber = self.fiber.alpha(freq_array)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
# z propagation axis
z_array = self.stimulated_raman_scattering.z
ase_bc = np.zeros(freq_array.shape)
# calculate ase power
int_spontaneous_raman = self._int_spontaneous_raman(z_array, self._stimulated_raman_scattering.power,
alphap_fiber, freq_array, cr, freq_diff, ase_bc,
bn_array, temperature)
spontaneous_raman_scattering = SpontaneousRamanScattering(freq_array, z_array, int_spontaneous_raman.x)
logger.debug("Spontaneous Raman Scattering evaluated successfully")
self._spontaneous_raman_scattering = spontaneous_raman_scattering
@staticmethod
def _compute_power_spectrum(carriers, raman_pumps=None):
"""
@@ -412,10 +282,14 @@ class RamanSolver:
return pow_array, f_array, propagation_direction, noise_bandwidth_array
def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array, cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature):
def _int_spontaneous_raman(self, z_array, raman_matrix, alphap_fiber, freq_array,
cr_raman_matrix, freq_diff, ase_bc, bn_array, temperature):
spontaneous_raman_scattering = OptimizeResult()
dx = self.raman_params.space_resolution
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
dx = sim_params.raman_params.space_resolution
h = ph.value('Planck constant')
kb = ph.value('Boltzmann constant')
@@ -428,12 +302,14 @@ class RamanSolver:
eta = 1/(np.exp((h*freq_diff[f_ind, f_ind+1:])/(kb*temperature)) - 1)
int_fiber_loss = -alphap_fiber[f_ind] * z_array
int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), axis=0)
int_raman_loss = np.sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(),
axis=0)
int_raman_gain = np.sum((cr_raman[f_ind + 1:] * int_pump[f_ind + 1:, :].transpose()).transpose(), axis=0)
int_gain_loss = int_fiber_loss + int_raman_gain + int_raman_loss
new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose() * h * f_ase * bn_array[f_ind], axis=0)
new_ase = np.sum((cr_raman[f_ind+1:] * (1 + eta) * raman_matrix[f_ind+1:, :].transpose()).transpose()
* h * f_ase * bn_array[f_ind], axis=0)
bc_evolution = ase_bc[f_ind] * np.exp(int_gain_loss)
ase_evolution = np.exp(int_gain_loss) * cumtrapz(new_ase*np.exp(-int_gain_loss), z_array, dx=dx, initial=0)
@@ -441,69 +317,51 @@ class RamanSolver:
power_ase[f_ind, :] = bc_evolution + ase_evolution
spontaneous_raman_scattering.x = 2 * power_ase
spontaneous_raman_scattering.success = True
spontaneous_raman_scattering.message = "Spontaneous Raman Scattering evaluated successfully"
return spontaneous_raman_scattering
def stimulated_raman_scattering(self, carriers, raman_pumps=None):
def calculate_stimulated_raman_scattering(self, carriers, raman_pumps):
""" Returns stimulated Raman scattering solution including
fiber gain/loss profile.
:return: self._stimulated_raman_scattering: the SRS problem solution.
scipy.interpolate.PPoly instance
:return: None
"""
# fiber parameters
fiber_length = self.fiber.params.length
loss_coef = self.fiber.params.lin_loss_exp
raman_efficiency = self.fiber.params.raman_efficiency
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
if self._stimulated_raman_scattering is None:
# fiber parameters
fiber_length = self.fiber_params.length
loss_coef = self.fiber_params.loss_coef
if self.raman_params.flag_raman:
raman_efficiency = self.fiber_params.raman_efficiency
else:
raman_efficiency = self.fiber_params.raman_efficiency
raman_efficiency['cr'] = np.array(raman_efficiency['cr']) * 0
# raman solver parameters
z_resolution = self.raman_params.space_resolution
tolerance = self.raman_params.tolerance
if not sim_params.raman_params.flag_raman:
raman_efficiency['cr'] = np.zeros(len(raman_efficiency['cr']))
# raman solver parameters
z_resolution = sim_params.raman_params.space_resolution
tolerance = sim_params.raman_params.tolerance
logger.debug('Start computing fiber Stimulated Raman Scattering')
logger.debug('Start computing fiber Stimulated Raman Scattering')
power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps)
power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps)
if not hasattr(loss_coef, 'alpha_power'):
alphap_fiber = loss_coef * np.ones(freq_array.shape)
else:
interp_alphap = interp1d(loss_coef['frequency'], loss_coef['alpha_power'])
alphap_fiber = interp_alphap(freq_array)
alphap_fiber = self.fiber.alpha(freq_array)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
freq_diff = abs(freq_array - np.reshape(freq_array, (len(freq_array), 1)))
interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr'])
cr = interp_cr(freq_diff)
# z propagation axis
z = np.arange(0, fiber_length+1, z_resolution)
# z propagation axis
z = np.arange(0, fiber_length + 1, z_resolution)
ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct)
initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct)
ode_function = lambda z, p: self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct)
boundary_residual = lambda ya, yb: self._residuals_stimulated_raman(ya, yb, power_spectrum, prop_direct)
initial_guess_conditions = self._initial_guess_stimulated_raman(z, power_spectrum, alphap_fiber, prop_direct)
# ODE SOLVER
stimulated_raman_scattering = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
# ODE SOLVER
bvp_solution = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance)
rho = (stimulated_raman_scattering.y.transpose() / power_spectrum).transpose()
rho = np.sqrt(rho) # From power attenuation to field attenuation
setattr(stimulated_raman_scattering, 'frequency', freq_array)
setattr(stimulated_raman_scattering, 'z', stimulated_raman_scattering.x)
setattr(stimulated_raman_scattering, 'rho', rho)
setattr(stimulated_raman_scattering, 'power', stimulated_raman_scattering.y)
delattr(stimulated_raman_scattering, 'x')
delattr(stimulated_raman_scattering, 'y')
rho = (bvp_solution.y.transpose() / power_spectrum).transpose()
rho = np.sqrt(rho) # From power attenuation to field attenuation
stimulated_raman_scattering = StimulatedRamanScattering(freq_array, bvp_solution.x, rho, bvp_solution.y)
self.carriers = carriers
self.raman_pumps = raman_pumps
self._stimulated_raman_scattering = stimulated_raman_scattering
return self._stimulated_raman_scattering
self._stimulated_raman_scattering = stimulated_raman_scattering
def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct):
@@ -520,11 +378,14 @@ class RamanSolver:
def _initial_guess_stimulated_raman(self, z, power_spectrum, alphap_fiber, prop_direct):
""" Computes the initial guess knowing the boundary conditions
:param z: patial axis [m]. numpy array
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array
:param power_spectrum: power in each frequency slice [W].
Frequency axis is defined by freq_array. numpy array
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m].
Frequency defined by freq_array. numpy array
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array
:return: power_guess: guess on the initial conditions [W]. The first ndarray index identifies the frequency slice,
:return: power_guess: guess on the initial conditions [W].
The first ndarray index identifies the frequency slice,
the second ndarray index identifies the step in z. ndarray
"""
@@ -538,14 +399,19 @@ class RamanSolver:
return power_guess
def _ode_stimulated_raman(self, z, power_spectrum, alphap_fiber, freq_array, cr_raman_matrix, prop_direct):
""" Aim of ode_raman is to implement the set of ordinary differential equations (ODEs) describing the Raman effect.
""" Aim of ode_raman is to implement the set of ordinary differential equations (ODEs)
describing the Raman effect.
:param z: spatial axis (unused).
:param power_spectrum: power in each frequency slice [W]. Frequency axis is defined by freq_array. numpy array. Size n
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m]. Frequency defined by freq_array. numpy array. Size n
:param power_spectrum: power in each frequency slice [W].
Frequency axis is defined by freq_array. numpy array. Size n
:param alphap_fiber: frequency dependent fiber attenuation of signal power [1/m].
Frequency defined by freq_array. numpy array. Size n
:param freq_array: reference frequency axis [Hz]. numpy array. Size n
:param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m]. Frequency defined by freq_array. numpy ndarray. Size nxn
:param cr_raman: Cr(f) Raman gain efficiency variation in frequency [1/W/m].
Frequency defined by freq_array. numpy ndarray. Size nxn
:param prop_direct: indicates the propagation direction of each power slice in power_spectrum:
+1 for forward propagation and -1 for backward propagation. Frequency defined by freq_array. numpy array. Size n
+1 for forward propagation and -1 for backward propagation.
Frequency defined by freq_array. numpy array. Size n
:return: dP/dz: the power variation in dz [W/m]. numpy array. Size n
"""
@@ -563,28 +429,24 @@ class RamanSolver:
return np.vstack(dpdz)
class NliSolver:
""" This class implements the NLI models.
Model and method can be specified in `self.nli_params.method`.
Model and method can be specified in `sim_params.nli_params.method`.
List of implemented methods:
'gn_model_analytic': brute force triple integral solution
'ggn_spectrally_separated_xpm_spm': XPM plus SPM
"""
def __init__(self, nli_params=None, fiber_params=None):
""" Initialize the fiber object with its physical parameters
def __init__(self, fiber=None):
""" Initialize the Nli solver object.
:param fiber: instance of elements.py/Fiber.
"""
self.fiber_params = fiber_params
self.nli_params = nli_params
self.stimulated_raman_scattering = None
self._fiber = fiber
self._stimulated_raman_scattering = None
@property
def fiber_params(self):
return self._fiber_params
@fiber_params.setter
def fiber_params(self, fiber_params):
self._fiber_params = fiber_params
def fiber(self):
return self._fiber
@property
def stimulated_raman_scattering(self):
@@ -594,28 +456,19 @@ class NliSolver:
def stimulated_raman_scattering(self, stimulated_raman_scattering):
self._stimulated_raman_scattering = stimulated_raman_scattering
@property
def nli_params(self):
return self._nli_params
@nli_params.setter
def nli_params(self, nli_params):
"""
:param model_params: namedtuple containing the parameters used to compute the NLI.
"""
self._nli_params = nli_params
def compute_nli(self, carrier, *carriers):
""" Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier`
at the end of the fiber span.
"""
if 'gn_model_analytic' == self.nli_params.nli_method_name.lower():
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
if 'gn_model_analytic' == sim_params.nli_params.nli_method_name.lower():
carrier_nli = self._gn_analytic(carrier, *carriers)
elif 'ggn_spectrally_separated' in self.nli_params.nli_method_name.lower():
elif 'ggn_spectrally_separated' in sim_params.nli_params.nli_method_name.lower():
eta_matrix = self._compute_eta_matrix(carrier, *carriers)
carrier_nli = self._carrier_nli_from_eta_matrix(eta_matrix, carrier, *carriers)
else:
raise ValueError(f'Method {self.nli_params.method_nli} not implemented.')
raise ValueError(f'Method {sim_params.nli_params.method_nli} not implemented.')
return carrier_nli
@@ -632,6 +485,8 @@ class NliSolver:
def _compute_eta_matrix(self, carrier_cut, *carriers):
cut_index = carrier_cut.channel_number - 1
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
# Matrix initialization
matrix_size = max(carriers, key=lambda x: getattr(x, 'channel_number')).channel_number
eta_matrix = np.zeros(shape=(matrix_size, matrix_size))
@@ -639,10 +494,10 @@ class NliSolver:
# SPM
logger.debug(f'Start computing SPM on channel #{carrier_cut.channel_number}')
# SPM GGN
if 'ggn' in self.nli_params.nli_method_name.lower():
if 'ggn' in sim_params.nli_params.nli_method_name.lower():
partial_nli = self._generalized_spectrally_separated_spm(carrier_cut)
# SPM GN
elif 'gn' in self.nli_params.nli_method_name.lower():
elif 'gn' in sim_params.nli_params.nli_method_name.lower():
partial_nli = self._gn_analytic(carrier_cut, *[carrier_cut])
eta_matrix[cut_index, cut_index] = partial_nli / (carrier_cut.power.signal**3)
@@ -653,10 +508,10 @@ class NliSolver:
logger.debug(f'Start computing XPM on channel #{carrier_cut.channel_number} '
f'from channel #{pump_carrier.channel_number}')
# XPM GGN
if 'ggn' in self.nli_params.nli_method_name.lower():
if 'ggn' in sim_params.nli_params.nli_method_name.lower():
partial_nli = self._generalized_spectrally_separated_xpm(carrier_cut, pump_carrier)
# XPM GGN
elif 'gn' in self.nli_params.nli_method_name.lower():
elif 'gn' in sim_params.nli_params.nli_method_name.lower():
partial_nli = self._gn_analytic(carrier_cut, *[pump_carrier])
eta_matrix[pump_index, pump_index] = partial_nli /\
(carrier_cut.power.signal * pump_carrier.power.signal**2)
@@ -670,47 +525,51 @@ class NliSolver:
:param carriers: the full WDM comb
:return: carrier_nli: the amount of nonlinear interference in W on the carrier under analysis
"""
alpha = self.fiber_params.alpha0() / 2
beta2 = self.fiber_params.beta2
gamma = self.fiber_params.gamma
length = self.fiber_params.length
effective_length = (1 - np.exp(-2 * alpha * length)) / (2 * alpha)
asymptotic_length = 1 / (2 * alpha)
beta2 = self.fiber.params.beta2
gamma = self.fiber.params.gamma
effective_length = self.fiber.params.effective_length
asymptotic_length = self.fiber.params.asymptotic_length
g_nli = 0
for interfering_carrier in carriers:
g_interfearing = interfering_carrier.power.signal / interfering_carrier.baud_rate
g_signal = carrier.power.signal / carrier.baud_rate
g_nli += g_interfearing**2 * g_signal \
* _psi(carrier, interfering_carrier, beta2=self.fiber_params.beta2, asymptotic_length=1/self.fiber_params.alpha0())
g_nli *= (16.0 / 27.0) * (gamma * effective_length)**2 /\
* _psi(carrier, interfering_carrier, beta2=beta2, asymptotic_length=asymptotic_length)
g_nli *= (16.0 / 27.0) * (gamma * effective_length) ** 2 /\
(2 * np.pi * abs(beta2) * asymptotic_length)
carrier_nli = carrier.baud_rate * g_nli
return carrier_nli
# Methods for computing the GGN-model
def _generalized_spectrally_separated_spm(self, carrier):
f_cut_resolution = self.nli_params.f_cut_resolution['delta_0']
gamma = self.fiber.params.gamma
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
f_cut_resolution = sim_params.nli_params.f_cut_resolution['delta_0']
f_eval = carrier.frequency
g_cut = (carrier.power.signal / carrier.baud_rate)
spm_nli = carrier.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_cut**3 * \
spm_nli = carrier.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_cut ** 3 * \
self._generalized_psi(carrier, carrier, f_eval, f_cut_resolution, f_cut_resolution)
return spm_nli
def _generalized_spectrally_separated_xpm(self, carrier_cut, pump_carrier):
gamma = self.fiber.params.gamma
simulation = Simulation.get_simulation()
sim_params = simulation.sim_params
delta_index = pump_carrier.channel_number - carrier_cut.channel_number
f_cut_resolution = self.nli_params.f_cut_resolution[f'delta_{delta_index}']
f_pump_resolution = self.nli_params.f_pump_resolution
f_cut_resolution = sim_params.nli_params.f_cut_resolution[f'delta_{delta_index}']
f_pump_resolution = sim_params.nli_params.f_pump_resolution
f_eval = carrier_cut.frequency
g_pump = (pump_carrier.power.signal / pump_carrier.baud_rate)
g_cut = (carrier_cut.power.signal / carrier_cut.baud_rate)
frequency_offset_threshold = self._frequency_offset_threshold(pump_carrier.baud_rate)
if abs(carrier_cut.frequency - pump_carrier.frequency) <= frequency_offset_threshold:
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
2 * self._generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution, f_pump_resolution)
else:
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * self.fiber_params.gamma**2 * g_pump**2 * g_cut * \
xpm_nli = carrier_cut.baud_rate * (16.0 / 27.0) * gamma ** 2 * g_pump**2 * g_cut * \
2 * self._fast_generalized_psi(carrier_cut, pump_carrier, f_eval, f_cut_resolution)
return xpm_nli
@@ -719,10 +578,10 @@ class NliSolver:
:return: generalized_psi
"""
# Fiber parameters
alpha0 = self.fiber_params.alpha0(f_eval)
beta2 = self.fiber_params.beta2
beta3 = self.fiber_params.beta3
f_ref_beta = self.fiber_params.f_ref_beta
alpha0 = self.fiber.alpha0(f_eval)
beta2 = self.fiber.params.beta2
beta3 = self.fiber.params.beta3
f_ref_beta = self.fiber.params.ref_frequency
z = self.stimulated_raman_scattering.z
frequency_rho = self.stimulated_raman_scattering.frequency
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
@@ -752,10 +611,10 @@ class NliSolver:
:return: generalized_psi
"""
# Fiber parameters
alpha0 = self.fiber_params.alpha0(f_eval)
beta2 = self.fiber_params.beta2
beta3 = self.fiber_params.beta3
f_ref_beta = self.fiber_params.f_ref_beta
alpha0 = self.fiber.alpha0(f_eval)
beta2 = self.fiber.params.beta2
beta3 = self.fiber.params.beta3
f_ref_beta = self.fiber.params.ref_frequency
z = self.stimulated_raman_scattering.z
frequency_rho = self.stimulated_raman_scattering.frequency
rho_norm = self.stimulated_raman_scattering.rho * np.exp(np.abs(alpha0) * z / 2)
@@ -803,7 +662,8 @@ class NliSolver:
beta2_ref = 21.3e-27
delta_f_ref = 50e9
rs_ref = 32e9
freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (self.fiber_params.beta2 * symbol_rate)
beta2 = self.fiber.params.beta2
freq_offset_th = ((k_ref * delta_f_ref) * rs_ref * beta2_ref) / (beta2 * symbol_rate)
return freq_offset_th
def _psi(carrier, interfering_carrier, beta2, asymptotic_length):

View File

@@ -544,7 +544,6 @@ Additional details of the simulation are controlled via ``sim_params.json``:
.. code-block:: json
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
@@ -554,6 +553,7 @@ Additional details of the simulation are controlled via ``sim_params.json``:
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
"phase_shift_tolerance": 0.1,
"computed_channels": [1, 18, 37, 56, 75]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
{
"raman_computed_channels": [1, 18, 37, 56, 75],
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
"tolerance": 1e-8
},
"nli_parameters": {
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tollerance": 0.1
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tolerance": 0.1,
"computed_channels": [1, 18, 37, 56, 75]
}
}

View File

@@ -251,48 +251,6 @@
}
}
},
{
"uid": "roadm a",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": [
"std_booster"
]
}
},
"metadata": {
"location": {
"latitude": 6.0,
"longitude": 0.0,
"city": "a",
"region": ""
}
}
},
{
"uid": "roadm b",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [
"std_low_gain"
],
"booster_variety_list": []
}
},
"metadata": {
"location": {
"latitude": 5.0,
"longitude": 0.0,
"city": "b",
"region": ""
}
}
},
{
"uid": "roadm c",
"type": "Roadm",
@@ -407,6 +365,48 @@
}
}
},
{
"uid": "roadm a",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [],
"booster_variety_list": [
"std_booster"
]
}
},
"metadata": {
"location": {
"latitude": 6.0,
"longitude": 0.0,
"city": "a",
"region": ""
}
}
},
{
"uid": "roadm b",
"type": "Roadm",
"params": {
"target_pch_out_db": -20,
"restrictions": {
"preamp_variety_list": [
"std_low_gain"
],
"booster_variety_list": []
}
},
"metadata": {
"location": {
"latitude": 5.0,
"longitude": 0.0,
"city": "b",
"region": ""
}
}
},
{
"uid": "west fused spans in Corlay",
"type": "Fused",
@@ -502,7 +502,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 20.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -524,7 +523,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 50.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -546,7 +544,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -568,7 +565,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 10.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -590,7 +586,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -612,7 +607,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 65.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -634,7 +628,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 40.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -656,7 +649,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 35.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -678,7 +670,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 75.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -700,7 +691,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 70.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -722,7 +712,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 50.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -744,7 +733,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 55.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -766,7 +754,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 30.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -788,7 +775,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 30.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -810,7 +796,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 10.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -832,7 +817,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -854,7 +838,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 70.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -876,7 +859,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -898,7 +880,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 90.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -920,7 +901,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 100.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -942,7 +922,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 110.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -964,7 +943,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 20.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -986,7 +964,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 50.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1008,7 +985,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1030,7 +1006,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 10.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1052,7 +1027,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1074,7 +1048,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 65.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1096,7 +1069,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 40.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1118,7 +1090,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 35.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1140,7 +1111,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 75.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1162,7 +1132,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 70.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1184,7 +1153,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 50.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1206,7 +1174,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 55.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1228,7 +1195,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 30.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1250,7 +1216,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 30.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1272,7 +1237,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 10.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1294,7 +1258,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 60.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1316,7 +1279,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 70.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1338,7 +1300,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 80.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1360,7 +1321,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 90.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1382,7 +1342,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 100.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1404,7 +1363,6 @@
"type": "Fiber",
"type_variety": "SSMF",
"params": {
"type_variety": "SSMF",
"length": 110.0,
"loss_coef": 0.2,
"length_units": "km",
@@ -1459,25 +1417,6 @@
}
}
},
{
"uid": "east edfa in Stbrieuc to Rennes_STA",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 18.5,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 3.0,
"longitude": 0.0,
"city": "Stbrieuc",
"region": "RLD"
}
}
},
{
"uid": "east edfa in Lannion_CAS to Morlaix",
"type": "Edfa",
@@ -1497,6 +1436,25 @@
}
}
},
{
"uid": "east edfa in Stbrieuc to Rennes_STA",
"type": "Edfa",
"type_variety": "std_medium_gain",
"operational": {
"gain_target": 18.5,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 3.0,
"longitude": 0.0,
"city": "Stbrieuc",
"region": "RLD"
}
}
},
{
"uid": "east edfa in Brest_KLA to Quimper",
"type": "Edfa",
@@ -1593,7 +1551,7 @@
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20.0,
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
@@ -1612,7 +1570,7 @@
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20.0,
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
@@ -1740,88 +1698,12 @@
}
}
},
{
"uid": "Edfa0_roadm a",
"type": "Edfa",
"type_variety": "std_booster",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.75,
"longitude": 0.0,
"city": "a",
"region": ""
}
}
},
{
"uid": "Edfa1_roadm a",
"type": "Edfa",
"type_variety": "std_booster",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 6.0,
"longitude": 0.25,
"city": "a",
"region": ""
}
}
},
{
"uid": "Edfa0_roadm b",
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.25,
"longitude": 0.0,
"city": "b",
"region": ""
}
}
},
{
"uid": "Edfa1_roadm b",
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.0,
"longitude": 0.25,
"city": "b",
"region": ""
}
}
},
{
"uid": "Edfa0_roadm c",
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 22,
"gain_target": 22.0,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
@@ -1840,7 +1722,7 @@
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 22,
"gain_target": 22.0,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
@@ -2063,6 +1945,82 @@
}
}
},
{
"uid": "Edfa0_roadm a",
"type": "Edfa",
"type_variety": "std_booster",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.75,
"longitude": 0.0,
"city": "a",
"region": ""
}
}
},
{
"uid": "Edfa1_roadm a",
"type": "Edfa",
"type_variety": "std_booster",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 6.0,
"longitude": 0.25,
"city": "a",
"region": ""
}
}
},
{
"uid": "Edfa0_roadm b",
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.25,
"longitude": 0.0,
"city": "b",
"region": ""
}
}
},
{
"uid": "Edfa1_roadm b",
"type": "Edfa",
"type_variety": "test_fixed_gain",
"operational": {
"gain_target": 20,
"delta_p": null,
"tilt_target": 0,
"out_voa": 0
},
"metadata": {
"location": {
"latitude": 5.0,
"longitude": 0.25,
"city": "b",
"region": ""
}
}
},
{
"uid": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054",
"type": "Edfa",
@@ -2797,30 +2755,6 @@
"from_node": "roadm c",
"to_node": "east edfa in c to d"
},
{
"from_node": "roadm a",
"to_node": "trx a"
},
{
"from_node": "roadm a",
"to_node": "Edfa0_roadm a"
},
{
"from_node": "roadm a",
"to_node": "Edfa1_roadm a"
},
{
"from_node": "roadm b",
"to_node": "trx b"
},
{
"from_node": "roadm b",
"to_node": "Edfa0_roadm b"
},
{
"from_node": "roadm b",
"to_node": "Edfa1_roadm b"
},
{
"from_node": "roadm c",
"to_node": "trx c"
@@ -2897,6 +2831,30 @@
"from_node": "roadm h",
"to_node": "Edfa1_roadm h"
},
{
"from_node": "roadm a",
"to_node": "trx a"
},
{
"from_node": "roadm a",
"to_node": "Edfa0_roadm a"
},
{
"from_node": "roadm a",
"to_node": "Edfa1_roadm a"
},
{
"from_node": "roadm b",
"to_node": "trx b"
},
{
"from_node": "roadm b",
"to_node": "Edfa0_roadm b"
},
{
"from_node": "roadm b",
"to_node": "Edfa1_roadm b"
},
{
"from_node": "west fused spans in Corlay",
"to_node": "fiber (Corlay → Loudeac)-F010"
@@ -3097,14 +3055,14 @@
"from_node": "east edfa in Lannion_CAS to Stbrieuc",
"to_node": "fiber (Lannion_CAS → Stbrieuc)-F056"
},
{
"from_node": "east edfa in Stbrieuc to Rennes_STA",
"to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
},
{
"from_node": "east edfa in Lannion_CAS to Morlaix",
"to_node": "fiber (Lannion_CAS → Morlaix)-F059"
},
{
"from_node": "east edfa in Stbrieuc to Rennes_STA",
"to_node": "fiber (Stbrieuc → Rennes_STA)-F057"
},
{
"from_node": "east edfa in Brest_KLA to Quimper",
"to_node": "fiber (Brest_KLA → Quimper)-"
@@ -3157,22 +3115,6 @@
"from_node": "Edfa0_roadm Brest_KLA",
"to_node": "fiber (Brest_KLA → Morlaix)-F060"
},
{
"from_node": "Edfa0_roadm a",
"to_node": "fiber (a → b)-"
},
{
"from_node": "Edfa1_roadm a",
"to_node": "fiber (a → c)-"
},
{
"from_node": "Edfa0_roadm b",
"to_node": "fiber (b → a)-"
},
{
"from_node": "Edfa1_roadm b",
"to_node": "fiber (b → f)-"
},
{
"from_node": "Edfa0_roadm c",
"to_node": "fiber (c → a)-"
@@ -3225,6 +3167,22 @@
"from_node": "Edfa1_roadm h",
"to_node": "fiber (h → g)-"
},
{
"from_node": "Edfa0_roadm a",
"to_node": "fiber (a → b)-"
},
{
"from_node": "Edfa1_roadm a",
"to_node": "fiber (a → c)-"
},
{
"from_node": "Edfa0_roadm b",
"to_node": "fiber (b → a)-"
},
{
"from_node": "Edfa1_roadm b",
"to_node": "fiber (b → f)-"
},
{
"from_node": "Edfa0_fiber (Loudeac → Lorient_KMA)-F054",
"to_node": "roadm Lorient_KMA"
@@ -3354,4 +3312,4 @@
"to_node": "roadm h"
}
]
}
}

View File

@@ -0,0 +1,14 @@
{
"raman_parameters": {
"flag_raman": true,
"space_resolution": 10e3,
"tolerance": 1e-8
},
"nli_parameters": {
"nli_method_name": "ggn_spectrally_separated",
"wdm_grid_size": 50e9,
"dispersion_tolerance": 1,
"phase_shift_tolerance": 0.1,
"raman_computed_channels": [1, 18, 37, 56, 75]
}
}

40
tests/test_parameters.py Normal file
View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json, pytest
from pathlib import Path
from gnpy.core.parameters import SimParams
from gnpy.core.science_utils import Simulation
from gnpy.core.elements import Fiber
TEST_DIR = Path(__file__).parent
DATA_DIR = TEST_DIR / 'data'
def test_sim_parameters():
f = open(DATA_DIR / 'test_sim_params.json')
j = json.load(f)
sim_params = SimParams(**j)
Simulation.set_params(sim_params)
s1 = Simulation.get_simulation()
assert s1.sim_params.raman_params.flag_raman
s2 = Simulation.get_simulation()
assert s2.sim_params.raman_params.flag_raman
j['raman_parameters']['flag_raman'] = False
sim_params = SimParams(**j)
Simulation.set_params(sim_params)
assert not s2.sim_params.raman_params.flag_raman
assert not s1.sim_params.raman_params.flag_raman
if __name__ == '__main__':
from logging import getLogger, basicConfig, INFO
logger = getLogger(__name__)
basicConfig(level=INFO)
test_sim_parameters()
print('\n')

View File

@@ -39,9 +39,9 @@ def propagation(input_power, con_in, con_out,dest):
# (assumes all spans are identical)
for e in network.nodes():
if isinstance(e, Fiber):
loss = e.loss_coef * e.length
e.con_in = con_in
e.con_out = con_out
loss = e.params.loss_coef * e.params.length
e.params.con_in = con_in
e.params.con_out = con_out
if isinstance(e, Edfa):
e.operational.gain_target = loss + con_in + con_out
@@ -63,30 +63,30 @@ def propagation(input_power, con_in, con_out,dest):
print(f'pw: {input_power} conn in: {con_in} con out: {con_out}',
f'OSNR@0.1nm: {round(mean(sink.osnr_ase_01nm),2)}',
f'SNR@bandwitdth: {round(mean(sink.snr),2)}')
return sink , nf
return sink, nf
test = {'a':(-1,1,0),'b':(-1,1,1),'c':(0,1,0),'d':(1,1,1)}
expected = {'a':(-2,0,0),'b':(-2,0,1),'c':(-1,0,0),'d':(0,0,1)}
expected = {'a': (-2, 0, 0), 'b': (-2, 0, 1), 'c': (-1, 0, 0), 'd': (0, 0, 1)}
@pytest.mark.parametrize("dest",['trx B','trx F'])
@pytest.mark.parametrize("osnr_test", ['a','b','c','d'])
@pytest.mark.parametrize("osnr_test", ['a', 'b', 'c', 'd'])
def test_snr(osnr_test, dest):
pw = test[osnr_test][0]
conn_in = test[osnr_test][1]
conn_out =test[osnr_test][2]
sink,nf = propagation(pw,conn_in,conn_out,dest)
osnr = round(mean(sink.osnr_ase),3)
nli = 1.0/db2lin(round(mean(sink.snr),3)) - 1.0/db2lin(osnr)
conn_out = test[osnr_test][2]
sink, nf = propagation(pw, conn_in, conn_out, dest)
osnr = round(mean(sink.osnr_ase), 3)
nli = 1.0/db2lin(round(mean(sink.snr), 3)) - 1.0/db2lin(osnr)
pw = expected[osnr_test][0]
conn_in = expected[osnr_test][1]
conn_out = expected[osnr_test][2]
sink,exp_nf = propagation(pw,conn_in,conn_out,dest)
expected_osnr = round(mean(sink.osnr_ase),3)
expected_nli = 1.0/db2lin(round(mean(sink.snr),3)) - 1.0/db2lin(expected_osnr)
sink,exp_nf = propagation(pw, conn_in, conn_out, dest)
expected_osnr = round(mean(sink.osnr_ase), 3)
expected_nli = 1.0/db2lin(round(mean(sink.snr), 3)) - 1.0/db2lin(expected_osnr)
# compare OSNR taking into account nf change of amps
osnr_diff = abs(osnr - expected_osnr + nf - exp_nf)
nli_diff = abs((nli-expected_nli)/nli)
assert osnr_diff <0.01 and nli_diff<0.01
assert osnr_diff < 0.01 and nli_diff < 0.01
if __name__ == '__main__':
@@ -94,6 +94,6 @@ if __name__ == '__main__':
logger = getLogger(__name__)
basicConfig(level=INFO)
for a in test :
test_snr(a,'trx F')
for a in test:
test_snr(a, 'trx F')
print('\n')

View File

@@ -11,7 +11,9 @@ from pandas import read_csv
from numpy.testing import assert_allclose
from gnpy.core.info import create_input_spectral_information
from gnpy.core.elements import RamanFiber
from gnpy.core.network import load_sim_params
from gnpy.core.parameters import SimParams
from gnpy.core.science_utils import Simulation
from gnpy.core.utils import load_json
from pathlib import Path
TEST_DIR = Path(__file__).parent
@@ -29,12 +31,9 @@ def test_raman_fiber():
spectral_info_params.pop('sys_margins')
spectral_info_input = create_input_spectral_information(power=power, **spectral_info_params)
# RamanFiber
with open(TEST_DIR / 'data' / 'raman_fiber_config.json', 'r') as file:
raman_fiber_params = json.load(file)
sim_params = load_sim_params(TEST_DIR / 'data' / 'sim_params.json')
fiber = RamanFiber(**raman_fiber_params)
fiber.sim_params = sim_params
sim_params = SimParams(**load_json(TEST_DIR / 'data' / 'sim_params.json'))
Simulation.set_params(sim_params)
fiber = RamanFiber(**load_json(TEST_DIR / 'data' / 'raman_fiber_config.json'))
# propagation
spectral_info_out = fiber(spectral_info_input)