Exemple #1
0
def propagate_and_optimize_mode(path, req, equipment, show=False):
    # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the
    # step 1: create an ordered list of modes based on baudrate
    baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode 
        if float(m['min_spacing'])<= req.spacing]))  
        # TODO be carefull on limits cases if spacing very close to req spacing eg 50.001 50.000
    baudrate_to_explore = sorted(baudrate_to_explore, reverse=True)
    if baudrate_to_explore : 
        # at least 1 baudrate can be tested wrt spacing
        for b in baudrate_to_explore :
            modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode 
                if m['baud_rate'] == b and float(m['min_spacing'])<= req.spacing]
            modes_to_explore = sorted(modes_to_explore, 
                key = lambda x: x['bit_rate'], reverse=True)
            # print(modes_to_explore)
            # step2 : computes propagation for each baudrate: stop and select the first that passes
            found_a_feasible_mode = False
            # TODO : the case of roll of is not included: for now use SI one
            # TODO : if the loop in mode optimization does not have a feasible path, then bugs
            si = create_input_spectral_information(
            req.f_min, req.f_max, equipment['SI']['default'].roll_off,
            b, req.power, req.spacing)
            for el in path:
                si = el(si)
                if show:
                    print(el)
            for m in modes_to_explore :
                if path[-1].snr is not None:
                    path[-1].update_snr(m['tx_osnr'], equipment['Roadm']['default'].add_drop_osnr)
                    if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] :
                        found_a_feasible_mode = True
                        return path, m
                else:  
                    return [], None
        # only get to this point if no baudrate/mode satisfies OSNR requirement

        # returns the last propagated path and mode
        msg = f'\tWarning! Request {req.request_id}: no mode satisfies path SNR requirement.\n'
        print(msg)
        logger.info(msg)
        return [],None
    else :
    #  no baudrate satisfying spacing
        msg = f'\tWarning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n'
        print(msg)
        logger.info(msg)
        return [], None
Exemple #2
0
 def propagate(self, pref, *carriers):
     #pin_target and loss are read from eqpt_config.json['Roadm']
     #all ingress channels in xpress are set to this power level
     #but add channels are not, so we define an effective loss
     #in the case of add channels
     self.effective_pch_out_db = min(pref.p_spani, self.params.target_pch_out_db)
     self.effective_loss = pref.p_spani - self.effective_pch_out_db
     carriers_power = array([c.power.signal +c.power.nli+c.power.ase for c in carriers])
     carriers_att = list(map(lambda x : lin2db(x*1e3)-self.params.target_pch_out_db, carriers_power))
     exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default = 0)
     carriers_att = list(map(lambda x: db2lin(x+exceeding_att), carriers_att))
     for carrier_att, carrier in zip(carriers_att, carriers) :
         pwr = carrier.power
         pwr = pwr._replace( signal = pwr.signal/carrier_att,
                             nli = pwr.nli/carrier_att,
                             ase = pwr.ase/carrier_att)
         yield carrier._replace(power=pwr)
def test_excel_service_json_generation(xls_input, expected_json_output):
    """ test services creation
    """
    equipment = load_equipment(eqpt_filename)
    network = load_network(DATA_DIR / 'testTopology.xls', equipment)
    # Build the network once using the default power defined in SI in eqpt config
    p_db = equipment['SI']['default'].power_dbm
    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)
    from_xls = read_service_sheet(xls_input,
                                  equipment,
                                  network,
                                  network_filename=DATA_DIR /
                                  'testTopology.xls')
    assert from_xls == load_json(expected_json_output)
def test_setup():
    ''' common setup for tests: builds network, equipment and oms only once
    '''
    equipment = load_equipment(EQPT_LIBRARY_NAME)
    network = load_network(NETWORK_FILE_NAME, equipment)
    # Build the network once using the default power defined in SI in eqpt config
    # power density : db2linp(ower_dbm': 0)/power_dbm': 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)
    build_oms_list(network, equipment)

    return network, equipment
Exemple #5
0
def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
    """ compare the 2 amplifier models (polynomial and estimated from nf_min and max)
     => nf_model vs nf_poly_fit for intermediate gain values:
     between gain_min and gain_flatmax some discrepancy is expected but target < 0.5dB
     => unitary test for Edfa._calc_nf (and Edfa.interpol_params)"""
    edfa = setup_edfa_variable_gain
    frequencies = array([c.frequency for c in si.carriers])
    pin = array(
        [c.power.signal + c.power.nli + c.power.ase for c in si.carriers])
    pin = pin / db2lin(gain)
    baud_rates = array([c.baud_rate for c in si.carriers])
    edfa.operational.gain_target = gain
    pref = Pref(0, -gain, lin2db(len(frequencies)))
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
    nf_model = edfa.nf[0]
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
    nf_poly = edfa.nf[0]
    assert pytest.approx(nf_model, abs=0.5) == nf_poly
Exemple #6
0
def test_compare_nf_models(gain, setup_edfa_variable_gain, si):
    """ compare the 2 amplifier models (polynomial and estimated from nf_min and max)
     => nf_model vs nf_poly_fit for intermediate gain values:
     between gain_min and gain_flatmax some discrepancy is expected but target < 0.5dB
     => unitary test for Edfa._calc_nf (and Edfa.interpol_params)"""
    edfa = setup_edfa_variable_gain
    frequencies = array([c.frequency for c in si.carriers])
    pin = array(
        [c.power.signal + c.power.nli + c.power.ase for c in si.carriers])
    pin = pin / db2lin(gain)
    baud_rates = array([c.baud_rate for c in si.carriers])
    edfa.operational.gain_target = gain
    # edfa is variable gain type
    pref = Pref(0, -gain, lin2db(len(frequencies)))
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
    nf_model = edfa.nf[0]

    # change edfa type variety to a polynomial
    el_config = {
        "uid": "Edfa1",
        "operational": {
            "gain_target": gain,
            "tilt_target": 0
        },
        "metadata": {
            "location": {
                "region": "",
                "latitude": 2,
                "longitude": 0
            }
        }
    }
    equipment = load_equipment(eqpt_library)
    extra_params = equipment['Edfa']['CienaDB_medium_gain']
    temp = el_config.setdefault('params', {})
    temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
    el_config['params'] = temp
    edfa = Edfa(**el_config)

    # edfa is variable gain type
    edfa.interpol_params(frequencies, pin, baud_rates, pref)
    nf_poly = edfa.nf[0]
    print(nf_poly, nf_model)
    assert pytest.approx(nf_model, abs=0.5) == nf_poly
Exemple #7
0
def test_no_amp_feature(node_uid):
    ''' Check that booster is not placed on a roadm if fused is specified
        test_parser covers partly this behaviour. This test should guaranty that the
        feature is preserved even if convert is changed
    '''
    equipment = load_equipment(EQPT_LIBRARY_NAME)
    json_network = load_json(NETWORK_FILE_NAME)

    for elem in json_network['elements']:
        if elem['uid'] == node_uid:
            #replace edfa node by a fused node in the topology
            elem['type'] = 'Fused'
            elem.pop('type_variety')
            elem.pop('operational')
            elem['params'] = {'loss': 0}

            next_node_uid = next(conn['to_node'] for conn in json_network['connections'] \
                                 if conn['from_node'] == node_uid)
            previous_node_uid = next(conn['from_node'] for conn in json_network['connections'] \
                                 if conn['to_node'] == node_uid)

    network = network_from_json(json_network, equipment)
    # Build the network once using the default power defined in SI in eqpt config
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm
    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))

    build_network(network, equipment, p_db, p_total_db)

    node = next(nd for nd in network.nodes() if nd.uid == node_uid)
    next_node = next(network.successors(node))
    previous_node = next(network.predecessors(node))

    if not isinstance(node, Fused):
        raise AssertionError()
    if not node.params.loss == 0.0:
        raise AssertionError()
    if not next_node_uid == next_node.uid:
        raise AssertionError()
    if not previous_node_uid == previous_node.uid:
        raise AssertionError()
