Only propagates carriers that belong to Amp bandwidth

The commit introduces mux/demux functions in amps and ensures that the
propagation is only done on carriers that are in the Amp bandwitdh, ie
with all their spectrum including slot width is in bandwidth.

For consistency, default amp f_min is changed:
Objective is to use amplifiers' band to bound the possible frequencies
to be propagated. Since the current default f_min of Amp in json_io.py is
higher than the SI one, this would result in a different nb of channels
than currently used in tests, and a change in all tests. In order to
avoid this, I preferred to change this value and have consistency
between SI f_min and Amp f_min.

The commits adds a set of functions to make amps band the useable
spectrum on each OMS. Thee OMS generation is changed to use the amp band.

The commit adds filtering functions (demux and mux) to filter out spectrum
which is not in the amplifier band.

Spectrum assignment is also corrected to correctly match the amp bandwidth
constraint with guardband: center frequency index must be within the
usable part of the amp band. This changes a bit the notion of freq_index
and guardband in the functions, but this is transparent to user:
f_min, f_max represent the amp band, while self.freq_index_min/max
represent the center frequency boundary for a reference 50GHz channel.

Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com>
Change-Id: I225b2b2dc0e1f1992c0460f6e08fa9c9bc641edf
This commit is contained in:
EstherLerouzic
2022-01-06 15:07:20 +01:00
committed by Esther Le Rouzic
parent 5277ae2005
commit ac8fd770ab
13 changed files with 286 additions and 73 deletions

View File

@@ -34,7 +34,7 @@ from gnpy.core.utils import lin2db, db2lin, arrange_frequencies, snr_sum, per_la
from gnpy.core.parameters import RoadmParams, FusedParams, FiberParams, PumpParams, EdfaParams, EdfaOperational, \ from gnpy.core.parameters import RoadmParams, FusedParams, FiberParams, PumpParams, EdfaParams, EdfaOperational, \
RoadmPath, RoadmImpairment RoadmPath, RoadmImpairment
from gnpy.core.science_utils import NliSolver, RamanSolver from gnpy.core.science_utils import NliSolver, RamanSolver
from gnpy.core.info import SpectralInformation from gnpy.core.info import SpectralInformation, demuxed_spectral_information
from gnpy.core.exceptions import NetworkTopologyError, SpectrumError, ParametersError from gnpy.core.exceptions import NetworkTopologyError, SpectrumError, ParametersError
@@ -1204,5 +1204,10 @@ class Edfa(_Node):
self.propagated_labels = spectral_info.label self.propagated_labels = spectral_info.label
def __call__(self, spectral_info): def __call__(self, spectral_info):
self.propagate(spectral_info) # filter out carriers outside the amplifier band
return spectral_info band = next(b for b in self.params.bands)
spectral_info = demuxed_spectral_information(spectral_info, band)
if spectral_info.carriers:
self.propagate(spectral_info)
return spectral_info
raise ValueError(f'Amp {self.uid} Defined propagation band does not match amplifiers band.')

View File

@@ -11,7 +11,7 @@ This module contains classes for modelling :class:`SpectralInformation`.
from __future__ import annotations from __future__ import annotations
from collections import namedtuple from collections import namedtuple
from collections.abc import Iterable from collections.abc import Iterable
from typing import Union from typing import Union, List
from dataclasses import dataclass from dataclasses import dataclass
from numpy import argsort, mean, array, append, ones, ceil, any, zeros, outer, full, ndarray, asarray from numpy import argsort, mean, array, append, ones, ceil, any, zeros, outer, full, ndarray, asarray
@@ -317,6 +317,56 @@ def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, spacing
tx_osnr=tx_osnr, tx_power=tx_power, label=label) tx_osnr=tx_osnr, tx_power=tx_power, label=label)
def is_in_band(frequency: float, band: dict) -> bool:
"""band has {"f_min": value, "f_max": value} format
"""
if frequency >= band['f_min'] and frequency <= band['f_max']:
return True
return False
def demuxed_spectral_information(input_si: SpectralInformation, band: dict) -> SpectralInformation:
"""extract a si based on band
"""
filtered_indices = [i for i, f in enumerate(input_si.frequency)
if is_in_band(f - input_si.slot_width[i] / 2, band)
and is_in_band(f + input_si.slot_width[i] / 2, band)]
if filtered_indices:
frequency = input_si.frequency[filtered_indices]
baud_rate = input_si.baud_rate[filtered_indices]
slot_width = input_si.slot_width[filtered_indices]
signal = input_si.signal[filtered_indices]
nli = input_si.nli[filtered_indices]
ase = input_si.ase[filtered_indices]
roll_off = input_si.roll_off[filtered_indices]
chromatic_dispersion = input_si.chromatic_dispersion[filtered_indices]
pmd = input_si.pmd[filtered_indices]
pdl = input_si.pdl[filtered_indices]
latency = input_si.latency[filtered_indices]
delta_pdb_per_channel = input_si.delta_pdb_per_channel[filtered_indices]
tx_osnr = input_si.tx_osnr[filtered_indices]
tx_power = input_si.tx_power[filtered_indices]
label = input_si.label[filtered_indices]
return SpectralInformation(frequency=frequency, baud_rate=baud_rate, slot_width=slot_width, signal=signal,
nli=nli, ase=ase, roll_off=roll_off, chromatic_dispersion=chromatic_dispersion,
pmd=pmd, pdl=pdl, latency=latency, delta_pdb_per_channel=delta_pdb_per_channel,
tx_osnr=tx_osnr, tx_power=tx_power, label=label)
return None
def muxed_spectral_information(input_si_list: List[SpectralInformation]) -> SpectralInformation:
"""return the assembled spectrum
"""
if input_si_list and len(input_si_list) > 1:
si = input_si_list[0] + muxed_spectral_information(input_si_list[1:])
return si
elif input_si_list and len(input_si_list) == 1:
return input_si_list[0]
else:
raise ValueError('liste vide')
def carriers_to_spectral_information(initial_spectrum: dict[float, Carrier], def carriers_to_spectral_information(initial_spectrum: dict[float, Carrier],
power: float) -> SpectralInformation: power: float) -> SpectralInformation:
"""Initial spectrum is a dict with key = carrier frequency, and value a Carrier object. """Initial spectrum is a dict with key = carrier frequency, and value a Carrier object.

