mirror of
				https://github.com/Telecominfraproject/oopt-gnpy.git
				synced 2025-10-30 17:47:50 +00:00 
			
		
		
		
	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
					AndreaDAmico
				
			
				
					committed by
					
						 Jan Kundrát
						Jan Kundrát
					
				
			
			
				
	
			
			
			 Jan Kundrát
						Jan Kundrát
					
				
			
						parent
						
							2960d307fa
						
					
				
				
					commit
					80eced85ec
				
			| @@ -412,7 +412,6 @@ parameters: | ||||
|         "type_variety": "SSMF", | ||||
|         "params": | ||||
|         { | ||||
|               "type_variety": "SSMF", | ||||
|               "length": 120.0, | ||||
|               "loss_coef": 0.2, | ||||
|               "length_units": "km", | ||||
|   | ||||
| @@ -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]  | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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,         | ||||
|   | ||||
| @@ -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''' | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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
											
										
									
								
							| @@ -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] | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -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" | ||||
|     } | ||||
|   ] | ||||
| } | ||||
| } | ||||
							
								
								
									
										14
									
								
								tests/data/test_sim_params.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								tests/data/test_sim_params.json
									
									
									
									
									
										Normal 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
									
								
							
							
						
						
									
										40
									
								
								tests/test_parameters.py
									
									
									
									
									
										Normal 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') | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -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') | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user