Exemple #8
0
    def _calc_nf(self):
        """nf calculation based on 2 models: self.params.nf_model.enabled from json import:
        True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
        False => polynomial fit based on self.params.nf_fit_coeff"""
        #TODO : tbd alarm rising or input VOA padding in case
        #gain_min > gain_target TBD:
        pad = max(self.params.gain_min - self.operational.gain_target, 0)
        gain_target = self.operational.gain_target + pad
        dg = gain_target - self.params.gain_flatmax  # ! <0
        if self.params.nf_model.enabled:
            g1a = gain_target - self.params.nf_model.delta_p + dg
            nf_avg = lin2db(
                db2lin(self.params.nf_model.nf1) +
                db2lin(self.params.nf_model.nf2) / db2lin(g1a))
        else:
            nf_avg = np.polyval(self.params.nf_fit_coeff, dg)

        nf_array = self.interpol_nf_ripple + nf_avg + pad  #input VOA = 1 for 1 NF degradation
        return nf_array
Exemple #9
0
 def update_snr(self, *args):
     """
     snr_added in 0.1nm
     compute SNR penalties such as transponder Tx_osnr or Roadm add_drop_osnr
     only applied in request.py / propagate on the last Trasceiver node of the path
     all penalties are added in a single call because to avoid uncontrolled cumul
     """
     # use raw_values so that the added SNR penalties are not cumulated
     snr_added = 0
     for s in args:
         snr_added += db2lin(-s)
     snr_added = -lin2db(snr_added)
     self.osnr_ase = list(map(lambda x, y: snr_sum(x, y, snr_added),
                              self.raw_osnr_ase, self.baud_rate))
     self.snr = list(map(lambda x, y: snr_sum(x, y, snr_added),
                         self.raw_snr, self.baud_rate))
     self.osnr_ase_01nm = list(map(lambda x: snr_sum(x, 12.5e9, snr_added),
                                   self.raw_osnr_ase_01nm))
     self.snr_01nm = list(map(lambda x: snr_sum(x, 12.5e9, snr_added),
                              self.raw_snr_01nm))
def calc_average_gsnr_opt():
    """
    Function to print the average GSNR for all channels for several .mat data files
    """
    path_data = Path(
        '/mnt/Bruno_Data/GoogleDrive/Material_Academico/PoliTo_WON/Research/Simulations_Data/Results/'
        'JOCN_Power_Optimization/C_L_S/Future_scenarios_analyze/Profiles')

    list_folder = [file for file in os.listdir(path_data)]

    for folder in list_folder:
        print(folder)
        list_files = [file for file in os.listdir(path_data / folder)]
        for file in list_files:
            data = loadmat(path_data / (folder + '/' + file))
            gsnr = np.transpose(data['GSNR'])

            print('Name of profile: {}'.format(data['name'][0]))
            print('Average GSNR: {} dB'.format(np.mean(lin2db(gsnr))))
        print('\n')
def test_disjunction(net, eqpt, serv):
    data = load_requests(serv, eqpt)
    equipment = load_equipment(eqpt)
    network = load_network(net, equipment)

    # Build the network once using the default power defined in SI in eqpt config
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)

    rqs = requests_from_json(data, equipment)
    rqs = correct_route_list(network, rqs)
    dsjn = disjunctions_from_json(data)
    pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
    print(dsjn)

    dsjn_list = [d.disjunctions_req for d in dsjn]

    # assumes only pairs in dsjn list
    test = True
    for e in dsjn_list:
        rqs_id_list = [r.request_id for r in rqs]
        p1 = pths[rqs_id_list.index(e[0])][1:-1]
        p2 = pths[rqs_id_list.index(e[1])][1:-1]
        if isdisjoint(p1, p2) + isdisjoint(p1, find_reversed_path(
                p2, network)) > 0:
            test = False
            print(
                f'Computed path (roadms):{[e.uid for e in p1  if isinstance(e, Roadm)]}\n'
            )
            print(
                f'Computed path (roadms):{[e.uid for e in p2  if isinstance(e, Roadm)]}\n'
            )
            break
    print(dsjn_list)
    assert test
def test_auto_design_generation_fromjson(tmpdir, json_input, power_mode):
    """test that autodesign creates same file as an input file already autodesigned
    """
    equipment = load_equipment(eqpt_filename)
    network = load_network(json_input, equipment)
    # in order to test the Eqpt sheet and load gain target,
    # change the power-mode to False (to be in gain mode)
    equipment['Span']['default'].power_mode = power_mode
    # Build the network once using the default power defined in SI in eqpt config

    p_db = equipment['SI']['default'].power_dbm
    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)
    actual_json_output = tmpdir / json_input.with_name(
        json_input.stem + '_auto_design').with_suffix('.json').name
    save_network(network, actual_json_output)
    actual = load_json(actual_json_output)
    unlink(actual_json_output)
    assert actual == load_json(json_input)
def calc_best_gsnr(data_path, output_folder=None, thr=0.01, copy_files=False):
    """
    Save a list of profiles with best GSNR average for a threshold value of difference
    """
    config_path = data_path / 'config_file.json'
    if not output_folder:
        output_folder = data_path.parent / 'results/results_{}'.format(thr)
    if not os.path.isdir(output_folder):
        os.makedirs(output_folder)

    # Create list of .mat files (combinations) and load configuration file
    list_data_files = [file for file in os.listdir(data_path) if file.endswith('.mat')]
    config = json.load(open(config_path, 'r'))
    bands = config['spectral_config']['bands']

    # Calculation only for the channels under test
    ordered_list = []
    files_bar = tqdm(iterable=list_data_files, desc='Calculating gsnr average for all combinations')
    for file_name in files_bar:
        data = DataQot.load_qot_mat(data_path / file_name)
        calc_gsnr_lin = [gsnr for i, gsnr in enumerate(data.gsnr) if i in data.cut_index]
        calc_gsnr_db = [lin2db(gsnr) for gsnr in calc_gsnr_lin]
        gsnr_average = np.mean(calc_gsnr_db)
        ordered_list.append([data.name, gsnr_average])

    # Order list of combinations by GSNR average
    ordered_list.sort(key=lambda value: value[1], reverse=True)
    thr_gsnr = ordered_list[0][1] - (ordered_list[0][1] * thr)

    # Remove items with smaller GSNR average than the threshold
    ordered_list = [item for item in ordered_list if item[1] >= thr_gsnr]
    print('{} profiles with acceptable GSNR average'.format(len(ordered_list)))

    # Save best GSNR list and profiles
    with open((output_folder / 'Best_GSNR_average_{}.txt'.format(thr)), 'w') as best_combs:
        for comb in ordered_list:
            best_combs.write('{}: {}\n'.format(comb[0], comb[1]))
            if copy_files:
                shutil.copy2((data_path / (comb[0] + '.mat')), output_folder)
