diff --git a/docs/json.rst b/docs/json.rst index c5087a1a..535ef3c4 100644 --- a/docs/json.rst +++ b/docs/json.rst @@ -93,6 +93,33 @@ The fiber library currently describes SSMF and NZDF but additional fiber types c .. _Corning whitepaper on MFD/EA: https://www.corning.com/microsites/coc/oem/documents/specialty-fiber/WP7071-Mode-Field-Diam-and-Eff-Area.pdf +RamanFiber +~~~~~~~~~~ + +The RamanFiber can be used to simulate Raman amplification through dedicated Raman pumps. The Raman pumps must be listed +in the key ``raman_pumps`` within the RamanFiber ``operational`` dictionary. The description of each Raman pump must +contain the following: + ++---------------------------+-----------+------------------------------------------------------------+ +| field | type | description | ++===========================+===========+============================================================+ +| ``power`` | (number) | Total pump power in :math:`W` | +| | | considering a depolarized pump | ++---------------------------+-----------+------------------------------------------------------------+ +| ``frequency`` | (number) | Pump central frequency in :math:`Hz` | ++---------------------------+-----------+------------------------------------------------------------+ +| ``propagation_direction`` | (number) | The pumps can propagate in the same or opposite direction | +| | | with respect the signal. Valid choices are ``coprop`` and | +| | | ``counterprop``, respectively | ++---------------------------+-----------+------------------------------------------------------------+ + +Beside the list of Raman pumps, the RamanFiber ``operational`` dictionary must include the ``temperature`` that affects +the amplified spontaneous emission noise generated by the Raman amplification. +As the loss coefficient significantly varies outside the C-band, where the Raman pumps are usually placed, +it is suggested to include an estimation of the loss coefficient for the Raman pump central frequencies within +a dictionary-like definition of the ``RamanFiber.params.loss_coef`` +(e.g. ``loss_coef = {"value": [0.18, 0.18, 0.20, 0.20], "frequency": [191e12, 196e12, 200e12, 210e12]}``). + Transceiver ~~~~~~~~~~~ @@ -191,45 +218,57 @@ For amplifiers defined in the topology JSON input but whose ``gain = 0`` (placeh The file ``sim_params.json`` contains the tuning parameters used within both the ``gnpy.science_utils.RamanSolver`` and the ``gnpy.science_utils.NliSolver`` for the evaluation of the Raman profile and the NLI generation, respectively. -+-----------------------------------------+-----------+---------------------------------------------+ -| field | type | description | -+=========================================+===========+=============================================+ -| ``raman_params.flag`` | (boolean) | Enable/Disable the Raman effect that | -| | | produces a power transfer from higher to | -| | | lower frequencies. | -| | | In general, considering the Raman effect | -| | | provides more accurate results. It is | -| | | mandatory when Raman amplification is | -| | | included in the simulation | -+-----------------------------------------+-----------+---------------------------------------------+ -| ``raman_params.space_resolution`` | (number) | Spatial resolution of the output | -| | | Raman profile along the entire fiber span. | -| | | This affects the accuracy and the | -| | | computational time of the NLI | -| | | calculation when the GGN method is used: | -| | | smaller the space resolution higher both | -| | | the accuracy and the computational time. | -| | | In C-band simulations, with input power per | -| | | channel around 0 dBm, a suggested value of | -| | | space resolution is 10e3 m | -+-----------------------------------------+-----------+---------------------------------------------+ -| ``nli_params.method`` | (string) | Model used for the NLI evaluation. Valid | -| | | choices are ``gn_model_analytic`` (see | -| | | eq. 120 from `arXiv:1209.0394 | -| | | `_) and | -| | | ``ggn_spectrally_separated`` (see eq. 21 | -| | | from `arXiv:1710.02225 | -| | | `_). | -+-----------------------------------------+-----------+---------------------------------------------+ -| ``nli_params.computed_channels`` | (number) | The channels on which the NLI is | -| | | explicitly evaluated. | -| | | The NLI of the other channels is | -| | | interpolated using ``numpy.interp``. | -| | | In a C-band simulation with 96 channels in | -| | | a 50 GHz spacing fix-grid we recommend at | -| | | one computed channel every 20 channels. | -+-----------------------------------------+-----------+---------------------------------------------+ - ++---------------------------------------------+-----------+---------------------------------------------+ +| field | type | description | ++=============================================+===========+=============================================+ +| ``raman_params.flag`` | (boolean) | Enable/Disable the Raman effect that | +| | | produces a power transfer from higher to | +| | | lower frequencies. | +| | | In general, considering the Raman effect | +| | | provides more accurate results. It is | +| | | mandatory when Raman amplification is | +| | | included in the simulation | ++---------------------------------------------+-----------+---------------------------------------------+ +| ``raman_params.result_spatial_resolution`` | (number) | Spatial resolution of the output | +| | | Raman profile along the entire fiber span. | +| | | This affects the accuracy and the | +| | | computational time of the NLI | +| | | calculation when the GGN method is used: | +| | | smaller the spatial resolution higher both | +| | | the accuracy and the computational time. | +| | | In C-band simulations, with input power per | +| | | channel around 0 dBm, a suggested value of | +| | | spatial resolution is 10e3 m | ++---------------------------------------------+-----------+---------------------------------------------+ +| ``raman_params.solver_spatial_resolution`` | (number) | Spatial step for the iterative solution | +| | | of the first order differential equation | +| | | used to calculate the Raman profile | +| | | along the entire fiber span. | +| | | This affects the accuracy and the | +| | | computational time of the evaluated | +| | | Raman profile: | +| | | smaller the spatial resolution higher both | +| | | the accuracy and the computational time. | +| | | In C-band simulations, with input power per | +| | | channel around 0 dBm, a suggested value of | +| | | spatial resolution is 100 m | ++---------------------------------------------+-----------+---------------------------------------------+ +| ``nli_params.method`` | (string) | Model used for the NLI evaluation. Valid | +| | | choices are ``gn_model_analytic`` (see | +| | | eq. 120 from `arXiv:1209.0394 | +| | | `_) and | +| | | ``ggn_spectrally_separated`` (see eq. 21 | +| | | from `arXiv:1710.02225 | +| | | `_). | ++---------------------------------------------+-----------+---------------------------------------------+ +| ``nli_params.computed_channels`` | (number) | The channels on which the NLI is | +| | | explicitly evaluated. | +| | | The NLI of the other channels is | +| | | interpolated using ``numpy.interp``. | +| | | In a C-band simulation with 96 channels in | +| | | a 50 GHz spacing fix-grid we recommend at | +| | | one computed channel every 20 channels. | ++---------------------------------------------+-----------+---------------------------------------------+ Span ~~~~ diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 1e8ca232..7452da0b 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -20,8 +20,8 @@ unique identifier and a printable name, and provide the :py:meth:`__call__` meth instance as a result. """ -from numpy import abs, array, divide, errstate, interp, mean, pi, polyfit, polyval, sum, sqrt, log10, exp,\ - asarray, full, squeeze +from numpy import abs, array, divide, errstate, ones, interp, mean, pi, polyfit, polyval, sum, sqrt, log10, exp,\ + asarray, full, squeeze, zeros, append, flip, outer from scipy.constants import h, c from scipy.interpolate import interp1d from collections import namedtuple @@ -30,7 +30,7 @@ from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum from gnpy.core.parameters import FiberParams, PumpParams from gnpy.core.science_utils import NliSolver, RamanSolver from gnpy.core.info import SpectralInformation -from gnpy.core.exceptions import SpectrumError +from gnpy.core.exceptions import NetworkTopologyError, SpectrumError class Location(namedtuple('Location', 'latitude longitude city region')): @@ -313,9 +313,20 @@ class Fiber(_Node): params = {} super().__init__(*args, params=FiberParams(**params), **kwargs) self.pch_out_db = None - self.nli_solver = NliSolver(self) self.passive = True + # Raman efficiency matrix function of the delta frequency constructed such that each row is related to a + # fixed frequency: positive elements represent a gain (from higher frequency) and negative elements represent + # a loss (to lower frequency) + if self.params.raman_efficiency: + frequency_offset = self.params.raman_efficiency['frequency_offset'] + frequency_offset = append(-flip(frequency_offset[1:]), frequency_offset) + cr = self.params.raman_efficiency['cr'] + cr = append(- flip(cr[1:]), cr) + self._cr_function = lambda frequency: interp(frequency, frequency_offset, cr) + else: + self._cr_function = lambda frequency: zeros(squeeze(frequency).shape) + @property def to_json(self): return {'uid': self.uid, @@ -376,9 +387,6 @@ class Fiber(_Node): return self.loss_coef_func(self.params.ref_frequency) * self.params.length + \ self.params.con_in + self.params.con_out + self.params.att_in - def lin_attenuation(self, frequency): - return 1 / db2lin(self.params.length * self.loss_coef_func(frequency)) - def alpha(self, frequency): """Returns the linear exponent attenuation coefficient such that :math: `lin_attenuation = e^{- alpha length}` @@ -388,6 +396,17 @@ class Fiber(_Node): """ return self.loss_coef_func(frequency) / (10 * log10(exp(1))) + def cr(self, frequency): + """Returns the raman efficiency matrix including the vibrational loss + + :param frequency: the frequency at which cr is computed [Hz] + :return: cr: raman efficiency matrix [1 / (W m)] + """ + df = outer(ones(frequency.shape), frequency) - outer(frequency, ones(frequency.shape)) + cr = self._cr_function(df) + vibrational_loss = outer(frequency, ones(frequency.shape)) / outer(ones(frequency.shape), frequency) + return cr * (cr >= 0) + cr * (cr < 0) * vibrational_loss # Raman efficiency [1/(W m)] + def chromatic_dispersion(self, freq=None): """Returns accumulated chromatic dispersion (CD). @@ -412,21 +431,25 @@ class Fiber(_Node): """Modifies the spectral information computing the attenuation, the non-linear interference generation, the CD and PMD accumulation. """ - + # apply the attenuation due to the input connector loss attenuation_in_db = self.params.con_in + self.params.att_in spectral_info.apply_attenuation_db(attenuation_in_db) + # inter channels Raman effect + stimulated_raman_scattering = RamanSolver.calculate_stimulated_raman_scattering(spectral_info, self) + # NLI noise evaluated at the fiber input - nli = self.nli_solver.compute_nli(spectral_info) - spectral_info.nli += nli + spectral_info.nli += NliSolver.compute_nli(spectral_info, stimulated_raman_scattering, self) # chromatic dispersion and pmd variations spectral_info.chromatic_dispersion += self.chromatic_dispersion(spectral_info.frequency) spectral_info.pmd = sqrt(spectral_info.pmd ** 2 + self.pmd ** 2) # apply the attenuation due to the fiber losses - spectral_info.apply_attenuation_lin(self.lin_attenuation(spectral_info.frequency)) + attenuation_fiber = stimulated_raman_scattering.loss_profile[:, -1] + spectral_info.apply_attenuation_lin(attenuation_fiber) + # apply the attenuation due to the output connector loss attenuation_out_db = self.params.con_out spectral_info.apply_attenuation_db(attenuation_out_db) @@ -451,44 +474,50 @@ class Fiber(_Node): class RamanFiber(Fiber): def __init__(self, *args, params=None, **kwargs): 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) + if not self.operational: + raise NetworkTopologyError(f'Fiber element uid:{self.uid} ' + 'defined as RamanFiber without operational parameters') - @property - def to_json(self): - return dict(super().to_json, operational=self.operational) + if 'raman_pumps' not in self.operational: + raise NetworkTopologyError(f'Fiber element uid:{self.uid} ' + 'defined as RamanFiber without raman pumps description in operational') + + if 'temperature' not in self.operational: + raise NetworkTopologyError(f'Fiber element uid:{self.uid} ' + 'defined as RamanFiber without temperature in operational') + + pump_loss = db2lin(self.params.con_out) + self.raman_pumps = tuple(PumpParams(p['power'] / pump_loss, p['frequency'], p['propagation_direction']) + for p in self.operational['raman_pumps']) + self.temperature = self.operational['temperature'] def propagate(self, spectral_info: SpectralInformation): """Modifies the spectral information computing the attenuation, the non-linear interference generation, the CD and PMD accumulation. """ + # apply the attenuation due to the input connector loss attenuation_in_db = self.params.con_in + self.params.att_in spectral_info.apply_attenuation_db(attenuation_in_db) - self.raman_solver.carriers = spectral_info.carriers - self.raman_solver.raman_pumps = self.raman_pumps - self.nli_solver.stimulated_raman_scattering = self.raman_solver.stimulated_raman_scattering - raman_ase = self.raman_solver.spontaneous_raman_scattering.power[:spectral_info.number_of_channels, -1] + # Raman pumps and inter channel Raman effect + stimulated_raman_scattering = RamanSolver.calculate_stimulated_raman_scattering(spectral_info, self) + spontaneous_raman_scattering = \ + RamanSolver.calculate_spontaneous_raman_scattering(spectral_info, stimulated_raman_scattering, self) - # NLI noise evaluated at the fiber input - nli = self.nli_solver.compute_nli(spectral_info) - spectral_info.nli += nli + # nli and ase noise evaluated at the fiber input + spectral_info.nli += NliSolver.compute_nli(spectral_info, stimulated_raman_scattering, self) + spectral_info.ase += spontaneous_raman_scattering # chromatic dispersion and pmd variations spectral_info.chromatic_dispersion += self.chromatic_dispersion(spectral_info.frequency) spectral_info.pmd = sqrt(spectral_info.pmd ** 2 + self.pmd ** 2) # apply the attenuation due to the fiber losses - attenuation_fiber = \ - self.raman_solver.stimulated_raman_scattering.rho[:spectral_info.number_of_channels, -1] ** 2 + attenuation_fiber = stimulated_raman_scattering.loss_profile[:spectral_info.number_of_channels, -1] + spectral_info.apply_attenuation_lin(attenuation_fiber) - spectral_info.ase += raman_ase - + # apply the attenuation due to the output connector loss attenuation_out_db = self.params.con_out spectral_info.apply_attenuation_db(attenuation_out_db) diff --git a/gnpy/core/parameters.py b/gnpy/core/parameters.py index 7db9435c..59b78ba8 100644 --- a/gnpy/core/parameters.py +++ b/gnpy/core/parameters.py @@ -34,15 +34,15 @@ class PumpParams(Parameters): class RamanParams(Parameters): - def __init__(self, flag=False, space_resolution=10e3, tolerance=None): + def __init__(self, flag=False, result_spatial_resolution=10e3, solver_spatial_resolution=50): """ Simulation parameters used within the Raman Solver - :params flag: boolean for enabling/disable the evaluation of the Raman Power profile in frequency and position - :params space_resolution: spatial resolution of the evaluated Raman Power profile - :params tolerance: tuning parameter for scipy.integrate.solve_bvp solution + :params flag: boolean for enabling/disable the evaluation of the Raman power profile in frequency and position + :params result_spatial_resolution: spatial resolution of the evaluated Raman power profile + :params solver_spatial_resolution: spatial step for the iterative solution of the first order ode """ self.flag = flag - self.space_resolution = space_resolution # [m] - self.tolerance = tolerance + self.result_spatial_resolution = result_spatial_resolution # [m] + self.solver_spatial_resolution = solver_spatial_resolution # [m] class NLIParams(Parameters): @@ -156,7 +156,6 @@ class FiberParams(Parameters): else: self._loss_coef = asarray(kwargs['loss_coef']) * 1e-3 # lineic loss dB/m self._f_loss_ref = asarray(self._ref_frequency) # Hz - self._pumps_loss_coef = kwargs.get('pumps_loss_coef') except KeyError as e: raise ParametersError(f'Fiber configurations json must include {e}. Configuration: {kwargs}') @@ -237,12 +236,10 @@ class FiberParams(Parameters): def raman_efficiency(self): return self._raman_efficiency - @property - def pumps_loss_coef(self): - return self._pumps_loss_coef - def asdict(self): dictionary = super().asdict() dictionary['loss_coef'] = self.loss_coef * 1e3 dictionary['length_units'] = 'm' + if not self.raman_efficiency: + dictionary.pop('raman_efficiency') return dictionary diff --git a/gnpy/core/science_utils.py b/gnpy/core/science_utils.py index f7d94ec0..20c4a065 100644 --- a/gnpy/core/science_utils.py +++ b/gnpy/core/science_utils.py @@ -10,15 +10,11 @@ Solver definitions to calculate the Raman effect and the nonlinear interference The solvers take as input instances of the spectral information, the fiber and the simulation parameters """ -from numpy import interp, pi, zeros, shape, where, cos, reshape, array, append, ones, argsort, nan, exp, arange, sqrt, \ - empty, vstack, trapz, arcsinh, clip, abs, sum, outer, diag -from operator import attrgetter +from numpy import interp, pi, zeros, shape, where, cos, array, append, ones, exp, arange, sqrt, empty, trapz, arcsinh, \ + clip, abs, sum, concatenate, flip, outer, inner, transpose, max, format_float_scientific, diag from logging import getLogger -import scipy.constants as ph -from scipy.integrate import solve_bvp -from scipy.integrate import cumtrapz +from scipy.constants import k, h from scipy.interpolate import interp1d -from scipy.optimize import OptimizeResult from math import isclose from gnpy.core.utils import db2lin, lin2db @@ -29,10 +25,10 @@ from gnpy.core.info import SpectralInformation logger = getLogger(__name__) sim_params = SimParams.get() - 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 + :param f: numpy array of frequencies in Hz :param carriers: namedtuple describing the WDM comb :return: PSD of the WDM comb evaluated over f @@ -54,277 +50,199 @@ def raised_cosine_comb(f, *carriers): return psd -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): + def __init__(self, power_profile, loss_profile, frequency, z): + """ + :params power_profile: power profile matrix along frequency and z [W] + :params loss_profile: power profile matrix along frequency and z [linear units] + :params frequency: channels frequencies array [Hz] + :params z: positions array [m] + """ + self.power_profile = power_profile + self.loss_profile = loss_profile + # Field loss profile matrix along frequency and z + self.rho = sqrt(loss_profile) self.frequency = frequency self.z = z - self.rho = rho - self.power = power class RamanSolver: - 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 = fiber - self._carriers = None - self._raman_pumps = None - self._stimulated_raman_scattering = None - self._spontaneous_raman_scattering = None - - @property - def fiber(self): - return self._fiber - - @property - def carriers(self): - return self._carriers - - @carriers.setter - def carriers(self, carriers): - self._carriers = carriers - self._spontaneous_raman_scattering = None - self._stimulated_raman_scattering = None - - @property - def raman_pumps(self): - return self._raman_pumps - - @raman_pumps.setter - def raman_pumps(self, raman_pumps): - self._raman_pumps = raman_pumps - self._stimulated_raman_scattering = None - - @property - 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: - 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 - 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 = 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 + """This class contains the methods to calculate the Raman scattering effect.""" @staticmethod - def _compute_power_spectrum(carriers, raman_pumps=None): + def calculate_attenuation_profile(spectral_info: SpectralInformation, fiber): + """Evaluates the attenuation profile along the z axis for all the frequency propagating in the + fiber without considering the stimulated Raman scattering. """ - Rearrangement of spectral and Raman pump information to make them compatible with Raman solver - :param carriers: a tuple of namedtuples describing the transmitted channels - :param raman_pumps: a namedtuple describing the Raman pumps - :return: + # z array definition + z = array([0, fiber.params.length]) + frequency = spectral_info.frequency + alpha = fiber.alpha(frequency) + loss_profile = exp(- outer(alpha, z)) + power_profile = outer(spectral_info.signal, ones(z.size)) * loss_profile + stimulated_raman_scattering = StimulatedRamanScattering(power_profile, loss_profile, frequency, z) + return stimulated_raman_scattering + + @staticmethod + def calculate_stimulated_raman_scattering(spectral_info: SpectralInformation, fiber): + """Evaluates the Raman profile along the z axis for all the frequency propagated in the fiber + including the Raman pumps co- and counter-propagating """ - - # Signal power spectrum - pow_array = array([]) - f_array = array([]) - noise_bandwidth_array = array([]) - for carrier in sorted(carriers, key=attrgetter('frequency')): - f_array = append(f_array, carrier.frequency) - pow_array = append(pow_array, carrier.power.signal) - ref_bw = carrier.baud_rate - noise_bandwidth_array = append(noise_bandwidth_array, ref_bw) - - propagation_direction = ones(len(f_array)) - - # Raman pump power spectrum - if raman_pumps: - for pump in raman_pumps: - pow_array = append(pow_array, pump.power) - f_array = append(f_array, pump.frequency) - direction = +1 if pump.propagation_direction == 'coprop' else -1 - propagation_direction = append(propagation_direction, direction) - noise_bandwidth_array = append(noise_bandwidth_array, ref_bw) - - # Final sorting - ind = argsort(f_array) - f_array = f_array[ind] - pow_array = pow_array[ind] - propagation_direction = propagation_direction[ind] - - 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): - spontaneous_raman_scattering = OptimizeResult() - - dx = sim_params.raman_params.space_resolution - h = ph.value('Planck constant') - kb = ph.value('Boltzmann constant') - - power_ase = nan * ones(raman_matrix.shape) - int_pump = cumtrapz(raman_matrix, z_array, dx=dx, axis=1, initial=0) - - for f_ind, f_ase in enumerate(freq_array): - cr_raman = cr_raman_matrix[f_ind, :] - vibrational_loss = f_ase / freq_array[:f_ind] - eta = 1 / (exp((h * freq_diff[f_ind, f_ind + 1:]) / (kb * temperature)) - 1) - - int_fiber_loss = -alphap_fiber[f_ind] * z_array - int_raman_loss = sum((cr_raman[:f_ind] * vibrational_loss * int_pump[:f_ind, :].transpose()).transpose(), - axis=0) - int_raman_gain = 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 = 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] * exp(int_gain_loss) - ase_evolution = exp(int_gain_loss) * cumtrapz(new_ase * exp(-int_gain_loss), z_array, dx=dx, initial=0) - - power_ase[f_ind, :] = bc_evolution + ase_evolution - - spontaneous_raman_scattering.x = 2 * power_ase - return spontaneous_raman_scattering - - def calculate_stimulated_raman_scattering(self, carriers, raman_pumps): - """ Returns stimulated Raman scattering solution including - fiber gain/loss profile. - :return: None - """ - # fiber parameters - fiber_length = self.fiber.params.length - raman_efficiency = self.fiber.params.raman_efficiency - - if not sim_params.raman_params.flag: - raman_efficiency['cr'] = 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') - power_spectrum, freq_array, prop_direct, _ = self._compute_power_spectrum(carriers, raman_pumps) + # Raman parameters + z_resolution = sim_params.raman_params.result_spatial_resolution + z_step = sim_params.raman_params.solver_spatial_resolution + z = append(arange(0, fiber.params.length, z_step), fiber.params.length) + z_final = append(arange(0, fiber.params.length, z_resolution), fiber.params.length) - alphap_fiber = self.fiber.alpha(freq_array) + if sim_params.raman_params.flag: + if hasattr(fiber, 'raman_pumps'): + # TODO: verify co-propagating pumps computation and in general unsorted frequency + # Co-propagating spectrum definition + co_raman_pump_power = array([pump.power for pump in fiber.raman_pumps + if pump.propagation_direction == 'coprop']) + co_raman_pump_frequency = array([pump.frequency for pump in fiber.raman_pumps + if pump.propagation_direction == 'coprop']) - freq_diff = abs(freq_array - reshape(freq_array, (len(freq_array), 1))) - interp_cr = interp1d(raman_efficiency['frequency_offset'], raman_efficiency['cr']) - cr = interp_cr(freq_diff) + co_power = concatenate((spectral_info.signal, co_raman_pump_power)) + co_frequency = concatenate((spectral_info.frequency, co_raman_pump_frequency)) - # z propagation axis - z = append(arange(0, fiber_length, z_resolution), fiber_length) - - def ode_function(z, p): - return self._ode_stimulated_raman(z, p, alphap_fiber, freq_array, cr, prop_direct) - - def boundary_residual(ya, yb): - return 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 - bvp_solution = solve_bvp(ode_function, boundary_residual, z, initial_guess_conditions, tol=tolerance) - - rho = (bvp_solution.y.transpose() / power_spectrum).transpose() - rho = sqrt(rho) # From power attenuation to field attenuation - stimulated_raman_scattering = StimulatedRamanScattering(freq_array, bvp_solution.x, rho, bvp_solution.y) - - self._stimulated_raman_scattering = stimulated_raman_scattering - - def _residuals_stimulated_raman(self, ya, yb, power_spectrum, prop_direct): - - computed_boundary_value = zeros(ya.size) - - for index, direction in enumerate(prop_direct): - if direction == +1: - computed_boundary_value[index] = ya[index] + # Counter-propagating spectrum definition + cnt_power = array([pump.power for pump in fiber.raman_pumps + if pump.propagation_direction == 'counterprop']) + cnt_frequency = array([pump.frequency for pump in fiber.raman_pumps + if pump.propagation_direction == 'counterprop']) + # Co-propagating profile initialization + co_power_profile = empty([co_frequency.size, z.size]) + if co_frequency.size: + co_cr = fiber.cr(co_frequency) + co_alpha = fiber.alpha(co_frequency) + co_power_profile = \ + RamanSolver.first_order_derivative_solution(co_power, co_alpha, co_cr, z) + # Counter-propagating profile initialization + cnt_power_profile = empty([co_frequency.size, z.size]) + if cnt_frequency.size: + cnt_cr = fiber.cr(cnt_frequency) + cnt_alpha = fiber.alpha(cnt_frequency) + cnt_power_profile = \ + flip(RamanSolver.first_order_derivative_solution(cnt_power, cnt_alpha, cnt_cr, z[-1] - flip(z))) + # Co-propagating and Counter-propagating Profile Computation + if co_frequency.size and cnt_frequency.size: + co_power_profile, cnt_power_profile = \ + RamanSolver.iterative_algorithm(co_power_profile, cnt_power_profile, co_frequency, cnt_frequency, + z, fiber) + # Complete Power Profile + power_profile = concatenate((co_power_profile, cnt_power_profile), axis=0) + # Complete Loss Profile + co_loss_profile = co_power_profile / outer(co_power, ones(z.size)) + cnt_loss_profile = cnt_power_profile / outer(cnt_power, ones(z.size)) + loss_profile = concatenate((co_loss_profile, cnt_loss_profile), axis=0) + # Complete frequency + frequency = concatenate((co_frequency, cnt_frequency)) else: - computed_boundary_value[index] = yb[index] + # Without Raman pumps + alpha = fiber.alpha(spectral_info.frequency) + cr = fiber.cr(spectral_info.frequency) + # Power profile + power_profile = \ + RamanSolver.first_order_derivative_solution(spectral_info.signal, alpha, cr, z) + # Loss profile + loss_profile = power_profile / outer(spectral_info.signal, ones(z.size)) + frequency = spectral_info.frequency + power_profile = interp1d(z, power_profile, axis=1)(z_final) + loss_profile = interp1d(z, loss_profile, axis=1)(z_final) + stimulated_raman_scattering = StimulatedRamanScattering(power_profile, loss_profile, frequency, z_final) + else: + stimulated_raman_scattering = RamanSolver.calculate_attenuation_profile(spectral_info, fiber) + return stimulated_raman_scattering - return power_spectrum - computed_boundary_value - - 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 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, - the second ndarray index identifies the step in z. ndarray + @staticmethod + def calculate_spontaneous_raman_scattering(spectral_info: SpectralInformation, srs: StimulatedRamanScattering, + fiber): + """Evaluates the Raman profile along the z axis for all the frequency propagated in the fiber + including the Raman pumps co- and counter-propagating. """ + logger.debug('Start computing fiber Spontaneous Raman Scattering') + z = srs.z + baud_rate = spectral_info.baud_rate + frequency = spectral_info.frequency + channels_loss = srs.loss_profile[:spectral_info.number_of_channels, :] - power_guess = empty((power_spectrum.size, z.size)) - for f_index, power_slice in enumerate(power_spectrum): - if prop_direct[f_index] == +1: - power_guess[f_index, :] = exp(-alphap_fiber[f_index] * z) * power_slice - else: - power_guess[f_index, :] = exp(-alphap_fiber[f_index] * z[::-1]) * power_slice + # calculate ase power + ase = zeros(spectral_info.number_of_channels) + for i, pump in enumerate(fiber.raman_pumps): + pump_power = srs.power_profile[spectral_info.number_of_channels + i, :] + df = pump.frequency - frequency + eta = - 1 / (1 - exp(h * df / (k * fiber.temperature))) + cr = fiber._cr_function(df) + integral = trapz(pump_power / channels_loss, z, axis=1) + ase += 2 * h * baud_rate * frequency * (1 + eta) * cr * (df > 0) * integral # 2 factor for double pol + return ase - return power_guess + @staticmethod + def first_order_derivative_solution(power_in, alpha, cr, z): + """Solves the Raman first order derivative equation - 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. - :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 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 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 - :return: dP/dz: the power variation in dz [W/m]. numpy array. Size n + :param power_in: launch power array + :param alpha: loss coefficient array + :param cr: Raman efficiency coefficients matrix + :param z: z position array + :return: power profile matrix """ + dz = z[1:] - z[:-1] + power = outer(power_in, ones(z.size)) + for i in range(1, z.size): + power[:, i] = power[:, i - 1] * (1 + (- alpha + sum(cr * power[:, i - 1], 1)) * dz[i - 1]) + return power - dpdz = nan * ones(power_spectrum.shape) - for f_ind, power in enumerate(power_spectrum): - cr_raman = cr_raman_matrix[f_ind, :] - vibrational_loss = freq_array[f_ind] / freq_array[:f_ind] + @staticmethod + def iterative_algorithm(co_initial_guess_power, cnt_initial_guess_power, co_frequency, cnt_frequency, z, fiber): + """Solves the Raman first order derivative equation in case of both co- and counter-propagating + frequencies - for z_ind, power_sample in enumerate(power): - raman_gain = sum(cr_raman[f_ind + 1:] * power_spectrum[f_ind + 1:, z_ind]) - raman_loss = sum(vibrational_loss * cr_raman[:f_ind] * power_spectrum[:f_ind, z_ind]) + :param co_initial_guess_power: co-propagationg Raman first order derivative equation solution + :param cnt_initial_guess_power: counter-propagationg Raman first order derivative equation solution + :param co_frequency: co-propagationg frequencies + :param cnt_frequency: counter-propagationg frequencies + :param z: z position array + :param fiber: instance of gnpy.core.elements.Fiber or gnpy.core.elements.RamanFiber + :return: co- and counter-propagatng power profile matrix + """ + logger.debug(' Start iterative algorithm') + residue = 1 + residue_tol = 1e-6 + accuracy = 1 + accuracy_tol = 1e-3 + iteration = 0 + num_max_iter = 1000 + prev_power = concatenate((co_initial_guess_power, cnt_initial_guess_power)) + frequency = concatenate((co_frequency, cnt_frequency)) + dz = z[1:] - z[:-1] + cr = fiber.cr(frequency) + alpha = fiber.alpha(frequency) + next_power = array(prev_power) + while residue > residue_tol and accuracy > accuracy_tol and iteration < num_max_iter: + iteration += 1 + for i in range(1, z.size): + dpdz = - alpha + sum(cr * next_power[:, i - 1], 1) + next_power[:co_frequency.size, i] = \ + next_power[:co_frequency.size, i - 1] * (1 + dpdz[:co_frequency.size] * dz[i - 1]) + for i in range(1, z.size): + dpdz = - alpha + sum(cr * next_power[:, -i], 1) + next_power[co_frequency.size:, -i - 1] = \ + next_power[co_frequency.size:, -i] * (1 + dpdz[co_frequency.size:] * dz[-i]) - dpdz_element = prop_direct[f_ind] * (-alphap_fiber[f_ind] + raman_gain - raman_loss) * power_sample - dpdz[f_ind][z_ind] = dpdz_element + dpdz_num = (next_power[:co_frequency.size, 1:] - next_power[:co_frequency.size, :-1]) / dz + dpdz_exp = next_power[:co_frequency.size, :-1] * \ + (- outer(alpha, ones(z.size)) + inner(cr, transpose(next_power)))[:co_frequency.size, :-1] + + residue = max(abs((next_power - prev_power) / next_power)) + accuracy = max(abs((dpdz_exp - dpdz_num) / dpdz_exp)) + prev_power = array(next_power) + logger.debug(f' Iteration: {iteration} Accuracy: {format_float_scientific(accuracy, precision=3)}') + return next_power[:co_frequency.size, :], next_power[co_frequency.size:, :] - return vstack(dpdz) class NliSolver: """ This class implements the NLI models. @@ -334,21 +252,6 @@ class NliSolver: 'ggn_spectrally_separated': eq. 21 from arXiv: 1710.02225 spectrally separated """ - def __init__(self, fiber=None): - """ Initialize the Nli solver object. - :param fiber: instance of elements.py/Fiber. - """ - self._fiber = fiber - self._stimulated_raman_scattering = None - - @property - def stimulated_raman_scattering(self): - return self._stimulated_raman_scattering - - @stimulated_raman_scattering.setter - def stimulated_raman_scattering(self, stimulated_raman_scattering): - self._stimulated_raman_scattering = stimulated_raman_scattering - @staticmethod def effective_length(alpha, length): """The effective length identify the region in which the NLI has a significant contribution to @@ -356,15 +259,13 @@ class NliSolver: """ return (1 - exp(- alpha * length)) / alpha - def compute_nli(self, spectral_info: SpectralInformation): + @staticmethod + def compute_nli(spectral_info: SpectralInformation, srs: StimulatedRamanScattering, fiber): """ Compute NLI power generated by the WDM comb `*carriers` on the channel under test `carrier` at the end of the fiber span. """ logger.debug('Start computing fiber NLI noise') # Physical fiber parameters - fiber = self._fiber - srs = self._stimulated_raman_scattering - alpha = fiber.alpha(spectral_info.frequency) beta2 = fiber.params.beta2 beta3 = fiber.params.beta3 @@ -429,7 +330,7 @@ class NliSolver: dispersion_tolerance = sim_params.nli_params.dispersion_tolerance phase_shift_tolerance = sim_params.nli_params.phase_shift_tolerance slot_width = max(spectral_info.slot_width) - delta_z = sim_params.raman_params.space_resolution + delta_z = sim_params.raman_params.result_spatial_resolution spm_weight = (16.0 / 27.0) * gamma ** 2 xpm_weight = 2 * (16.0 / 27.0) * gamma ** 2 cuts = [carrier for carrier in spectral_info.carriers if carrier.channel_number diff --git a/gnpy/example-data/raman_edfa_example_network.json b/gnpy/example-data/raman_edfa_example_network.json index 4974238f..9b014b7e 100644 --- a/gnpy/example-data/raman_edfa_example_network.json +++ b/gnpy/example-data/raman_edfa_example_network.json @@ -20,12 +20,12 @@ "temperature": 283, "raman_pumps": [ { - "power": 200e-3, + "power": 224.403e-3, "frequency": 205e12, "propagation_direction": "counterprop" }, { - "power": 206e-3, + "power": 231.135e-3, "frequency": 201e12, "propagation_direction": "counterprop" } @@ -49,6 +49,21 @@ } } }, + { + "uid": "Fused1", + "type": "Fused", + "params": { + "loss": 0 + }, + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 0, + "city": null, + "region": "" + } + } + }, { "uid": "Edfa1", "type": "Edfa", @@ -88,6 +103,10 @@ }, { "from_node": "Span1", + "to_node": "Fused1" + }, + { + "from_node": "Fused1", "to_node": "Edfa1" }, { diff --git a/gnpy/example-data/sim_params.json b/gnpy/example-data/sim_params.json index 35c91dfd..7a76b295 100644 --- a/gnpy/example-data/sim_params.json +++ b/gnpy/example-data/sim_params.json @@ -1,8 +1,8 @@ { "raman_params": { "flag": true, - "space_resolution": 10e3, - "tolerance": 1e-8 + "result_spatial_resolution": 10e3, + "solver_spatial_resolution": 50 }, "nli_params": { "method": "ggn_spectrally_separated", diff --git a/tests/data/sim_params.json b/tests/data/sim_params.json index 35c91dfd..7a76b295 100644 --- a/tests/data/sim_params.json +++ b/tests/data/sim_params.json @@ -1,8 +1,8 @@ { "raman_params": { "flag": true, - "space_resolution": 10e3, - "tolerance": 1e-8 + "result_spatial_resolution": 10e3, + "solver_spatial_resolution": 50 }, "nli_params": { "method": "ggn_spectrally_separated", diff --git a/tests/data/test_raman_fiber_expected_results.csv b/tests/data/test_raman_fiber_expected_results.csv index 6171b269..832a890b 100644 --- a/tests/data/test_raman_fiber_expected_results.csv +++ b/tests/data/test_raman_fiber_expected_results.csv @@ -1,97 +1,97 @@ ,signal,ase,nli -0,0.0002869472910749756,3.829244288314411e-08,2.1570435023738975e-07 -1,0.0002844264441819097,3.810807396068084e-08,2.1799950841473497e-07 -2,0.00028192866252406385,3.792544000755193e-08,2.2023841125047751e-07 -3,0.0002794537215642667,3.7744517714620316e-08,2.2242189941355056e-07 -4,0.00027562432957345563,3.739256592350871e-08,2.2343448272115905e-07 -5,0.0002718482755003939,3.7044482870002475e-08,2.2437826192962336e-07 -6,0.00026812479793132313,3.670020704375223e-08,2.2525495466693408e-07 -7,0.000264450700138397,3.635954085714981e-08,2.2606415187873477e-07 -8,0.0002608253488030976,3.602242835595967e-08,2.2680748521505387e-07 -9,0.0002569046888856947,3.564392097524325e-08,2.2718285844823122e-07 -10,0.0002530414048172964,3.52696660940159e-08,2.2749429758474536e-07 -11,0.0002492279873569917,3.489974200864255e-08,2.277374766527899e-07 -12,0.00024546394589921574,3.453407358954537e-08,2.2791414400785136e-07 -13,0.00024174879169001578,3.4172586853993816e-08,2.280260208417818e-07 -14,0.00023798746912554602,3.3802283179520985e-08,2.2798420759778034e-07 -15,0.00023427697848580554,3.343627022987542e-08,2.2788101592695744e-07 -16,0.0002306167836320285,3.307447309241581e-08,2.2771816297650914e-07 -17,0.00022700656967539738,3.2716831574363364e-08,2.274975560288182e-07 -18,0.00022344579480967338,3.236327278261661e-08,2.2361822442592406e-07 -19,0.00021953361935365365,3.195819964288877e-08,2.1939761734541424e-07 -20,0.000215683131390894,3.155821693631402e-08,2.152494588710531e-07 -21,0.0002118936126056039,3.116322947665684e-08,2.1117277567387026e-07 -22,0.00020816423698459974,3.0773146233359933e-08,2.0716649124095414e-07 -23,0.000204494186708796,3.0387877710694614e-08,2.0322954179937734e-07 -24,0.0002011608152067422,3.0044038268833097e-08,1.9963693210325328e-07 -25,0.0001978756946189507,2.9704204306604607e-08,1.9610141536963302e-07 -26,0.00019463824873067792,2.9368307297032184e-08,1.9262221997374404e-07 -27,0.00019144860669288407,2.903632861769827e-08,1.8919927457566036e-07 -28,0.00018830616497929743,2.870820070744311e-08,1.8583178406705711e-07 -29,0.0001852103256336822,2.838385708911634e-08,1.8251896218718027e-07 -30,0.0001821604972098109,2.8063232252848876e-08,1.7926003240910756e-07 -31,0.00017915618670059162,2.774625963676283e-08,1.76054318231953e-07 -32,0.00017619680881745593,2.7432875871797347e-08,1.729010553429381e-07 -33,0.0001732817839023698,2.712301856538676e-08,1.6979948820365403e-07 -34,0.0001704966413678542,2.6828122477482957e-08,1.6683312331765736e-07 -35,0.00016775189226190024,2.6536528664560742e-08,1.639139770351803e-07 -36,0.00016504703499518105,2.624818226917535e-08,1.6104139135569604e-07 -37,0.00016238266779776653,2.5963117448579666e-08,1.5795381794641793e-07 -38,0.0001597582427278871,2.568127942199337e-08,1.5492098715709327e-07 -39,0.0001571732182027887,2.5402614261982925e-08,1.5194201541883415e-07 -40,0.00015462705891567335,2.5127068868391087e-08,1.4901603171959048e-07 -41,0.00015212101646395513,2.4854550603641668e-08,1.4614388817380648e-07 -42,0.00014965447757985992,2.4585009902449718e-08,1.4332463586635585e-07 -43,0.0001472268380950584,2.4318397887399997e-08,1.4055734193945962e-07 -44,0.0001447164668892332,2.4034551917480693e-08,1.377259000826997e-07 -45,0.00014224784112376056,2.3753930444781328e-08,1.3494914625940223e-07 -46,0.000139820283675003,2.3476479506890216e-08,1.3222606385781202e-07 -47,0.00013743418748444287,2.3202247900619965e-08,1.295566531341862e-07 -48,0.00013508884015386686,2.2931181973013504e-08,1.2693987096025158e-07 -49,0.00013278354172498307,2.2663228905058608e-08,1.2437469442130953e-07 -50,0.00013051760419724657,2.2398336706395863e-08,1.2186012017917007e-07 -51,0.00012829168984638487,2.2136423459712534e-08,1.1939640981689728e-07 -52,0.00012610506317956756,2.1877440279108582e-08,1.1698252030563078e-07 -53,0.00012395700285919374,2.1621338937233993e-08,1.1461743054419825e-07 -54,0.00012180241033650921,2.136015630373758e-08,1.1225922783040025e-07 -55,0.0001196865090578088,2.11019103466444e-08,1.0994951537260489e-07 -56,0.00011760857776205185,2.0846552296319304e-08,1.0757395097863843e-07 -57,0.00011556891128259512,2.0594154864038522e-08,1.0524972555992818e-07 -58,0.00011356676177304645,2.0344670536408355e-08,1.0297570549834491e-07 -59,0.00011160139690545148,2.009805268169949e-08,1.007507830554809e-07 -60,0.00010967209909252316,1.9854255584746143e-08,9.857387536569294e-08 -61,0.00010777915187088834,1.961321154131787e-08,9.644480679617587e-08 -62,0.00010592181397175025,1.9374877782865603e-08,9.43624842461164e-08 -63,0.00010409936038609485,1.913921236065976e-08,9.232584080120623e-08 -64,0.00010246447558376296,1.8936229484424864e-08,9.046927135292076e-08 -65,0.00010085803630103994,1.873544193319646e-08,8.865067925960422e-08 -66,9.927950010555374e-05,1.8536821682157304e-08,8.686925127148483e-08 -67,9.772837346090753e-05,1.834034757300294e-08,8.512422533827403e-08 -68,9.62041343011343e-05,1.8145993316507615e-08,8.341482250640209e-08 -69,9.470627135912848e-05,1.7953733512786736e-08,8.174028142913557e-08 -70,9.32342835979764e-05,1.776354374489084e-08,8.009985766376519e-08 -71,9.178813743816069e-05,1.757538990695628e-08,7.849321446941075e-08 -72,9.036733009485282e-05,1.7389250225057777e-08,7.691961625609573e-08 -73,8.897136946428169e-05,1.7205104136353174e-08,7.537834446343352e-08 -74,8.760740745801088e-05,1.7025340034280735e-08,7.38751341742058e-08 -75,8.626710469266231e-05,1.6847609082084475e-08,7.274492099364066e-08 -76,8.495000573672366e-05,1.6671897815367364e-08,7.16342744751107e-08 -77,8.365569697520734e-05,1.6498202874185357e-08,7.054284583689086e-08 -78,8.238374036673638e-05,1.6326516066391613e-08,6.94702656996508e-08 -79,8.11337070649851e-05,1.615683240442047e-08,6.84161724378069e-08 -80,7.990517700271111e-05,1.5989150837085435e-08,6.738021182875641e-08 -81,7.869784230919362e-05,1.5823472723367315e-08,6.63621242598539e-08 -82,7.751129541079501e-05,1.5659808141896922e-08,6.536156604375558e-08 -83,7.634513730458697e-05,1.5498175122781168e-08,6.437820072038669e-08 -84,7.530262080974513e-05,1.5364277079429572e-08,6.349909645089698e-08 -85,7.427675504203511e-05,1.523236493234819e-08,6.263403294276124e-08 -86,7.326723873728716e-05,1.510251249079146e-08,6.178275615432246e-08 -87,7.227232864620995e-05,1.4974078108462424e-08,6.094379608687809e-08 -88,7.1291797553153e-05,1.4847055996011248e-08,6.011696114034367e-08 -89,7.032542203609039e-05,1.4721440784517874e-08,5.930206291361685e-08 -90,6.937298231673965e-05,1.4597227547292096e-08,5.849891607818969e-08 -91,6.843339696762385e-05,1.447443282270653e-08,5.7706608718023645e-08 -92,6.750649045006057e-05,1.4353051811356354e-08,5.6924992809748396e-08 -93,6.65920896785063e-05,1.4233080214004659e-08,5.615392239860827e-08 -94,6.554258932109667e-05,1.407504972937325e-08,5.5268928972034444e-08 -95,6.450957734109368e-05,1.3918655180382722e-08,5.439783940506079e-08 +0,0.0002866683470642085,3.455694800734997e-08,2.1767706055953313e-07 +1,0.0002842930902246378,3.4445260342151434e-08,2.20064716108892e-07 +2,0.00028193841273409963,3.4334217950641774e-08,2.2239856929822977e-07 +3,0.0002796041237984927,3.422381587730477e-08,2.2467937700272344e-07 +4,0.00027589218358262,3.3956266402003705e-08,2.2576401766047814e-07 +5,0.0002722303444814487,3.3690926476196685e-08,2.2678094635121413e-07 +6,0.00026861791294303266,3.342777134334201e-08,2.2773179097640802e-07 +7,0.00026505174756069215,3.316675429137828e-08,2.2861602718115889e-07 +8,0.0002615312775878486,3.290785186664305e-08,2.294352005376256e-07 +9,0.0002577007690018081,3.26092154155734e-08,2.2987401053105823e-07 +10,0.00025392474812815994,3.231329178154223e-08,2.3024928325076607e-07 +11,0.0002501957390130402,3.201993265117851e-08,2.3055653947072044e-07 +12,0.0002465133077961936,3.172911082648897e-08,2.3079745224174315e-07 +13,0.00024287702172285261,3.144079924487205e-08,2.309736700807475e-07 +14,0.00023918644496802598,3.1142660565561954e-08,2.3099023972100006e-07 +15,0.00023554415781363666,3.084719002003063e-08,2.3094533745656626e-07 +16,0.0002319496781605366,3.0554358283826426e-08,2.3084061926161906e-07 +17,0.000228402746264896,3.026413819712743e-08,2.306779372506828e-07 +18,0.00022490287297566154,2.997650061885732e-08,2.2679314039447925e-07 +19,0.0002210339853226993,2.9639081336421986e-08,2.225476971173533e-07 +20,0.00021722472675673681,2.9305156366940595e-08,2.1837424228396343e-07 +21,0.00021347443350916938,2.8974683300060073e-08,2.1427183120915025e-07 +22,0.00020978233224910872,2.864761802749998e-08,2.1023941353936252e-07 +23,0.0002061476568412488,2.832391679540816e-08,2.0627595093585302e-07 +24,0.0002028237056285935,2.8034565895618217e-08,2.0263423697267893e-07 +25,0.00019954715529254185,2.7748013284124615e-08,1.9905015325521452e-07 +26,0.0001963174528000437,2.7464226779716075e-08,1.9552292765090665e-07 +27,0.00019313475803109547,2.718318103861899e-08,1.920525003885138e-07 +28,0.00018999848980183525,2.6904843987797665e-08,1.8863807494364378e-07 +29,0.00018690807208013476,2.6629183784477213e-08,1.852788635171717e-07 +30,0.00018386293497138034,2.635616888672288e-08,1.8197408797080072e-07 +31,0.0001808626075954048,2.608576967648626e-08,1.7872307155753016e-07 +32,0.00017790652540681915,2.5817954962418864e-08,1.7552504805064169e-07 +33,0.00017499412908771533,2.5552693765056307e-08,1.723792598876346e-07 +34,0.0001721914205512116,2.529821177538242e-08,1.69350416018518e-07 +35,0.00016942913344260413,2.5046172734840803e-08,1.663699885487624e-07 +36,0.0001667067703020692,2.4796549229968703e-08,1.6343730102750106e-07 +37,0.00016402494737034808,2.4549324625938814e-08,1.602954566731582e-07 +38,0.0001613831201060569,2.430447139995257e-08,1.5720933628441338e-07 +39,0.00015878075021906192,2.406196225171638e-08,1.541780421866172e-07 +40,0.00015621730558753943,2.3821770097360405e-08,1.5120068940449393e-07 +41,0.000153694061439545,2.3583901792213434e-08,1.4827814327673852e-07 +42,0.00015121040694331307,2.3348329147104765e-08,1.4540943949658482e-07 +43,0.00014876574026315321,2.3115024224465226e-08,1.425936300160931e-07 +44,0.00014623043935025647,2.2864250993313975e-08,1.397065102093322e-07 +45,0.00014373723010448477,2.2616046400344574e-08,1.3687531950007013e-07 +46,0.0001412854316913198,2.2370377299191064e-08,1.3409901704421416e-07 +47,0.00013887544742801196,2.2127221340618422e-08,1.313775961274423e-07 +48,0.0001365065605420479,2.1886545482453933e-08,1.2870998887773554e-07 +49,0.00013417806673897108,2.164831702445933e-08,1.2609514810979993e-07 +50,0.00013188927370907155,2.1412503578881085e-08,1.2353204666803017e-07 +51,0.00012964085531237725,2.117909919147538e-08,1.2102094138348867e-07 +52,0.00012743207116500861,2.094807084934547e-08,1.1856076487337955e-07 +53,0.0001252621950917354,2.0719385892834214e-08,1.161504721855161e-07 +54,0.00012308423338164536,2.0485363383535514e-08,1.1374627006340893e-07 +55,0.00012094535834842106,2.0253755300794944e-08,1.1139168040879032e-07 +56,0.00011884484242431182,2.0024527468430072e-08,1.0897675288127846e-07 +57,0.00011678298769107047,1.9797656169961167e-08,1.0661409941810817e-07 +58,0.0001147590394591346,1.9573107382367898e-08,1.0430256532408603e-07 +59,0.00011277225867179729,1.935084744632288e-08,1.0204102225018314e-07 +60,0.00011082192110664448,1.913084301808743e-08,9.982836715827351e-08 +61,0.00010890831555726861,1.8913080844667903e-08,9.76644171489201e-08 +62,0.00010703069380321927,1.8697527263494167e-08,9.554805889434612e-08 +63,0.00010518832400867466,1.8484148971365717e-08,9.347820572370307e-08 +64,0.00010353027948847247,1.8300360477604286e-08,9.158630538270034e-08 +65,0.00010190114820620951,1.8118339508838893e-08,8.97332684173061e-08 +66,0.00010030037817345079,1.7938063167097722e-08,8.791826040657302e-08 +67,9.872746699919663e-05,1.775950920143011e-08,8.614049912759125e-08 +68,9.718188342878233e-05,1.7582655209782296e-08,8.439918541238176e-08 +69,9.566310719955732e-05,1.740747903977101e-08,8.269353818341506e-08 +70,9.417062845393912e-05,1.7233958752991638e-08,8.102279372648069e-08 +71,9.27044102709892e-05,1.7062082036004708e-08,7.938660156523957e-08 +72,9.12639411274833e-05,1.6891826998956218e-08,7.778420731697601e-08 +73,8.984872036936478e-05,1.6723171993235663e-08,7.621487408851861e-08 +74,8.845926525396718e-05,1.6557077218868872e-08,7.467873241780861e-08 +75,8.709405706837696e-05,1.6392620086127443e-08,7.352620174175576e-08 +76,8.575262707251024e-05,1.6229781230847938e-08,7.239374499535451e-08 +77,8.44345490553445e-05,1.6068541919248203e-08,7.128100236441453e-08 +78,8.313937224726923e-05,1.5908883354530017e-08,7.018759330199112e-08 +79,8.18666553697718e-05,1.5750787028460176e-08,6.91131452736768e-08 +80,8.061596620368467e-05,1.5594234699428168e-08,6.80572933931087e-08 +81,7.93869860927298e-05,1.54392105845737e-08,6.701976864553917e-08 +82,7.81792957880057e-05,1.528569691328632e-08,6.600021709431229e-08 +83,7.69924848842345e-05,1.5133676199095668e-08,6.499829226870119e-08 +84,7.592423495462984e-05,1.5006007729453082e-08,6.40964585216171e-08 +85,7.487323130273564e-05,1.4879695488874347e-08,6.320918435915818e-08 +86,7.383915942693816e-05,1.475473179887786e-08,6.233620427401105e-08 +87,7.282024738915393e-05,1.4631093950979863e-08,6.147602236758762e-08 +88,7.181625694043332e-05,1.4508775744557438e-08,6.062843750629826e-08 +89,7.082695384072487e-05,1.4387771381868581e-08,5.979325194092954e-08 +90,6.985210770121701e-05,1.4268075471622408e-08,5.8970271173546604e-08 +91,6.889061932028676e-05,1.414966436041678e-08,5.8158567240485706e-08 +92,6.79423037538828e-05,1.4032534237451406e-08,5.7357984009008465e-08 +93,6.700697867481953e-05,1.3916681725635683e-08,5.656836755557654e-08 +94,6.594003301527265e-05,1.3765768410137306e-08,5.5667634894222326e-08 +95,6.489000516837228e-05,1.3616343300622314e-08,5.4781184522010985e-08 diff --git a/tests/data/test_science_utils_fiber_config.json b/tests/data/test_science_utils_fiber_config.json index 28656998..49ad249c 100644 --- a/tests/data/test_science_utils_fiber_config.json +++ b/tests/data/test_science_utils_fiber_config.json @@ -13,20 +13,20 @@ "pmd_coef": 1.265e-15 }, "operational": { - "temperature": 283, - "raman_pumps": [ - { - "power": 0.2, - "frequency": 205000000000000, - "propagation_direction": "counterprop" + "temperature": 283, + "raman_pumps": [ + { + "power": 224.403e-3, + "frequency": 205e12, + "propagation_direction": "counterprop" + }, + { + "power": 231.135e-3, + "frequency": 201e12, + "propagation_direction": "counterprop" + } + ] }, - { - "power": 0.206, - "frequency": 201000000000000, - "propagation_direction": "counterprop" - } - ] - }, "metadata": { "location": { "latitude": 1, diff --git a/tests/invocation/transmission_main_example__raman b/tests/invocation/transmission_main_example__raman index 9c04651d..fa7f9ca0 100644 --- a/tests/invocation/transmission_main_example__raman +++ b/tests/invocation/transmission_main_example__raman @@ -21,109 +21,111 @@ RamanFiber Span1 total loss (dB): 17.00 (includes conn loss (dB) in: 0.50 out: 0.50) (conn loss out includes EOL margin defined in eqpt_config.json) - pch out (dBm): -7.74 + pch out (dBm): -7.71 +Fused Fused1 + loss (dB): 0.00 Edfa Edfa1 type_variety: std_low_gain - effective gain(dB): 5.74 + effective gain(dB): 5.71 (before att_in and before output VOA) - noise figure (dB): 13.26 + noise figure (dB): 13.29 (including att_in) - pad att_in (dB): 2.26 - Power In (dBm): 11.07 + pad att_in (dB): 2.29 + Power In (dBm): 11.11 Power Out (dBm): 16.82 Delta_P (dB): -2.00 target pch (dBm): -2.00 effective pch (dBm): -2.00 output VOA (dB): 0.00 Transceiver Site_B - GSNR (0.1nm, dB): 31.43 - GSNR (signal bw, dB): 27.35 - OSNR ASE (0.1nm, dB): 34.18 - OSNR ASE (signal bw, dB): 30.10 + GSNR (0.1nm, dB): 31.44 + GSNR (signal bw, dB): 27.36 + OSNR ASE (0.1nm, dB): 34.22 + OSNR ASE (signal bw, dB): 30.14 CD (ps/nm): 1336.00 PMD (ps): 0.36 Transmission result for input power = 0.00 dBm: - Final GSNR (0.1 nm): 31.43 dB + Final GSNR (0.1 nm): 31.44 dB The GSNR per channel at the end of the line is: Ch. # Channel frequency (THz) Channel power (dBm) OSNR ASE (signal bw, dB) SNR NLI (signal bw, dB) GSNR (signal bw, dB) - 1 191.35 0.21 31.56 31.47 28.50 - 2 191.40 0.17 31.54 31.38 28.45 - 3 191.45 0.14 31.52 31.30 28.40 - 4 191.50 0.10 31.50 31.22 28.34 - 5 191.55 0.04 31.47 31.14 28.29 - 6 191.60 -0.02 31.44 31.06 28.23 - 7 191.65 -0.08 31.41 30.98 28.18 - 8 191.70 -0.14 31.37 30.90 28.12 - 9 191.75 -0.20 31.34 30.83 28.07 - 10 191.80 -0.26 31.31 30.75 28.01 - 11 191.85 -0.33 31.27 30.68 27.96 - 12 191.90 -0.39 31.24 30.61 27.90 - 13 191.95 -0.46 31.20 30.54 27.85 - 14 192.00 -0.52 31.17 30.47 27.79 - 15 192.05 -0.59 31.13 30.40 27.74 - 16 192.10 -0.66 31.10 30.33 27.69 - 17 192.15 -0.72 31.06 30.27 27.63 - 18 192.20 -0.79 31.02 30.20 27.58 - 19 192.25 -0.86 30.98 30.21 27.57 - 20 192.30 -0.94 30.94 30.21 27.55 - 21 192.35 -1.01 30.90 30.22 27.54 - 22 192.40 -1.09 30.86 30.23 27.52 - 23 192.45 -1.16 30.81 30.23 27.50 - 24 192.50 -1.24 30.77 30.24 27.49 - 25 192.55 -1.31 30.73 30.25 27.47 - 26 192.60 -1.38 30.69 30.26 27.46 - 27 192.65 -1.45 30.65 30.26 27.44 - 28 192.70 -1.52 30.61 30.27 27.42 - 29 192.75 -1.59 30.56 30.28 27.41 - 30 192.80 -1.66 30.52 30.28 27.39 - 31 192.85 -1.73 30.48 30.29 27.37 - 32 192.90 -1.80 30.44 30.30 27.36 - 33 192.95 -1.87 30.39 30.31 27.34 - 34 193.00 -1.94 30.35 30.31 27.32 - 35 193.05 -2.01 30.31 30.32 27.30 - 36 193.10 -2.08 30.27 30.33 27.29 - 37 193.15 -2.15 30.22 30.33 27.27 - 38 193.20 -2.22 30.18 30.35 27.26 - 39 193.25 -2.29 30.14 30.37 27.24 - 40 193.30 -2.36 30.09 30.39 27.23 - 41 193.35 -2.43 30.05 30.40 27.21 - 42 193.40 -2.49 30.01 30.42 27.20 - 43 193.45 -2.56 29.96 30.44 27.18 - 44 193.50 -2.63 29.92 30.46 27.17 - 45 193.55 -2.70 29.87 30.47 27.15 - 46 193.60 -2.78 29.83 30.49 27.14 - 47 193.65 -2.85 29.78 30.51 27.12 - 48 193.70 -2.92 29.73 30.53 27.10 - 49 193.75 -2.99 29.68 30.55 27.08 - 50 193.80 -3.06 29.64 30.56 27.06 - 51 193.85 -3.14 29.59 30.58 27.05 - 52 193.90 -3.21 29.54 30.60 27.03 - 53 193.95 -3.28 29.49 30.62 27.01 - 54 194.00 -3.35 29.44 30.64 26.99 - 55 194.05 -3.42 29.39 30.66 26.97 - 56 194.10 -3.50 29.34 30.67 26.95 - 57 194.15 -3.57 29.29 30.73 26.94 - 58 194.20 -3.64 29.24 30.79 26.94 - 59 194.25 -3.72 29.19 30.85 26.93 - 60 194.30 -3.79 29.14 30.91 26.93 - 61 194.35 -3.86 29.09 30.97 26.92 - 62 194.40 -3.93 29.04 31.03 26.91 - 63 194.45 -4.01 28.99 31.09 26.90 - 64 194.50 -4.08 28.94 31.15 26.90 - 65 194.55 -4.14 28.89 31.22 26.89 - 66 194.60 -4.21 28.85 31.28 26.89 - 67 194.65 -4.28 28.80 31.35 26.88 - 68 194.70 -4.34 28.75 31.42 26.87 - 69 194.75 -4.41 28.70 31.48 26.86 - 70 194.80 -4.47 28.66 31.55 26.86 - 71 194.85 -4.54 28.61 31.62 26.85 - 72 194.90 -4.60 28.56 31.69 26.84 - 73 194.95 -4.67 28.51 31.77 26.83 - 74 195.00 -4.73 28.47 31.84 26.82 - 75 195.05 -4.80 28.42 31.92 26.81 - 76 195.10 -4.86 28.37 31.92 26.78 + 1 191.35 0.18 31.61 31.43 28.51 + 2 191.40 0.14 31.59 31.34 28.45 + 3 191.45 0.11 31.57 31.26 28.40 + 4 191.50 0.07 31.55 31.17 28.35 + 5 191.55 0.02 31.52 31.09 28.29 + 6 191.60 -0.04 31.49 31.02 28.24 + 7 191.65 -0.10 31.46 30.94 28.18 + 8 191.70 -0.16 31.43 30.86 28.13 + 9 191.75 -0.21 31.40 30.79 28.07 + 10 191.80 -0.28 31.36 30.71 28.02 + 11 191.85 -0.34 31.33 30.64 27.96 + 12 191.90 -0.40 31.29 30.57 27.91 + 13 191.95 -0.47 31.26 30.50 27.85 + 14 192.00 -0.53 31.22 30.43 27.80 + 15 192.05 -0.60 31.18 30.36 27.74 + 16 192.10 -0.66 31.15 30.30 27.69 + 17 192.15 -0.73 31.11 30.23 27.64 + 18 192.20 -0.79 31.07 30.16 27.58 + 19 192.25 -0.86 31.04 30.17 27.57 + 20 192.30 -0.94 30.99 30.18 27.56 + 21 192.35 -1.01 30.95 30.19 27.54 + 22 192.40 -1.08 30.91 30.20 27.53 + 23 192.45 -1.16 30.86 30.20 27.51 + 24 192.50 -1.23 30.82 30.21 27.49 + 25 192.55 -1.30 30.78 30.22 27.48 + 26 192.60 -1.37 30.74 30.23 27.46 + 27 192.65 -1.44 30.70 30.23 27.45 + 28 192.70 -1.51 30.65 30.24 27.43 + 29 192.75 -1.58 30.61 30.25 27.42 + 30 192.80 -1.65 30.57 30.26 27.40 + 31 192.85 -1.72 30.53 30.27 27.38 + 32 192.90 -1.79 30.48 30.27 27.37 + 33 192.95 -1.86 30.44 30.28 27.35 + 34 193.00 -1.93 30.40 30.29 27.33 + 35 193.05 -2.00 30.35 30.30 27.32 + 36 193.10 -2.07 30.31 30.30 27.30 + 37 193.15 -2.14 30.27 30.31 27.28 + 38 193.20 -2.20 30.22 30.33 27.27 + 39 193.25 -2.27 30.18 30.35 27.25 + 40 193.30 -2.34 30.14 30.37 27.24 + 41 193.35 -2.41 30.09 30.38 27.23 + 42 193.40 -2.48 30.05 30.40 27.21 + 43 193.45 -2.55 30.00 30.42 27.20 + 44 193.50 -2.61 29.96 30.44 27.18 + 45 193.55 -2.69 29.91 30.46 27.16 + 46 193.60 -2.76 29.86 30.47 27.15 + 47 193.65 -2.83 29.82 30.49 27.13 + 48 193.70 -2.90 29.77 30.51 27.11 + 49 193.75 -2.98 29.72 30.53 27.10 + 50 193.80 -3.05 29.67 30.55 27.08 + 51 193.85 -3.12 29.62 30.57 27.06 + 52 193.90 -3.19 29.58 30.58 27.04 + 53 193.95 -3.26 29.53 30.60 27.02 + 54 194.00 -3.33 29.48 30.62 27.00 + 55 194.05 -3.41 29.43 30.64 26.98 + 56 194.10 -3.48 29.38 30.66 26.96 + 57 194.15 -3.55 29.33 30.72 26.96 + 58 194.20 -3.63 29.28 30.78 26.95 + 59 194.25 -3.70 29.23 30.83 26.95 + 60 194.30 -3.77 29.18 30.89 26.94 + 61 194.35 -3.85 29.12 30.96 26.93 + 62 194.40 -3.92 29.07 31.02 26.93 + 63 194.45 -3.99 29.02 31.08 26.92 + 64 194.50 -4.06 28.97 31.14 26.91 + 65 194.55 -4.13 28.92 31.21 26.91 + 66 194.60 -4.19 28.88 31.27 26.90 + 67 194.65 -4.26 28.83 31.34 26.89 + 68 194.70 -4.33 28.78 31.41 26.89 + 69 194.75 -4.39 28.73 31.47 26.88 + 70 194.80 -4.46 28.68 31.54 26.87 + 71 194.85 -4.52 28.64 31.61 26.86 + 72 194.90 -4.59 28.59 31.68 26.86 + 73 194.95 -4.65 28.54 31.76 26.85 + 74 195.00 -4.72 28.49 31.83 26.84 + 75 195.05 -4.78 28.44 31.91 26.83 + 76 195.10 -4.85 28.39 31.91 26.79 (No source node specified: picked Site_A)