View File

@@ -463,10 +463,10 @@ class FiberParams(Parameters):
class EdfaParams: class EdfaParams:
default_values = { default_values = {
'f_min': 191.3e12, 'f_min': None,
'f_max': 196.1e12, 'f_max': None,
'multi_band': None, 'multi_band': None,
'bands': [], 'bands': None,
'type_variety': '', 'type_variety': '',
'type_def': '', 'type_def': '',
'gain_flatmax': None, 'gain_flatmax': None,
@@ -502,9 +502,11 @@ class EdfaParams:
# Bandwidth # Bandwidth
self.f_min = params['f_min'] self.f_min = params['f_min']
self.f_max = params['f_max'] self.f_max = params['f_max']
self.bandwidth = self.f_max - self.f_min self.bandwidth = self.f_max - self.f_min if self.f_max and self.f_min else None
self.f_cent = (self.f_max + self.f_min) / 2 self.f_cent = (self.f_max + self.f_min) / 2 if self.f_max and self.f_min else None
self.f_ripple_ref = params['f_ripple_ref'] self.f_ripple_ref = params['f_ripple_ref']
self.bands = [{'f_min': params['f_min'],
'f_max': params['f_max']}]
# Gain # Gain
self.gain_flatmax = params['gain_flatmax'] self.gain_flatmax = params['gain_flatmax']

View File

@@ -12,6 +12,7 @@ from csv import writer
from numpy import pi, cos, sqrt, log10, linspace, zeros, shape, where, logical_and, mean, array from numpy import pi, cos, sqrt, log10, linspace, zeros, shape, where, logical_and, mean, array
from scipy import constants from scipy import constants
from copy import deepcopy from copy import deepcopy
from typing import List
from gnpy.core.exceptions import ConfigurationError from gnpy.core.exceptions import ConfigurationError
@@ -458,3 +459,49 @@ def calculate_absolute_min_or_zero(x: array) -> array:
array([1., 0., 3.]) array([1., 0., 3.])
""" """
return (abs(x) - x) / 2 return (abs(x) - x) / 2
def find_common_range(amp_bands: List[List[dict]], default_band_f_min: float, default_band_f_max: float) \
-> List[dict]:
"""Find the common frequency range of bands
If there are no amplifiers in the path, then use default band
>>> amp_bands = [[{'f_min': 191e12, 'f_max' : 195e12}, {'f_min': 186e12, 'f_max' : 190e12} ], \
[{'f_min': 185e12, 'f_max' : 189e12}, {'f_min': 192e12, 'f_max' : 196e12}], \
[{'f_min': 186e12, 'f_max': 193e12}]]
>>> find_common_range(amp_bands, 190e12, 195e12)
[{'f_min': 186000000000000.0, 'f_max': 189000000000000.0}, {'f_min': 192000000000000.0, 'f_max': 193000000000000.0}]
>>> amp_bands = [[{'f_min': 191e12, 'f_max' : 195e12}, {'f_min': 186e12, 'f_max' : 190e12} ], \
[{'f_min': 185e12, 'f_max' : 189e12}, {'f_min': 192e12, 'f_max' : 196e12}], \
[{'f_min': 186e12, 'f_max': 192e12}]]
>>> find_common_range(amp_bands, 190e12, 195e12)
[{'f_min': 186000000000000.0, 'f_max': 189000000000000.0}]
"""
_amp_bands = [sorted(amp, key=lambda x: x['f_min']) for amp in amp_bands]
_temp = []
# remove None bands
for amp in _amp_bands:
is_band = True
for band in amp:
if not (is_band and band['f_min'] and band['f_max']):
is_band = False
if is_band:
_temp.append(amp)
# remove duplicate
unique_amp_bands = []
for amp in _temp:
if amp not in unique_amp_bands:
unique_amp_bands.append(amp)
if unique_amp_bands:
common_range = unique_amp_bands[0]
else:
if default_band_f_min is None or default_band_f_max is None:
return []
common_range = [{'f_min': default_band_f_min, 'f_max': default_band_f_max}]
for bands in unique_amp_bands:
common_range = [{'f_min': max(first['f_min'], second['f_min']), 'f_max': min(first['f_max'], second['f_max'])}
for first in common_range for second in bands
if max(first['f_min'], second['f_min']) < min(first['f_max'], second['f_max'])]
return sorted(common_range, key=lambda x: x['f_min'])

View File

@@ -5,8 +5,8 @@
"gain_ripple": [ "gain_ripple": [
0.0 0.0
], ],
"f_min": 191.35e12, "f_min": 191.275e12,
"f_max": 196.1e12, "f_max": 196.125e12,
"dgt": [ "dgt": [
1.0, 1.0,
1.017807767853702, 1.017807767853702,

View File

@@ -5,8 +5,8 @@
0.0359549, 0.0359549,
5.82851 5.82851
], ],
"f_min": 191.35e12, "f_min": 191.275e12,
"f_max": 196.1e12, "f_max": 196.125e12,
"nf_ripple": [ "nf_ripple": [
0.4372876328262819, 0.4372876328262819,
0.4372876328262819, 0.4372876328262819,

View File

@@ -192,7 +192,7 @@ class Amp(_JsonThing):
@classmethod @classmethod
def from_json(cls, filename, **kwargs): def from_json(cls, filename, **kwargs):
config = Path(filename).parent / 'default_edfa_config.json' config = Path(filename).parent / 'default_edfa_config.json'
# default_edfa_config.json assumes a DGT profile independantly from fmin/fmax, that's a generic profile
type_variety = kwargs['type_variety'] type_variety = kwargs['type_variety']
type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files type_def = kwargs.get('type_def', 'variable_gain') # default compatibility with older json eqpt files
nf_def = None nf_def = None
@@ -367,6 +367,31 @@ def _update_dual_stage(equipment):
return equipment return equipment
def _update_band(equipment: dict) -> dict:
"""Creates a list of bands for this amplifier, and remove other parameters which are not applicable
"""
amp_dict = equipment['Edfa']
for amplifier in amp_dict.values():
if amplifier.type_def != 'multi_band':
amplifier.bands = [{'f_min': amplifier.f_min,
'f_max': amplifier.f_max}]
# updates band parameter
else:
_bands = [{'f_min': amp_dict[a].f_min,
'f_max': amp_dict[a].f_max} for a in amp_dict[amplifier.type_variety].multi_band]
# remove duplicates
amplifier.bands = []
for b in _bands:
if b not in amplifier.bands:
amplifier.bands.append(b)
# remove non applicable parameters
for key in ['f_min', 'f_max', 'gain_flatmax', 'gain_min', 'p_max', 'nf_model', 'dual_stage_model',
'nf_fit_coeff', 'nf_ripple', 'dgt', 'gain_ripple']:
delattr(amplifier, key)
return equipment
def _roadm_restrictions_sanity_check(equipment): def _roadm_restrictions_sanity_check(equipment):
"""verifies that booster and preamp restrictions specified in roadm equipment are listed in the edfa.""" """verifies that booster and preamp restrictions specified in roadm equipment are listed in the edfa."""
for roadm_type, roadm_eqpt in equipment['Roadm'].items(): for roadm_type, roadm_eqpt in equipment['Roadm'].items():
@@ -428,6 +453,7 @@ def _equipment_from_json(json_data, filename):
raise EquipmentConfigError(f'Unrecognized network element type "{key}"') raise EquipmentConfigError(f'Unrecognized network element type "{key}"')
_check_fiber_vs_raman_fiber(equipment) _check_fiber_vs_raman_fiber(equipment)
equipment = _update_dual_stage(equipment) equipment = _update_dual_stage(equipment)
equipment = _update_band(equipment)
_roadm_restrictions_sanity_check(equipment) _roadm_restrictions_sanity_check(equipment)
return equipment return equipment

View File

@@ -16,14 +16,17 @@ See: draft-ietf-teas-yang-path-computation-01.txt
""" """
from collections import namedtuple, OrderedDict from collections import namedtuple, OrderedDict
from typing import List
from logging import getLogger from logging import getLogger
from networkx import (dijkstra_path, NetworkXNoPath, from networkx import (dijkstra_path, NetworkXNoPath,
all_simple_paths, shortest_simple_paths) all_simple_paths, shortest_simple_paths)
from networkx.utils import pairwise from networkx.utils import pairwise
from numpy import mean, argmin from numpy import mean, argmin
from gnpy.core.elements import Transceiver, Roadm
from gnpy.core.utils import lin2db from gnpy.core.elements import Transceiver, Roadm, Edfa
from gnpy.core.info import create_input_spectral_information, carriers_to_spectral_information from gnpy.core.utils import lin2db, find_common_range
from gnpy.core.info import create_input_spectral_information, carriers_to_spectral_information, \
demuxed_spectral_information, muxed_spectral_information, SpectralInformation
from gnpy.core import network as network_module from gnpy.core import network as network_module
from gnpy.core.exceptions import ServiceError, DisjunctionError from gnpy.core.exceptions import ServiceError, DisjunctionError
from copy import deepcopy from copy import deepcopy
@@ -332,14 +335,34 @@ def compute_constrained_path(network, req):
return total_path return total_path
def filter_si(path: list, equipment: dict, si: SpectralInformation) -> SpectralInformation:
"""Filter spectral information based on the amplifiers common range"""
# First retrieve f_min, f_max spectrum according to amplifiers' spectrum on the path
common_range = find_elements_common_range(path, equipment)
# filter out frequencies that should not be created
filtered_si = []
for band in common_range:
temp = demuxed_spectral_information(si, band)
if temp:
filtered_si.append(temp)
if not filtered_si:
raise ValueError('Defined propagation band does not match amplifiers band.')
return muxed_spectral_information(filtered_si)
def propagate(path, req, equipment): def propagate(path, req, equipment):
"""propagates signals in each element according to initial spectrum set by user""" """propagates signals in each element according to initial spectrum set by user
Spectrum is specified in request through f_min, f_max and spacing, or initial_spectrum
and amps frequency band on the path is used to filter out frequencies"""
# generates spectrum based on request
if req.initial_spectrum is not None: if req.initial_spectrum is not None:
si = carriers_to_spectral_information(initial_spectrum=req.initial_spectrum, power=req.power) si = carriers_to_spectral_information(initial_spectrum=req.initial_spectrum, power=req.power)
else: else:
si = create_input_spectral_information( si = create_input_spectral_information(
f_min=req.f_min, f_max=req.f_max, roll_off=req.roll_off, baud_rate=req.baud_rate, f_min=req.f_min, f_max=req.f_max, roll_off=req.roll_off, baud_rate=req.baud_rate,
spacing=req.spacing, tx_osnr=req.tx_osnr, tx_power=req.tx_power, delta_pdb=req.offset_db) spacing=req.spacing, tx_osnr=req.tx_osnr, tx_power=req.tx_power, delta_pdb=req.offset_db)
# filter out frequencies that should not be created
si = filter_si(path, equipment, si)
roadm_osnr = [] roadm_osnr = []
for i, el in enumerate(path): for i, el in enumerate(path):
if isinstance(el, Roadm): if isinstance(el, Roadm):
@@ -385,6 +408,7 @@ def propagate_and_optimize_mode(path, req, equipment):
baud_rate=this_br, spacing=req.spacing, baud_rate=this_br, spacing=req.spacing,
delta_pdb=this_offset, tx_osnr=req.tx_osnr, delta_pdb=this_offset, tx_osnr=req.tx_osnr,
tx_power=req.tx_power) tx_power=req.tx_power)
spc_info = filter_si(path, equipment, spc_info)
roadm_osnr = [] roadm_osnr = []
for i, el in enumerate(path): for i, el in enumerate(path):
if isinstance(el, Roadm): if isinstance(el, Roadm):
@@ -1225,3 +1249,11 @@ def _penalty_msg(total_path, msg, min_ind):
else: else:
msg += f'\n\t{pretty} penalty not evaluated' msg += f'\n\t{pretty} penalty not evaluated'
return msg return msg
def find_elements_common_range(el_list: list, equipment: dict) -> List[dict]:
"""Find the common frequency range of amps of a given list of elements (for example an OMS or a path)
If there are no amplifiers in the path, then use the SI
"""
amp_bands = [n.params.bands for n in el_list if isinstance(n, (Edfa))]
return find_common_range(amp_bands, equipment['SI']['default'].f_min, equipment['SI']['default'].f_max)

View File

@@ -15,28 +15,30 @@ element/oms correspondace
from collections import namedtuple from collections import namedtuple
from logging import getLogger from logging import getLogger
from gnpy.core.elements import Roadm, Transceiver from gnpy.core.elements import Roadm, Transceiver, Edfa
from gnpy.core.exceptions import ServiceError, SpectrumError from gnpy.core.exceptions import ServiceError, SpectrumError
from gnpy.core.utils import order_slots, restore_order from gnpy.core.utils import order_slots, restore_order
from gnpy.topology.request import compute_spectrum_slot_vs_bandwidth from gnpy.topology.request import compute_spectrum_slot_vs_bandwidth, find_elements_common_range
LOGGER = getLogger(__name__) LOGGER = getLogger(__name__)
GUARDBAND = 25e9
class Bitmap: class Bitmap:
"""records the spectrum occupation""" """records the spectrum occupation"""
def __init__(self, f_min, f_max, grid, guardband=0.15e12, bitmap=None): def __init__(self, f_min, f_max, grid, guardband=GUARDBAND, bitmap=None):
# n is the min index including guardband. Guardband is require to be sure # n is the min index including guardband. Guardband is required to be sure
# that a channel can be assigned with center frequency fmin (means that its # that a channel can be assigned with center frequency fmin (means that its
# slot occupation goes below freq_index_min # slot occupation goes below freq_index_min
n_min = frequency_to_n(f_min - guardband, grid) n_min = frequency_to_n(f_min, grid)
n_max = frequency_to_n(f_max + guardband, grid) - 1 n_max = frequency_to_n(f_max, grid)
self.n_min = n_min self.n_min = n_min
self.n_max = n_max self.n_max = n_max
self.freq_index_min = frequency_to_n(f_min) self.freq_index_min = frequency_to_n(f_min + guardband)
self.freq_index_max = frequency_to_n(f_max) self.freq_index_max = frequency_to_n(f_max - guardband)
self.freq_index = list(range(n_min, n_max + 1)) self.freq_index = list(range(n_min, n_max + 1))
self.guardband = guardband
if bitmap is None: if bitmap is None:
self.bitmap = [1] * (n_max - n_min + 1) self.bitmap = [1] * (n_max - n_min + 1)
elif len(bitmap) == len(self.freq_index): elif len(bitmap) == len(self.freq_index):
@@ -83,7 +85,6 @@ class OMS:
self.spectrum_bitmap = [] self.spectrum_bitmap = []
self.nb_channels = 0 self.nb_channels = 0
self.service_list = [] self.service_list = []
# TODO
def __str__(self): def __str__(self):
return '\n\t'.join([f'{type(self).__name__} {self.oms_id}', return '\n\t'.join([f'{type(self).__name__} {self.oms_id}',
@@ -98,7 +99,7 @@ class OMS:
self.el_id_list.append(elem.uid) self.el_id_list.append(elem.uid)
self.el_list.append(elem) self.el_list.append(elem)
def update_spectrum(self, f_min, f_max, guardband=0.15e12, existing_spectrum=None, grid=0.00625e12): def update_spectrum(self, f_min, f_max, guardband=GUARDBAND, existing_spectrum=None, grid=0.00625e12):
"""Frequencies expressed in Hz. """Frequencies expressed in Hz.
Add 150 GHz margin to enable a center channel on f_min Add 150 GHz margin to enable a center channel on f_min
Use ITU-T G694.1 Flexible DWDM grid definition Use ITU-T G694.1 Flexible DWDM grid definition
@@ -226,6 +227,40 @@ def align_grids(oms_list):
return oms_list return oms_list
def find_network_freq_range(network, equipment):
"""Find the lowest freq from amps and highest freq among all amps to determine the resulting bitmap
"""
amp_bands = [band for n in network.nodes() if isinstance(n, Edfa) for band in n.params.bands]
min_frequencies = [a['f_min'] for a in amp_bands]
max_frequencies = [a['f_max'] for a in amp_bands]
return min(min_frequencies), max(max_frequencies)
def create_oms_bitmap(oms, equipment, f_min, f_max, guardband, grid):
"""Find the highest low freq from oms amps and lowest high freq among oms amps to determine
the possible bitmap window.
f_min and f_max represent the useable spectrum (not the useable center frequencies)
ie n smaller than frequency_to_n(min_freq, grid) are not useable
"""
n_min = frequency_to_n(f_min, grid)
n_max = frequency_to_n(f_max, grid) - 1
common_range = find_elements_common_range(oms.el_list, equipment)
band0 = common_range[0]
band0_n_min = frequency_to_n(band0['f_min'], grid)
band0_n_max = frequency_to_n(band0['f_max'], grid)
bitmap = [0] * (band0_n_min - n_min) + [1] * (band0_n_max - band0_n_min + 1)
i = 1
while i < len(common_range):
band = common_range[i]
band_n_min = frequency_to_n(band['f_min'], grid)
band_n_max = frequency_to_n(band['f_max'], grid)
bitmap = bitmap + [0] * (band_n_min - band0_n_max - 1) + [1] * (band_n_max - band_n_min + 1)
band0_n_max = band_n_max
i += 1
bitmap = bitmap + [0] * (n_max - band0_n_max)
return bitmap
def build_oms_list(network, equipment): def build_oms_list(network, equipment):
"""initialization of OMS list in the network """initialization of OMS list in the network
@@ -237,7 +272,15 @@ def build_oms_list(network, equipment):
""" """
oms_id = 0 oms_id = 0
oms_list = [] oms_list = []
for node in [n for n in network.nodes() if isinstance(n, Roadm)]: # identify all vertices of OMS: of course ROADM, but aso links to external chassis transponders
oms_vertices = [n for n in network.nodes() if isinstance(n, Roadm)] +\
[n for n in network.nodes() if isinstance(n, Transceiver)
and not isinstance(next(network.successors(n)), Roadm)]
# determine the size of the bitmap common to all the omses: find min and max frequencies of all amps
# in the network. These gives the band not the center frequency. Thhen we use a reference channel
# slot width (50GHz) to set the f_min, f_max
f_min, f_max = find_network_freq_range(network, equipment)
for node in oms_vertices:
for edge in network.edges([node]): for edge in network.edges([node]):
if not isinstance(edge[1], Transceiver): if not isinstance(edge[1], Transceiver):
nd_in = edge[0] # nd_in is a Roadm nd_in = edge[0] # nd_in is a Roadm
@@ -271,8 +314,9 @@ def build_oms_list(network, equipment):
nd_out.oms_list = [] nd_out.oms_list = []
nd_out.oms_list.append(oms_id) nd_out.oms_list.append(oms_id)
oms.update_spectrum(equipment['SI']['default'].f_min, bitmap = create_oms_bitmap(oms, equipment, f_min=f_min, f_max=f_max, guardband=GUARDBAND,
equipment['SI']['default'].f_max, grid=0.00625e12) grid=0.00625e12)
oms.update_spectrum(f_min, f_max, guardband=GUARDBAND, grid=0.00625e12, existing_spectrum=bitmap)
# oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0) # oms.assign_spectrum(13,7) gives back (193137500000000.0, 193225000000000.0)
# as in the example in the standard # as in the example in the standard
# oms.assign_spectrum(13,7) # oms.assign_spectrum(13,7)
@@ -333,10 +377,11 @@ def aggregate_oms_bitmap(path_oms, oms_list):
'el_id_list': 0, 'el_id_list': 0,
'el_list': [] 'el_list': []
} }
freq_min = nvalue_to_frequency(spectrum.freq_index_min) freq_min = nvalue_to_frequency(spectrum.n_min)
freq_max = nvalue_to_frequency(spectrum.freq_index_max) freq_max = nvalue_to_frequency(spectrum.n_max)
aggregate_oms = OMS(**params) aggregate_oms = OMS(**params)
aggregate_oms.update_spectrum(freq_min, freq_max, grid=0.00625e12, existing_spectrum=bitmap) aggregate_oms.update_spectrum(freq_min, freq_max, grid=0.00625e12, guardband=spectrum.guardband,
existing_spectrum=bitmap)
return aggregate_oms return aggregate_oms

View File

@@ -1,6 +1,6 @@
{ {
"f_min": 191.35e12, "f_min": 191.275e12,
"f_max": 196.1e12, "f_max": 196.125e12,
"nf_ripple": [ "nf_ripple": [
0.0, 0.0,
0.0, 0.0,

View File

@@ -1,4 +1,7 @@
{ "nf_fit_coeff": [ {
"f_min": 191.275e12,
"f_max": 196.125e12,
"nf_fit_coeff": [
0.000168241, 0.000168241,
0.0469961, 0.0469961,
0.0359549, 0.0359549,

View File

@@ -251,35 +251,35 @@ def test_amp_behaviour(tilt_target, delta_p):
else: else:
if delta_p != 2: if delta_p != 2:
expected_sig_out = [ expected_sig_out = [
-32.00529182, -31.93540907, -31.86554231, -31.79417979, -31.71903263, -31.95025022, -31.88168886, -31.81178634, -31.73838831, -31.66318631,
-31.6424009, -31.56531159, -31.48775435, -31.41468382, -31.35973323, -31.58762141, -31.51156294, -31.43760161, -31.38124626, -31.34245197,
-31.32286555, -31.28602346, -31.2472908, -31.20086569, -31.14671746, -31.30629475, -31.26970711, -31.22566555, -31.17412914, -31.11806869,
-31.08702653, -31.01341963, -30.93430243, -30.87791656, -30.84413339, -31.05122228, -30.97358131, -30.90658619, -30.86616148, -30.83854197,
-30.81605918, -30.78824936, -30.76071036, -30.73319161, -30.70494101, -30.81115028, -30.78403337, -30.7570206, -30.73002834, -30.70088634,
-30.67368479, -30.63941012, -30.60178381, -30.55585766, -30.5066561, -30.66844432, -30.63427939, -30.59364514, -30.54659009, -30.49180643,
-30.43426575, -30.33848379, -30.24471112, -30.18220815, -30.15076699, -30.41406352, -30.31434813, -30.22984104, -30.18249387, -30.1516453,
-30.11934744, -30.08776718, -30.05548097, -30.02250068, -29.98954302, -30.12082034, -30.08970494, -30.05779424, -30.02543415, -29.99309889,
-29.95661362, -29.92370274, -29.8854762, -29.84193785, -29.79238328, -29.96078803, -29.92798594, -29.89002127, -29.84689015, -29.79726968,
-29.72452662, -29.6385071, -29.54788144, -29.44581202, -29.33924103, -29.72927112, -29.64485972, -29.55578693, -29.45569694, -29.35111795,
-29.23276107, -29.10289365, -28.91425473, -28.70204648, -28.50670713, -29.24662471, -29.12148491, -28.94244964, -28.73421833, -28.53930479,
-28.3282514, -28.15895225, -28.009065, -27.87864672, -27.76315964, -28.36231261, -28.19361236, -28.04376778, -27.91280403, -27.79433658,
-27.68523133, -27.62260405, -27.58076622] -27.7065072, -27.64495288, -27.59798975]
else: else:
expected_sig_out = [ expected_sig_out = [
-30.00529182, -29.93540907, -29.86554231, -29.79417979, -29.71903263, -29.95025022, -29.88168886, -29.81178634, -29.73838831, -29.66318631,
-29.6424009, -29.56531159, -29.48775435, -29.41468382, -29.35973323, -29.58762141, -29.51156294, -29.43760161, -29.38124626, -29.34245197,
-29.32286555, -29.28602346, -29.2472908, -29.20086569, -29.14671746, -29.30629475, -29.26970711, -29.22566555, -29.17412914, -29.11806869,
-29.08702653, -29.01341963, -28.93430243, -28.87791656, -28.84413339, -29.05122228, -28.97358131, -28.90658619, -28.86616148, -28.83854197,
-28.81605918, -28.78824936, -28.76071036, -28.73319161, -28.70494101, -28.81115028, -28.78403337, -28.7570206, -28.73002834, -28.70088634,
-28.67368479, -28.63941012, -28.60178381, -28.55585766, -28.5066561, -28.66844432, -28.63427939, -28.59364514, -28.54659009, -28.49180643,
-28.43426575, -28.33848379, -28.24471112, -28.18220815, -28.15076699, -28.41406352, -28.31434813, -28.22984104, -28.18249387, -28.1516453,
-28.11934744, -28.08776718, -28.05548097, -28.02250068, -27.98954302, -28.12082034, -28.08970494, -28.05779424, -28.02543415, -27.99309889,
-27.95661362, -27.92370274, -27.8854762, -27.84193785, -27.79238328, -27.96078803, -27.92798594, -27.89002127, -27.84689015, -27.79726968,
-27.72452662, -27.6385071, -27.54788144, -27.44581202, -27.33924103, -27.72927112, -27.64485972, -27.55578693, -27.45569694, -27.35111795,
-27.23276107, -27.10289365, -26.91425473, -26.70204648, -26.50670713, -27.24662471, -27.12148491, -26.94244964, -26.73421833, -26.53930479,
-26.3282514, -26.15895225, -26.009065, -25.87864672, -25.76315964, -26.36231261, -26.19361236, -26.04376778, -25.91280403, -25.79433658,
-25.68523133, -25.62260405, -25.58076622] -25.7065072, -25.64495288, -25.59798975]
print(sig_out) print(sig_out)
assert_allclose(sig_out, expected_sig_out, rtol=1e-9) assert_allclose(sig_out, expected_sig_out, rtol=1e-9)

View File

@@ -33,7 +33,7 @@ SERVICE_FILENAME = DATA_DIR / 'testTopology_services_expected.json'
grid = 0.00625e12 grid = 0.00625e12
slot = 0.0125e12 slot = 0.0125e12
guardband = 0.15e12 guardband = 25.0e9
cband_freq_min = 191.3e12 cband_freq_min = 191.3e12
cband_freq_max = 196.1e12 cband_freq_max = 196.1e12
@@ -101,14 +101,15 @@ def test_wrong_values(nval, mval, setup):
def test_aligned(nmin, nmax, setup): def test_aligned(nmin, nmax, setup):
"""Checks that the OMS grid is correctly aligned """Checks that the OMS grid is correctly aligned
Note that bitmap index uses guardband on both ends so that if nmin, nmax = -200, +200, Note that bitmap index uses guardband on both ends so that if center frequencies nmin, nmax = -200, +200,
min/max navalue in bitmap are -224, +223, which makes 223 -(-224) +1 frequencies. min/max navalue in bitmap are -224, +223, which makes 223 -(-224) +1 frequencies.
""" """
network, oms_list = setup network, oms_list = setup
nguard = guardband / grid nguard = guardband / grid
center = 193.1e12 center = 193.1e12
freq_min = center + nmin * grid # amplification band
freq_max = center + nmax * grid freq_min = center + nmin * grid - guardband
freq_max = center + nmax * grid + guardband
random_oms = oms_list[10] random_oms = oms_list[10]
# We're always starting with full C-band # We're always starting with full C-band
@@ -116,9 +117,9 @@ def test_aligned(nmin, nmax, setup):
assert pytest.approx(nvalue_to_frequency(random_oms.spectrum_bitmap.freq_index_max) * 1e-12, abs=1e-12) == 196.1 assert pytest.approx(nvalue_to_frequency(random_oms.spectrum_bitmap.freq_index_max) * 1e-12, abs=1e-12) == 196.1
ind_max = len(random_oms.spectrum_bitmap.bitmap) - 1 ind_max = len(random_oms.spectrum_bitmap.bitmap) - 1
# "inner" frequencies, without the guard baand # "inner" frequencies, without the guard band
inner_n_min = random_oms.spectrum_bitmap.getn(0) + nguard inner_n_min = random_oms.spectrum_bitmap.getn(0) + nguard
inner_n_max = random_oms.spectrum_bitmap.getn(ind_max) - nguard + 1 inner_n_max = random_oms.spectrum_bitmap.getn(ind_max) - nguard
assert inner_n_min == random_oms.spectrum_bitmap.freq_index_min assert inner_n_min == random_oms.spectrum_bitmap.freq_index_min
assert inner_n_max == random_oms.spectrum_bitmap.freq_index_max assert inner_n_max == random_oms.spectrum_bitmap.freq_index_max
assert inner_n_min == -288 assert inner_n_min == -288
@@ -136,7 +137,7 @@ def test_aligned(nmin, nmax, setup):
ind_max = len(random_oms.spectrum_bitmap.bitmap) - 1 ind_max = len(random_oms.spectrum_bitmap.bitmap) - 1
inner_n_min = random_oms.spectrum_bitmap.getn(0) + nguard inner_n_min = random_oms.spectrum_bitmap.getn(0) + nguard
inner_n_max = random_oms.spectrum_bitmap.getn(ind_max) - nguard + 1 inner_n_max = random_oms.spectrum_bitmap.getn(ind_max) - nguard
assert inner_n_min == random_oms.spectrum_bitmap.freq_index_min assert inner_n_min == random_oms.spectrum_bitmap.freq_index_min
assert inner_n_max == random_oms.spectrum_bitmap.freq_index_max assert inner_n_max == random_oms.spectrum_bitmap.freq_index_max
@@ -198,18 +199,20 @@ def test_assign_and_sum(nval1, nval2, setup):
def test_bitmap_assignment(setup): def test_bitmap_assignment(setup):
"""test that a bitmap can be assigned""" """test that a bitmap can be assigned"""
network, oms_list = setup _, oms_list = setup
random_oms = oms_list[2] random_oms = oms_list[2]
random_oms.assign_spectrum(13, 7) random_oms.assign_spectrum(13, 7)
btmp = deepcopy(random_oms.spectrum_bitmap.bitmap) btmp = deepcopy(random_oms.spectrum_bitmap.bitmap)
# try a first assignment that must pass # try a first assignment that must pass
spectrum_btmp = Bitmap(cband_freq_min, cband_freq_max, grid=0.00625e12, guardband=0.15e12, bitmap=btmp) freq_min = nvalue_to_frequency(random_oms.spectrum_bitmap.n_min)
freq_max = nvalue_to_frequency(random_oms.spectrum_bitmap.n_max)
_ = Bitmap(freq_min, freq_max, grid=0.00625e12, guardband=guardband, bitmap=btmp)
# try a wrong assignment that should not pass # try a wrong assignment that should not pass
btmp = btmp[1:-1] btmp = btmp[1:-1]
with pytest.raises(SpectrumError): with pytest.raises(SpectrumError):
spectrum_btmp = Bitmap(cband_freq_min, cband_freq_max, grid=0.00625e12, guardband=0.15e12, bitmap=btmp) _ = Bitmap(cband_freq_min, cband_freq_max, grid=0.00625e12, guardband=guardband, bitmap=btmp)
@pytest.fixture() @pytest.fixture()