Exemple #14
0
 def _calc_nf(self, avg=False):
     """nf calculation based on 2 models: self.params.nf_model.enabled from json import:
     True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
     False => polynomial fit based on self.params.nf_fit_coeff"""
     # TODO|jla: TBD alarm rising or input VOA padding in case
     # gain_min > gain_target TBD:
     pad = max(self.params.gain_min - self.effective_gain, 0)
     self.att_in = pad
     gain_target = self.effective_gain + pad
     dg = max(self.params.gain_flatmax - gain_target, 0)
     if self.params.type_def == 'variable_gain':
         g1a = gain_target - self.params.nf_model.delta_p - dg
         nf_avg = lin2db(
             db2lin(self.params.nf_model.nf1) +
             db2lin(self.params.nf_model.nf2) / db2lin(g1a))
     elif self.params.type_def == 'fixed_gain':
         nf_avg = self.params.nf_model.nf0
     else:
         nf_avg = polyval(self.params.nf_fit_coeff, -dg)
     if avg:
         return nf_avg + pad
     else:
         return self.interpol_nf_ripple + nf_avg + pad  # input VOA = 1 for 1 NF degradation
Exemple #15
0
    def _calc_nf(self, avg=False):
        """nf calculation based on 2 models: self.params.nf_model.enabled from json import:
        True => 2 stages amp modelling based on precalculated nf1, nf2 and delta_p in build_OA_json
        False => polynomial fit based on self.params.nf_fit_coeff"""
        # gain_min > gain_target TBD:
        if self.params.type_def == 'dual_stage':
            g1 = self.params.preamp_gain_flatmax
            g2 = self.effective_gain - g1
            nf1_avg, pad = self._nf(self.params.preamp_type_def,
                                    self.params.preamp_nf_model,
                                    self.params.preamp_nf_fit_coeff,
                                    self.params.preamp_gain_min,
                                    self.params.preamp_gain_flatmax,
                                    g1)
            # no padding expected for the 1stage because g1 = gain_max
            nf2_avg, pad = self._nf(self.params.booster_type_def,
                                    self.params.booster_nf_model,
                                    self.params.booster_nf_fit_coeff,
                                    self.params.booster_gain_min,
                                    self.params.booster_gain_flatmax,
                                    g2)
            nf_avg = lin2db(db2lin(nf1_avg) + db2lin(nf2_avg - g1))
            # no padding expected for the 1stage because g1 = gain_max
            pad = 0
        else:
            nf_avg, pad = self._nf(self.params.type_def,
                                   self.params.nf_model,
                                   self.params.nf_fit_coeff,
                                   self.params.gain_min,
                                   self.params.gain_flatmax,
                                   self.effective_gain)

        self.att_in = pad  # not used to attenuate carriers, only used in _repr_ and _str_
        if avg:
            return nf_avg
        else:
            return self.interpol_nf_ripple + nf_avg  # input VOA = 1 for 1 NF degradation
def calc_average_gsnr():
    path_data = Path(
        '/mnt/Bruno_Data/GoogleDrive/Material_Academico/PoliTo_WON/research/Simulations_Data/Results/'
        'JOCN_Power_Optimization/Compare_GSNR')
    config_path = path_data / 'config_file_cl.json'
    config = json.load(open(config_path, 'r'))
    bands = config['spectral_config']['bands']

    data = loadmat(
        path_data /
        'jocn_2020_l_c_offset_l=0.5_tilt_l=0.1_offset_c=1.0_tilt_c=-0.3.mat')

    num_ch_band = 0
    for band in bands:
        bands[band]['comp_channels_ind'] = [
            (ch + num_ch_band - 1) for ch in bands[band]['comp_channels']
        ]
        num_ch_band += bands[band]['nb_channels']

    gsnr_per_band = 0.0
    total_gsnr, freq_list = [], []
    for band in bands:
        gsnr_per_band = [
            lin2db(gsnr) for i, gsnr in enumerate(data['GSNR'][0])
            if i in bands[band]['comp_channels_ind']
        ]
        total_gsnr.extend(gsnr_per_band)
        freq_list.extend([
            freq for i, freq in enumerate(data['frequencies'][0])
            if i in bands[band]['comp_channels_ind']
        ])
        print('Average GSNR for {} band is {}'.format(band,
                                                      np.mean(gsnr_per_band)))

    print('Average GSNR is {}'.format(np.mean(total_gsnr)))
    array = np.array([freq_list, total_gsnr])
    np.save(path_data / 'C+L.npy', array)
Exemple #17
0
 def propagate(self, pref, *carriers, degree):
     # pin_target and loss are read from eqpt_config.json['Roadm']
     # all ingress channels in xpress are set to this power level
     # but add channels are not, so we define an effective loss
     # in the case of add channels
     # find the target power on this degree:
     # if a target power has been defined for this degree use it else use the global one.
     # if the input power is lower than the target one, use the input power instead because
     # a ROADM doesn't amplify, it can only attenuate
     # TODO maybe add a minimum loss for the ROADM
     per_degree_pch = self.per_degree_pch_out_db[degree] if degree in self.per_degree_pch_out_db.keys() else self.params.target_pch_out_db
     self.effective_pch_out_db = min(pref.p_spani, per_degree_pch)
     self.effective_loss = pref.p_spani - self.effective_pch_out_db
     carriers_power = array([c.power.signal + c.power.nli + c.power.ase for c in carriers])
     carriers_att = list(map(lambda x: lin2db(x * 1e3) - per_degree_pch, carriers_power))
     exceeding_att = -min(list(filter(lambda x: x < 0, carriers_att)), default=0)
     carriers_att = list(map(lambda x: db2lin(x + exceeding_att), carriers_att))
     for carrier_att, carrier in zip(carriers_att, carriers):
         pwr = carrier.power
         pwr = pwr._replace(signal=pwr.signal / carrier_att,
                            nli=pwr.nli / carrier_att,
                            ase=pwr.ase / carrier_att)
         pmd = sqrt(carrier.pmd**2 + self.params.pmd**2)
         yield carrier._replace(power=pwr, pmd=pmd)
Exemple #18
0
 def path_metric(pth, req):
     """ creates the metrics dictionary
     """
     return [{
         'metric-type': 'SNR-bandwidth',
         'accumulative-value': round(mean(pth[-1].snr), 2)
     }, {
         'metric-type':
         'SNR-0.1nm',
         'accumulative-value':
         round(mean(pth[-1].snr + lin2db(req.baud_rate / 12.5e9)), 2)
     }, {
         'metric-type': 'OSNR-bandwidth',
         'accumulative-value': round(mean(pth[-1].osnr_ase), 2)
     }, {
         'metric-type': 'OSNR-0.1nm',
         'accumulative-value': round(mean(pth[-1].osnr_ase_01nm), 2)
     }, {
         'metric-type': 'reference_power',
         'accumulative-value': req.power
     }, {
         'metric-type': 'path_bandwidth',
         'accumulative-value': req.path_bandwidth
     }]
def test_auto_design_generation_fromxlsgainmode(tmpdir, xls_input,
                                                expected_json_output):
    """ tests generation of topology json
        test that the build network gives correct results in gain mode
    """
    equipment = load_equipment(eqpt_filename)
    network = load_network(xls_input, equipment)
    # in order to test the Eqpt sheet and load gain target,
    # change the power-mode to False (to be in gain mode)
    equipment['Span']['default'].power_mode = False
    # Build the network once using the default power defined in SI in eqpt config

    p_db = equipment['SI']['default'].power_dbm
    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)
    actual_json_output = tmpdir / xls_input.with_name(
        xls_input.stem + '_auto_design').with_suffix('.json').name
    save_network(network, actual_json_output)
    actual = load_json(actual_json_output)
    unlink(actual_json_output)
    assert actual == load_json(expected_json_output)
Exemple #20
0
    except EquipmentConfigError as e:
        print(f'{ansi_escapes.red}Configuration error in the equipment library:{ansi_escapes.reset} {e}')
        exit(1)
    except NetworkTopologyError as e:
        print(f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}')
        exit(1)
    except ConfigurationError as e:
        print(f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
        exit(1)

    # Build the network once using the default power defined in SI in eqpt config
    # TODO power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(automatic_nch(equipment['SI']['default'].f_min,\
        equipment['SI']['default'].f_max, equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)
    save_network(args.network_filename, network)

    rqs = requests_from_json(data, equipment)

    # check that request ids are unique. Non unique ids, may
    # mess the computation : better to stop the computation
    all_ids = [r.request_id for r in rqs]
    if len(all_ids) != len(set(all_ids)):
        for a in list(set(all_ids)):
            all_ids.remove(a)
        msg = f'Requests id {all_ids} are not unique'
        logger.critical(msg)
        exit()
    rqs = correct_route_list(network, rqs)
Exemple #21
0
def create_input_spectral_information(f_min, f_max, roll_off, baud_rate, power,
                                      spacing):
    # pref in dB : convert power lin into power in dB
    pref = lin2db(power * 1e3)
    nb_channel = automatic_nch(f_min, f_max, spacing)
    si = SpectralInformation(pref=Pref(pref, pref, lin2db(nb_channel)),
                             carriers=[
                                 Channel(f, (f_min + spacing * f), baud_rate,
                                         roll_off, Power(power, 0, 0))
                                 for f in range(1, nb_channel + 1)
                             ])
    return si


if __name__ == '__main__':
    pref = lin2db(power * 1e3)
    si = SpectralInformation(
        Pref(pref, pref),
        Channel(
            1,
            193.95e12,
            32e9,
            0.15,  # 193.95 THz, 32 Gbaud
            Power(1e-3, 1e-6, 1e-6)),  # 1 mW, 1uW, 1uW
        Channel(
            1,
            195.95e12,
            32e9,
            0.15,  # 195.95 THz, 32 Gbaud
            Power(1.2e-3, 1e-6, 1e-6)),  # 1.2 mW, 1uW, 1uW
    )
Exemple #22
0
def test_excel_ila_constraints(source, destination, route_list, hoptype,
                               expected_correction):
    """ add different kind of constraints to test all correct_route cases
    """
    service_xls_input = DATA_DIR / 'testTopology.xls'
    network_json_input = DATA_DIR / 'testTopology_auto_design_expected.json'
    equipment = load_equipment(eqpt_filename)
    network = load_network(network_json_input, equipment)
    # increase length of one span to trigger automatic fiber splitting included by autodesign
    # so that the test also covers this case
    next(node for node in network.nodes()
         if node.uid == 'fiber (Brest_KLA → Quimper)-').length = 200000
    next(node for node in network.nodes()
         if node.uid == 'fiber (Quimper → Brest_KLA)-').length = 200000
    default_si = equipment['SI']['default']
    p_db = default_si.power_dbm
    p_total_db = p_db + lin2db(
        automatic_nch(default_si.f_min, default_si.f_max, default_si.spacing))
    build_network(network, equipment, p_db, p_total_db)
    # create params for a request based on input
    nodes_list = route_list.split(' | ') if route_list is not None else []
    params = {
        'request_id':
        '0',
        'source':
        source,
        'bidir':
        False,
        'destination':
        destination,
        'trx_type':
        '',
        'trx_mode':
        '',
        'format':
        '',
        'spacing':
        '',
        'nodes_list':
        nodes_list,
        'loose_list':
        [hoptype for node in nodes_list] if route_list is not None else '',
        'f_min':
        0,
        'f_max':
        0,
        'baud_rate':
        0,
        'OSNR':
        None,
        'bit_rate':
        None,
        'cost':
        None,
        'roll_off':
        0,
        'tx_osnr':
        0,
        'min_spacing':
        None,
        'nb_channel':
        0,
        'power':
        0,
        'path_bandwidth':
        0,
    }
    request = PathRequest(**params)

    if expected_correction != 'Fail':
        [request] = correct_xls_route_list(service_xls_input, network,
                                           [request])
        assert request.nodes_list == expected_correction
    else:
        with pytest.raises(ServiceError):
            [request] = correct_xls_route_list(service_xls_input, network,
                                               [request])
Exemple #23
0
def test_json_response_generation(xls_input, expected_response_file):
    """ tests if json response is correctly generated for all combinations of requests
    """

    equipment = load_equipment(eqpt_filename)
    network = load_network(xls_input, equipment)
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)

    data = read_service_sheet(xls_input, equipment, network)
    # change one of the request with bidir option to cover bidir case as well
    data['path-request'][2]['bidirectional'] = True

    oms_list = build_oms_list(network, equipment)
    rqs = requests_from_json(data, equipment)
    dsjn = disjunctions_from_json(data)
    dsjn = deduplicate_disjunctions(dsjn)
    rqs, dsjn = requests_aggregation(rqs, dsjn)
    pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
    propagatedpths, reversed_pths, reversed_propagatedpths = \
        compute_path_with_disjunction(network, equipment, rqs, pths)
    pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)

    result = []
    for i, pth in enumerate(propagatedpths):
        # test ServiceError handling : when M is zero at this point, the
        # json result should not be created if there is no blocking reason
        if i == 1:
            my_rq = deepcopy(rqs[i])
            my_rq.M = 0
            with pytest.raises(ServiceError):
                ResultElement(my_rq, pth, reversed_propagatedpths[i]).json

            my_rq.blocking_reason = 'NO_SPECTRUM'
            ResultElement(my_rq, pth, reversed_propagatedpths[i]).json

        result.append(ResultElement(rqs[i], pth, reversed_propagatedpths[i]))

    temp = {'response': [n.json for n in result]}

    expected = load_json(expected_response_file)

    for i, response in enumerate(temp['response']):
        if i == 2:
            # compare response must be False because z-a metric is missing
            # (request with bidir option to cover bidir case)
            assert not compare_response(expected['response'][i], response)
            print(f'response {response["response-id"]} should not match')
            expected['response'][2]['path-properties']['z-a-path-metric'] = [{
                'metric-type':
                'SNR-bandwidth',
                'accumulative-value':
                22.809999999999999
            }, {
                'metric-type':
                'SNR-0.1nm',
                'accumulative-value':
                26.890000000000001
            }, {
                'metric-type':
                'OSNR-bandwidth',
                'accumulative-value':
                26.239999999999998
            }, {
                'metric-type':
                'OSNR-0.1nm',
                'accumulative-value':
                30.32
            }, {
                'metric-type':
                'reference_power',
                'accumulative-value':
                0.0012589254117941673
            }, {
                'metric-type':
                'path_bandwidth',
                'accumulative-value':
                60000000000.0
            }]
            # test should be OK now
        else:
            assert compare_response(expected['response'][i], response)
            print(f'response {response["response-id"]} is not correct')
Exemple #24
0
    def _gain_profile(self, pin):
        """
        Pin : input power / channel in W

        :param gain_ripple: design flat gain
        :param dgt: design gain tilt
        :param Pin: total input power in W
        :param gp: Average gain setpoint in dB units
        :param gtp: gain tilt setting
        :type gain_ripple: numpy.ndarray
        :type dgt: numpy.ndarray
        :type Pin: numpy.ndarray
        :type gp: float
        :type gtp: float
        :return: gain profile in dBm
        :rtype: numpy.ndarray

        AMPLIFICATION USING INPUT PROFILE
        INPUTS:
            gain_ripple - vector of length number of channels or spectral slices
            DGT - vector of length number of channels or spectral slices
            Pin - input powers vector of length number of channels or
            spectral slices
            Gp  - provisioned gain length 1
            GTp - provisioned tilt length 1

        OUTPUT:
            amp gain per channel or spectral slice
        NOTE: there is no checking done for violations of the total output
            power capability of the amp.
        EDIT OF PREVIOUS NOTE: power violation now added in interpol_params
            Ported from Matlab version written by David Boerges at Ciena.
        Based on:
            R. di Muro, "The Er3+ fiber gain coefficient derived from a dynamic
            gain
            tilt technique", Journal of Lightwave Technology, Vol. 18, Iss. 3,
            Pp. 343-347, 2000.
        """
        err_tolerance = 1.0e-11
        simple_opt = True

        # TODO check what param should be used (currently length(dgt))
        nchan = np.arange(len(self.interpol_dgt))

        # TODO find a way to use these or lose them.  Primarily we should have
        # a way to determine if exceeding the gain or output power of the amp
        tot_in_power_db = lin2db(np.sum(pin * 1e3))  # ! Pin expressed in W

        # Linear fit to get the
        p = np.polyfit(nchan, self.interpol_dgt, 1)
        dgt_slope = p[0]

        # Calculate the target slope-  Currently assumes equal spaced channels
        # TODO make it so that supports arbitrary channel spacing.
        targ_slope = self.operational.tilt_target / (len(nchan) - 1)

        # 1st estimate of DGT scaling
        if abs(dgt_slope) > 0.001:  # add check for div 0 due to flat dgt
            dgts1 = targ_slope / dgt_slope
        else:
            dgts1 = 0
        # when simple_opt is true code makes 2 attempts to compute gain and
        # the internal voa value.  This is currently here to provide direct
        # comparison with original Matlab code.  Will be removed.
        # TODO replace with loop

        if simple_opt:

            # 1st estimate of Er gain & voa loss
            g1st = np.array(self.interpol_gain_ripple) + self.params.gain_flatmax + \
                np.array(self.interpol_dgt) * dgts1
            voa = lin2db(np.mean(db2lin(g1st))) - self.operational.gain_target

            # 2nd estimate of Amp ch gain using the channel input profile
            g2nd = g1st - voa

            pout_db = lin2db(np.sum(pin * 1e3 * db2lin(g2nd)))
            dgts2 = self.operational.gain_target - (pout_db - tot_in_power_db)

            # Center estimate of amp ch gain
            xcent = dgts2
            gcent = g1st - voa + np.array(self.interpol_dgt) * xcent
            pout_db = lin2db(np.sum(pin * 1e3 * db2lin(gcent)))
            gavg_cent = pout_db - tot_in_power_db

            # Lower estimate of Amp ch gain
            deltax = np.max(g1st) - np.min(g1st)
            # ! if no ripple deltax = 0 => xlow = xcent: div 0
            # add check for flat gain response :
            if abs(
                    deltax
            ) > 0.05:  #enough ripple to consider calculation and avoid div 0
                xlow = dgts2 - deltax
                glow = g1st - voa + np.array(self.interpol_dgt) * xlow
                pout_db = lin2db(np.sum(pin * 1e3 * db2lin(glow)))
                gavg_low = pout_db - tot_in_power_db

                # Upper gain estimate
                xhigh = dgts2 + deltax
                ghigh = g1st - voa + np.array(self.interpol_dgt) * xhigh
                pout_db = lin2db(np.sum(pin * 1e3 * db2lin(ghigh)))
                gavg_high = pout_db - tot_in_power_db

                # compute slope
                slope1 = (gavg_low - gavg_cent) / (xlow - xcent)
                slope2 = (gavg_cent - gavg_high) / (xcent - xhigh)

                if np.abs(self.operational.gain_target -
                          gavg_cent) <= err_tolerance:
                    dgts3 = xcent
                elif self.operational.gain_target < gavg_cent:
                    dgts3 = xcent - (gavg_cent -
                                     self.operational.gain_target) / slope1
                else:
                    dgts3 = xcent + (-gavg_cent +
                                     self.operational.gain_target) / slope2

                gprofile = g1st - voa + np.array(self.interpol_dgt) * dgts3
            else:  #not enough ripple
                gprofile = g1st - voa
        else:  #simple_opt
            gprofile = None

        return gprofile
def test_automaticmodefeature(net, eqpt, serv, expected_mode):
    equipment = load_equipment(eqpt)
    network = load_network(net, equipment)
    data = load_requests(serv,
                         eqpt,
                         bidir=False,
                         network=network,
                         network_filename=net)

    # Build the network once using the default power defined in SI in eqpt config
    # power density : db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    build_network(network, equipment, p_db, p_total_db)

    rqs = requests_from_json(data, equipment)
    rqs = correct_json_route_list(network, rqs)
    dsjn = []
    pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
    path_res_list = []

    for i, pathreq in enumerate(rqs):

        # use the power specified in requests but might be different from the one specified for design
        # the power is an optional parameter for requests definition
        # if optional, use the one defines in eqt_config.json
        p_db = lin2db(pathreq.power * 1e3)
        p_total_db = p_db + lin2db(pathreq.nb_channel)
        print(f'request {pathreq.request_id}')
        print(f'Computing path from {pathreq.source} to {pathreq.destination}')
        # adding first node to be clearer on the output
        print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}')

        total_path = pths[i]
        print(
            f'Computed path (roadms):{[e.uid for e in total_path  if isinstance(e, Roadm)]}\n'
        )
        # for debug
        # print(f'{pathreq.baud_rate}   {pathreq.power}   {pathreq.spacing}   {pathreq.nb_channel}')
        if pathreq.baud_rate is not None:
            print(pathreq.format)
            path_res_list.append(pathreq.format)
            total_path = propagate(total_path, pathreq, equipment)
        else:
            total_path, mode = propagate_and_optimize_mode(
                total_path, pathreq, equipment)
            # if no baudrate satisfies spacing, no mode is returned and an empty path is returned
            # a warning is shown in the propagate_and_optimize_mode
            if mode is not None:
                print(mode['format'])
                path_res_list.append(mode['format'])
            else:
                print('nok')
                path_res_list.append('nok')
    print(path_res_list)
    assert path_res_list == expected_mode
Exemple #26
0
def jsontocsv(json_data, equipment, fileout):
    # read json path result file in accordance with:
    # Yang model for requesting Path Computation
    # draft-ietf-teas-yang-path-computation-01.txt.
    # and write results in an CSV file

    mywriter = writer(fileout)
    mywriter.writerow(('response-id','source','destination','path_bandwidth','Pass?',\
        'nb of tsp pairs','total cost','transponder-type','transponder-mode',\
        'OSNR-0.1nm','SNR-0.1nm','SNR-bandwidth','baud rate (Gbaud)',\
        'input power (dBm)','path'))
    tspjsondata = equipment['Transceiver']
    #print(tspjsondata)

    for pth_el in json_data['response']:
        path_id = pth_el['response-id']
        try:
            if pth_el['no-path']:
                source = ''
                destination = ''
                tsp = ''
                mode = ''
                isok = False
                nb_tsp = 0
                pthbdbw = ''
                rosnr = ''
                rsnr = ''
                rsnrb = ''
                br = ''
                pw = ''
                total_cost = ''
                pth = ''
        except KeyError:

            source = pth_el['path-properties']['path-route-objects'][0]\
            ['path-route-object']['num-unnum-hop']['node-id']
            destination = pth_el['path-properties']['path-route-objects'][-2]\
            ['path-route-object']['num-unnum-hop']['node-id']
            # selects only roadm nodes
            temp = []
            for e in pth_el['path-properties']['path-route-objects']:
                try:
                    temp.append(
                        e['path-route-object']['num-unnum-hop']['node-id'])
                except KeyError:
                    pass
            pth = ' | '.join(temp)

            temp_tsp = pth_el['path-properties']['path-route-objects'][1]\
            ['path-route-object']['transponder']
            tsp = temp_tsp['transponder-type']
            mode = temp_tsp['transponder-mode']

            # find the min  acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
            # loading equipment already tests the existence of tsp type and mode:
            if mode != 'not feasible with this transponder':
                [minosnr, baud_rate, bit_rate, cost] = next(
                    [m['OSNR'], m['baud_rate'], m['bit_rate'], m['cost']]
                    for m in equipment['Transceiver'][tsp].mode
                    if m['format'] == mode)
            # else:
            #     [minosnr, baud_rate, bit_rate] = ['','','','']
            output_snr = next(e['accumulative-value']
                              for e in pth_el['path-properties']['path-metric']
                              if e['metric-type'] == 'SNR-0.1nm')
            output_snrbandwidth = next(
                e['accumulative-value']
                for e in pth_el['path-properties']['path-metric']
                if e['metric-type'] == 'SNR-bandwidth')
            output_osnr = next(
                e['accumulative-value']
                for e in pth_el['path-properties']['path-metric']
                if e['metric-type'] == 'OSNR-0.1nm')
            output_osnrbandwidth = next(
                e['accumulative-value']
                for e in pth_el['path-properties']['path-metric']
                if e['metric-type'] == 'OSNR-bandwidth')
            power = next(e['accumulative-value']
                         for e in pth_el['path-properties']['path-metric']
                         if e['metric-type'] == 'reference_power')
            path_bandwidth = next(
                e['accumulative-value']
                for e in pth_el['path-properties']['path-metric']
                if e['metric-type'] == 'path_bandwidth')
            if isinstance(output_snr, str):
                isok = False
                nb_tsp = 0
                pthbdbw = round(path_bandwidth * 1e-9, 2)
                rosnr = ''
                rsnr = ''
                rsnrb = ''
                br = ''
                pw = ''
                total_cost = ''
            else:
                isok = output_snr >= minosnr
                nb_tsp = ceil(path_bandwidth / bit_rate)
                pthbdbw = round(path_bandwidth * 1e-9, 2)
                rosnr = round(output_osnr, 2)
                rsnr = round(output_snr, 2)
                rsnrb = round(output_snrbandwidth, 2)
                br = round(baud_rate * 1e-9, 2)
                pw = round(lin2db(power) + 30, 2)
                total_cost = nb_tsp * cost

        mywriter.writerow(
            (path_id, source, destination, pthbdbw, isok, nb_tsp, total_cost,
             tsp, mode, rosnr, rsnr, rsnrb, br, pw, pth))
Exemple #27
0
    def pathresult(self):
        if not self.computed_path:
            return {
                'response-id':
                self.path_id,
                'no-path':
                "Response without path information, due to failure performing the path computation"
            }
        else:
            index = 0
            pro_list = []
            for element in self.computed_path:
                temp = {
                    'path-route-object': {
                        'index': index,
                        'num-unnum-hop': {
                            'node-id': element.uid,
                            'link-tp-id': element.uid,
                            # TODO change index in order to insert transponder attribute
                        }
                    }
                }
                pro_list.append(temp)
                index += 1
                if isinstance(element, Transceiver):
                    temp = {
                        'path-route-object': {
                            'index': index,
                            'transponder': {
                                'transponder-type': self.path_request.tsp,
                                'transponder-mode': self.path_request.tsp_mode,
                            }
                        }
                    }
                    pro_list.append(temp)
                    index += 1

            response = {
                'response-id': self.path_id,
                'path-properties':{
                    'path-metric': [
                        {
                            'metric-type': 'SNR-bandwidth',
                            'accumulative-value': round(mean(self.computed_path[-1].snr), 2)
                        },
                        {
                            'metric-type': 'SNR-0.1nm',
                            'accumulative-value': round(mean(self.computed_path[-1]. snr + \
                                                            lin2db(self.path_request.baud_rate/12.5e9)), 2)
                        },
                        {
                            'metric-type': 'OSNR-bandwidth',
                            'accumulative-value': round(mean(self.computed_path[-1].osnr_ase), 2)
                        },
                        {
                            'metric-type': 'OSNR-0.1nm',
                            'accumulative-value': round(mean(self.computed_path[-1].osnr_ase_01nm), 2)
                        },
                        {
                            'metric-type': 'reference_power',
                            'accumulative-value': self.path_request.power
                        },
                        {
                            'metric-type': 'path_bandwidth',
                            'accumulative-value': self.path_request.path_bandwidth
                        }
                        ],
                    'path-route-objects': pro_list
                    }
                }
        return response
Exemple #28
0
 def update_pref(self, pref, *carriers):
     pch_out_db = lin2db(
         mean([carrier.power.signal for carrier in carriers])) + 30
     self.pch_out_db = round(pch_out_db, 2)
     return pref._replace(p_span0=pref.p_span0, p_spani=self.pch_out_db)
Exemple #29
0
def path_requests_run(args=None):
    parser = argparse.ArgumentParser(
        description=
        'Compute performance for a list of services provided in a json file or an excel sheet',
        epilog=_help_footer,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )
    _add_common_options(parser,
                        network_default=_examples_dir /
                        'meshTopologyExampleV2.xls')
    parser.add_argument('service_filename',
                        nargs='?',
                        type=Path,
                        metavar='SERVICES-REQUESTS.(json|xls|xlsx)',
                        default=_examples_dir / 'meshTopologyExampleV2.xls',
                        help='Input service file')
    parser.add_argument('-bi',
                        '--bidir',
                        action='store_true',
                        help='considers that all demands are bidir')
    parser.add_argument(
        '-o',
        '--output',
        type=Path,
        metavar=_help_fname_json_csv,
        help='Store satisifed requests into a JSON or CSV file')

    args = parser.parse_args(args if args is not None else sys.argv[1:])
    _setup_logging(args)

    _logger.info(
        f'Computing path requests {args.service_filename} into JSON format')
    print(
        f'{ansi_escapes.blue}Computing path requests {os.path.relpath(args.service_filename)} into JSON format{ansi_escapes.reset}'
    )

    (equipment,
     network) = load_common_data(args.equipment, args.topology,
                                 args.sim_params,
                                 args.save_network_before_autodesign)

    # Build the network once using the default power defined in SI in eqpt config
    # TODO power density: db2linp(ower_dbm": 0)/power_dbm": 0 * nb channels as defined by
    # spacing, f_min and f_max
    p_db = equipment['SI']['default'].power_dbm

    p_total_db = p_db + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))
    try:
        build_network(network, equipment, p_db, p_total_db)
    except exceptions.NetworkTopologyError as e:
        print(
            f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}'
        )
        sys.exit(1)
    except exceptions.ConfigurationError as e:
        print(
            f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
        sys.exit(1)
    if args.save_network is not None:
        save_network(network, args.save_network)
        print(
            f'{ansi_escapes.blue}Network (after autodesign) saved to {args.save_network}{ansi_escapes.reset}'
        )
    oms_list = build_oms_list(network, equipment)

    try:
        data = load_requests(args.service_filename,
                             equipment,
                             bidir=args.bidir,
                             network=network,
                             network_filename=args.topology)
        rqs = requests_from_json(data, equipment)
    except exceptions.ServiceError as e:
        print(f'{ansi_escapes.red}Service error:{ansi_escapes.reset} {e}')
        sys.exit(1)
    # check that request ids are unique. Non unique ids, may
    # mess the computation: better to stop the computation
    all_ids = [r.request_id for r in rqs]
    if len(all_ids) != len(set(all_ids)):
        for item in list(set(all_ids)):
            all_ids.remove(item)
        msg = f'Requests id {all_ids} are not unique'
        _logger.critical(msg)
        sys.exit()
    rqs = correct_json_route_list(network, rqs)

    # pths = compute_path(network, equipment, rqs)
    dsjn = disjunctions_from_json(data)

    print(f'{ansi_escapes.blue}List of disjunctions{ansi_escapes.reset}')
    print(dsjn)
    # need to warn or correct in case of wrong disjunction form
    # disjunction must not be repeated with same or different ids
    dsjn = deduplicate_disjunctions(dsjn)

    # Aggregate demands with same exact constraints
    print(
        f'{ansi_escapes.blue}Aggregating similar requests{ansi_escapes.reset}')

    rqs, dsjn = requests_aggregation(rqs, dsjn)
    # TODO export novel set of aggregated demands in a json file

    print(
        f'{ansi_escapes.blue}The following services have been requested:{ansi_escapes.reset}'
    )
    print(rqs)

    print(
        f'{ansi_escapes.blue}Computing all paths with constraints{ansi_escapes.reset}'
    )
    try:
        pths = compute_path_dsjctn(network, equipment, rqs, dsjn)
    except exceptions.DisjunctionError as this_e:
        print(
            f'{ansi_escapes.red}Disjunction error:{ansi_escapes.reset} {this_e}'
        )
        sys.exit(1)

    print(
        f'{ansi_escapes.blue}Propagating on selected path{ansi_escapes.reset}')
    propagatedpths, reversed_pths, reversed_propagatedpths = compute_path_with_disjunction(
        network, equipment, rqs, pths)
    # Note that deepcopy used in compute_path_with_disjunction returns
    # a list of nodes which are not belonging to network (they are copies of the node objects).
    # so there can not be propagation on these nodes.

    pth_assign_spectrum(pths, rqs, oms_list, reversed_pths)

    print(f'{ansi_escapes.blue}Result summary{ansi_escapes.reset}')
    header = [
        'req id', '  demand', '  snr@bandwidth A-Z (Z-A)',
        '  [email protected] A-Z (Z-A)', '  Receiver minOSNR', '  mode', '  Gbit/s',
        '  nb of tsp pairs', 'N,M or blocking reason'
    ]
    data = []
    data.append(header)
    for i, this_p in enumerate(propagatedpths):
        rev_pth = reversed_propagatedpths[i]
        if rev_pth and this_p:
            psnrb = f'{round(mean(this_p[-1].snr),2)} ({round(mean(rev_pth[-1].snr),2)})'
            psnr = f'{round(mean(this_p[-1].snr_01nm), 2)}' +\
                f' ({round(mean(rev_pth[-1].snr_01nm),2)})'
        elif this_p:
            psnrb = f'{round(mean(this_p[-1].snr),2)}'
            psnr = f'{round(mean(this_p[-1].snr_01nm),2)}'

        try:
            if rqs[i].blocking_reason in BLOCKING_NOPATH:
                line = [
                    f'{rqs[i].request_id}',
                    f' {rqs[i].source} to {rqs[i].destination} :', f'-', f'-',
                    f'-', f'{rqs[i].tsp_mode}',
                    f'{round(rqs[i].path_bandwidth * 1e-9,2)}', f'-',
                    f'{rqs[i].blocking_reason}'
                ]
            else:
                line = [
                    f'{rqs[i].request_id}',
                    f' {rqs[i].source} to {rqs[i].destination} : ', psnrb,
                    psnr, f'-', f'{rqs[i].tsp_mode}',
                    f'{round(rqs[i].path_bandwidth * 1e-9, 2)}', f'-',
                    f'{rqs[i].blocking_reason}'
                ]
        except AttributeError:
            line = [
                f'{rqs[i].request_id}',
                f' {rqs[i].source} to {rqs[i].destination} : ', psnrb, psnr,
                f'{rqs[i].OSNR + equipment["SI"]["default"].sys_margins}',
                f'{rqs[i].tsp_mode}',
                f'{round(rqs[i].path_bandwidth * 1e-9,2)}',
                f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }',
                f'({rqs[i].N},{rqs[i].M})'
            ]
        data.append(line)

    col_width = max(len(word) for row in data for word in row[2:])  # padding
    firstcol_width = max(len(row[0]) for row in data)  # padding
    secondcol_width = max(len(row[1]) for row in data)  # padding
    for row in data:
        firstcol = ''.join(row[0].ljust(firstcol_width))
        secondcol = ''.join(row[1].ljust(secondcol_width))
        remainingcols = ''.join(
            word.center(col_width, ' ') for word in row[2:])
        print(f'{firstcol} {secondcol} {remainingcols}')
    print(
        f'{ansi_escapes.yellow}Result summary shows mean SNR and OSNR (average over all channels){ansi_escapes.reset}'
    )

    if args.output:
        result = []
        # assumes that list of rqs and list of propgatedpths have same order
        for i, pth in enumerate(propagatedpths):
            result.append(
                ResultElement(rqs[i], pth, reversed_propagatedpths[i]))
        temp = _path_result_json(result)
        if args.output.suffix.lower() == '.json':
            save_json(temp, args.output)
            print(
                f'{ansi_escapes.blue}Saved JSON to {args.output}{ansi_escapes.reset}'
            )
        elif args.output.suffix.lower() == '.csv':
            with open(args.output, "w", encoding='utf-8') as fcsv:
                jsontocsv(temp, equipment, fcsv)
            print(
                f'{ansi_escapes.blue}Saved CSV to {args.output}{ansi_escapes.reset}'
            )
        else:
            print(
                f'{ansi_escapes.red}Cannot save output: neither JSON nor CSV file{ansi_escapes.reset}'
            )
            sys.exit(1)
Exemple #30
0
def transmission_main_example(args=None):
    parser = argparse.ArgumentParser(
        description=
        'Send a full spectrum load through the network from point A to point B',
        epilog=_help_footer,
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
    )
    _add_common_options(parser,
                        network_default=_examples_dir /
                        'edfa_example_network.json')
    parser.add_argument('--show-channels',
                        action='store_true',
                        help='Show final per-channel OSNR summary')
    parser.add_argument('-pl', '--plot', action='store_true')
    parser.add_argument('-l',
                        '--list-nodes',
                        action='store_true',
                        help='list all transceiver nodes')
    parser.add_argument('-po',
                        '--power',
                        default=0,
                        help='channel ref power in dBm')
    parser.add_argument('source', nargs='?', help='source node')
    parser.add_argument('destination', nargs='?', help='destination node')

    args = parser.parse_args(args if args is not None else sys.argv[1:])
    _setup_logging(args)

    (equipment,
     network) = load_common_data(args.equipment, args.topology,
                                 args.sim_params,
                                 args.save_network_before_autodesign)

    if args.plot:
        plot_baseline(network)

    transceivers = {
        n.uid: n
        for n in network.nodes() if isinstance(n, Transceiver)
    }

    if not transceivers:
        sys.exit('Network has no transceivers!')
    if len(transceivers) < 2:
        sys.exit('Network has only one transceiver!')

    if args.list_nodes:
        for uid in transceivers:
            print(uid)
        sys.exit()

    # First try to find exact match if source/destination provided
    if args.source:
        source = transceivers.pop(args.source, None)
        valid_source = True if source else False
    else:
        source = None
        _logger.info('No source node specified: picking random transceiver')

    if args.destination:
        destination = transceivers.pop(args.destination, None)
        valid_destination = True if destination else False
    else:
        destination = None
        _logger.info(
            'No destination node specified: picking random transceiver')

    # If no exact match try to find partial match
    if args.source and not source:
        # TODO code a more advanced regex to find nodes match
        source = next(
            (transceivers.pop(uid)
             for uid in transceivers if args.source.lower() in uid.lower()),
            None)

    if args.destination and not destination:
        # TODO code a more advanced regex to find nodes match
        destination = next((transceivers.pop(uid) for uid in transceivers
                            if args.destination.lower() in uid.lower()), None)

    # If no partial match or no source/destination provided pick random
    if not source:
        source = list(transceivers.values())[0]
        del transceivers[source.uid]

    if not destination:
        destination = list(transceivers.values())[0]

    _logger.info(f'source = {args.source!r}')
    _logger.info(f'destination = {args.destination!r}')

    params = {}
    params['request_id'] = 0
    params['trx_type'] = ''
    params['trx_mode'] = ''
    params['source'] = source.uid
    params['destination'] = destination.uid
    params['bidir'] = False
    params['nodes_list'] = [destination.uid]
    params['loose_list'] = ['strict']
    params['format'] = ''
    params['path_bandwidth'] = 0
    trx_params = trx_mode_params(equipment)
    #     Randomly generate input power
    input_power = round(random.random(), 2)
    input_power = -2 + (input_power * (8))
    args.power = input_power
    if args.power:
        trx_params['power'] = db2lin(float(args.power)) * 1e-3
    params.update(trx_params)
    req = PathRequest(**params)

    power_mode = equipment['Span']['default'].power_mode
    print('\n'.join([
        f'Power mode is set to {power_mode}',
        f'=> it can be modified in eqpt_config.json - Span'
    ]))

    pref_ch_db = lin2db(req.power *
                        1e3)  # reference channel power / span (SL=20dB)
    pref_total_db = pref_ch_db + lin2db(
        req.nb_channel)  # reference total power / span (SL=20dB)
    try:
        build_network(network, equipment, pref_ch_db, pref_total_db)
    except exceptions.NetworkTopologyError as e:
        print(
            f'{ansi_escapes.red}Invalid network definition:{ansi_escapes.reset} {e}'
        )
        sys.exit(1)
    except exceptions.ConfigurationError as e:
        print(
            f'{ansi_escapes.red}Configuration error:{ansi_escapes.reset} {e}')
        sys.exit(1)
    path = compute_constrained_path(network, req)

    spans = [
        s.params.length for s in path
        if isinstance(s, RamanFiber) or isinstance(s, Fiber)
    ]
    print(
        f'\nThere are {len(spans)} fiber spans over {sum(spans)/1000:.0f} km between {source.uid} '
        f'and {destination.uid}')
    print(f'\nNow propagating between {source.uid} and {destination.uid}:')

    try:
        p_start, p_stop, p_step = equipment['SI']['default'].power_range_db
        p_num = abs(int(round(
            (p_stop - p_start) / p_step))) + 1 if p_step != 0 else 1
        power_range = list(linspace(p_start, p_stop, p_num))
    except TypeError:
        print(
            'invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]'
        )
        power_range = [0]

    if not power_mode:
        # power cannot be changed in gain mode
        power_range = [0]
    for dp_db in power_range:
        req.power = db2lin(pref_ch_db + dp_db) * 1e-3
        if power_mode:
            print(
                f'\nPropagating with input power = {ansi_escapes.cyan}{lin2db(req.power*1e3):.2f} dBm{ansi_escapes.reset}:'
            )
        else:
            print(
                f'\nPropagating in {ansi_escapes.cyan}gain mode{ansi_escapes.reset}: power cannot be set manually'
            )
        infos = propagate(path, req, equipment)
        if len(power_range) == 1:
            for elem in path:
                print(elem)
            if power_mode:
                print(
                    f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f} dBm:'
                )
            else:
                print(f'\nTransmission results:')
#             print('-------------')
#             print(destination.snr_01nm)
#             print('-------------')
            print(
                f'  Final SNR total (0.1 nm): {ansi_escapes.cyan}{mean(destination.snr_01nm):.02f} dB{ansi_escapes.reset}'
            )
        else:
            print(path[-1])

    if args.save_network is not None:
        save_network(network, args.save_network)
        print(
            f'{ansi_escapes.blue}Network (after autodesign) saved to {args.save_network}{ansi_escapes.reset}'
        )

    if args.show_channels:
        print('\nThe total SNR per channel at the end of the line is:')
        print('{:>5}{:>26}{:>26}{:>28}{:>28}{:>28}'.format(
            'Ch. #', 'Channel frequency (THz)', 'Channel power (dBm)',
            'OSNR ASE (signal bw, dB)', 'SNR NLI (signal bw, dB)',
            'SNR total (signal bw, dB)'))
        #         print(dir(info))
        for final_carrier, ch_osnr, ch_snr_nl, ch_snr in zip(
                infos.carriers, path[-1].osnr_ase, path[-1].osnr_nli,
                path[-1].snr):
            ch_freq = final_carrier.frequency * 1e-12
            ch_power = lin2db(final_carrier.power.signal * 1e3)
            print('{:5}{:26.2f}{:26.2f}{:28.2f}{:28.2f}{:28.2f}'.format(
                final_carrier.channel_number, round(ch_freq, 2),
                round(ch_power, 2), round(ch_osnr, 2), round(ch_snr_nl, 2),
                round(ch_snr, 2)))

    if not args.source:
        print(f'\n(No source node specified: picked {source.uid})')
    elif not valid_source:
        print(
            f'\n(Invalid source node {args.source!r} replaced with {source.uid})'
        )

    if not args.destination:
        print(f'\n(No destination node specified: picked {destination.uid})')
    elif not valid_destination:
        print(
            f'\n(Invalid destination node {args.destination!r} replaced with {destination.uid})'
        )

    if args.plot:
        plot_results(network, path, source, destination)

#     MY ADDITION
# just to see what the different contributions of ASE and NLI are
#     return input_power, path[-1].osnr_ase, path[-1].osnr_nli, path[-1].snr
# to test Raman
    return input_power, destination.snr_01nm, mean(destination.snr_01nm)