Beispiel #1
0
def test_n_m_requests(setup, equipment, n, m, final_n, final_m,
                      blocking_reason, request_set):
    """ test that various N and M values for a request end up with the correct path assgnment
    """
    network, oms_list = setup
    # add an occupation on one of the span of the expected path OMS list on both directions
    # as defined by its offsets within the OMS list: [17, 20, 13, 22] and reversed path [19, 16, 21, 26]
    expected_path = [17, 20, 13, 22]
    expected_oms = [13, 16, 17, 19, 20, 21, 22, 26]
    some_oms = oms_list[expected_oms[3]]
    some_oms.assign_spectrum(
        -30, 32
    )  # means that spectrum is occupied from indexes -62 to 1 on reversed path
    params = request_set
    params['effective_freq_slot'] = {'N': n, 'M': m}
    rqs = [PathRequest(**params)]

    paths = compute_path_dsjctn(network, equipment, rqs, [])
    # check that the computed path is the expected one (independant of blocking issues due to spectrum)
    path_oms = list(
        set([
            e.oms_id for e in paths[0]
            if not isinstance(e, (Transceiver, Roadm))
        ]))
    assert path_oms == expected_path
    # function to be tested:
    pth_assign_spectrum(paths, rqs, oms_list, [find_reversed_path(paths[0])])
    # check that spectrum is correctly assigned
    assert rqs[0].N == final_n
    assert rqs[0].M == final_m
    assert getattr(rqs[0], 'blocking_reason', None) == blocking_reason
Beispiel #2
0
def create_rq(equipment,
              srce,
              dest,
              bdir,
              node_list,
              loose_list,
              rqid='test_request'):
    ''' create the usual request list according to parameters
    '''
    requests_list = []
    params = {
        'request_id': rqid,
        'source': srce,
        'bidir': bdir,
        'destination': dest,
        'trx_type': 'Voyager',
        'trx_mode': 'mode 1',
        'spacing': 50000000000.0,
        'nodes_list': node_list,
        'loose_list': loose_list,
        'path_bandwidth': 100.0e9,
        'power': 1.0,
        'effective_freq_slot': None,
    }
    params['format'] = params['trx_mode']
    trx_params = trx_mode_params(equipment, params['trx_type'],
                                 params['trx_mode'], True)
    params.update(trx_params)
    f_min = params['f_min']
    f_max_from_si = params['f_max']
    params['nb_channel'] = automatic_nch(f_min, f_max_from_si,
                                         params['spacing'])
    requests_list.append(PathRequest(**params))
    return requests_list
Beispiel #3
0
def requests_from_json(json_data, equipment):
    """Extract list of requests from data parsed from JSON"""
    requests_list = []

    for req in json_data['path-request']:
        # init all params from request
        params = {}
        params['request_id'] = req['request-id']
        params['source'] = req['source']
        params['bidir'] = req['bidirectional']
        params['destination'] = req['destination']
        params['trx_type'] = req['path-constraints']['te-bandwidth']['trx_type']
        params['trx_mode'] = req['path-constraints']['te-bandwidth']['trx_mode']
        params['format'] = params['trx_mode']
        params['spacing'] = req['path-constraints']['te-bandwidth']['spacing']
        try:
            nd_list = req['explicit-route-objects']['route-object-include-exclude']
        except KeyError:
            nd_list = []
        params['nodes_list'] = [n['num-unnum-hop']['node-id'] for n in nd_list]
        params['loose_list'] = [n['num-unnum-hop']['hop-type'] for n in nd_list]
        # recover trx physical param (baudrate, ...) from type and mode
        # in trx_mode_params optical power is read from equipment['SI']['default'] and
        # nb_channel is computed based on min max frequency and spacing
        trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
        params.update(trx_params)
        # print(trx_params['min_spacing'])
        # optical power might be set differently in the request. if it is indicated then the
        # params['power'] is updated
        try:
            if req['path-constraints']['te-bandwidth']['output-power']:
                params['power'] = req['path-constraints']['te-bandwidth']['output-power']
        except KeyError:
            pass
        # same process for nb-channel
        f_min = params['f_min']
        f_max_from_si = params['f_max']
        try:
            if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None:
                nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel']
                params['nb_channel'] = nch
                spacing = params['spacing']
                params['f_max'] = automatic_fmax(f_min, spacing, nch)
            else:
                params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
        except KeyError:
            params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
        _check_one_request(params, f_max_from_si)

        try:
            params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth']
        except KeyError:
            pass
        requests_list.append(PathRequest(**params))
    return requests_list
Beispiel #4
0
def test_freq_slot_exist(setup, equipment, request_set):
    """ test that assignment works even if effective_freq_slot is not populated
    """
    network, oms_list = setup
    params = request_set
    params['effective_freq_slot'] = None
    rqs = [PathRequest(**params)]
    paths = compute_path_dsjctn(network, equipment, rqs, [])
    pth_assign_spectrum(paths, rqs, oms_list, [find_reversed_path(paths[0])])
    assert rqs[0].N == -256
    assert rqs[0].M == 32
Beispiel #5
0
def test_inconsistant_freq_slot(setup, equipment, request_set):
    """ test that an inconsistant M correctly raises an error
    """
    network, oms_list = setup
    params = request_set
    # minimum required nb of slots is 32 (800Gbit/100Gbit/s channels each occupying 50GHz ie 4 slots)
    params['effective_freq_slot'] = {'N': 0, 'M': 4}
    with pytest.raises(ServiceError):
        _check_one_request(params, 196.05e12)
    params['trx_mode'] = None
    rqs = [PathRequest(**params)]
    paths = compute_path_dsjctn(network, equipment, rqs, [])
    pth_assign_spectrum(paths, rqs, oms_list, [find_reversed_path(paths[0])])
    assert rqs[0].blocking_reason == 'NOT_ENOUGH_RESERVED_SPECTRUM'
Beispiel #6
0
def create_rq(equipment, srce, dest, bdir, nd_list, ls_list):
    """ create the usual request list according to parameters
    """
    requests_list = []
    params = {}
    params['request_id'] = 'test_request'
    params['source'] = srce
    params['bidir'] = bdir
    params['destination'] = dest
    params['trx_type'] = 'Voyager'
    params['trx_mode'] = 'mode 1'
    params['format'] = params['trx_mode']
    params['spacing'] = 50000000000.0
    params['nodes_list'] = nd_list
    params['loose_list'] = ls_list
    trx_params = trx_mode_params(equipment, params['trx_type'], params['trx_mode'], True)
    params.update(trx_params)
    params['power'] = 1.0
    f_min = params['f_min']
    f_max_from_si = params['f_max']
    params['nb_channel'] = automatic_nch(f_min, f_max_from_si, params['spacing'])
    params['path_bandwidth'] = 100000000000.0
    requests_list.append(PathRequest(**params))
    return requests_list
Beispiel #7
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])
Beispiel #8
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)
def test_roadm_target_power(prev_node_type, effective_pch_out_db, power_dbm):
    ''' Check that egress power of roadm is equal to target power if input power is greater
    than target power else, that it is equal to input power. Use a simple two hops A-B-C topology
    for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target
    power can not be met in this last case.
    '''
    equipment = load_equipment(EQPT_LIBRARY_NAME)
    json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json')
    prev_node = next(n for n in json_network['elements']
                     if n['uid'] == 'west edfa in node B to ila2')
    json_network['elements'].remove(prev_node)
    if prev_node_type == 'edfa':
        prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'}
    elif prev_node_type == 'fused':
        prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'}
        prev_node['params'] = {'loss': 0}
    json_network['elements'].append(prev_node)
    network = network_from_json(json_network, equipment)
    p_total_db = power_dbm + lin2db(
        automatic_nch(equipment['SI']['default'].f_min,
                      equipment['SI']['default'].f_max,
                      equipment['SI']['default'].spacing))

    build_network(network, equipment, power_dbm, p_total_db)

    params = {
        'request_id': 0,
        'trx_type': '',
        'trx_mode': '',
        'source': 'trx node A',
        'destination': 'trx node C',
        'bidir': False,
        'nodes_list': ['trx node C'],
        'loose_list': ['strict'],
        'format': '',
        'path_bandwidth': 100e9,
        'effective_freq_slot': None,
    }
    trx_params = trx_mode_params(equipment)
    params.update(trx_params)
    req = PathRequest(**params)
    req.power = db2lin(power_dbm - 30)
    path = compute_constrained_path(network, req)
    si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off,
                                           req.baud_rate, req.power,
                                           req.spacing)
    for i, el in enumerate(path):
        if isinstance(el, Roadm):
            carriers_power_in_roadm = min([
                c.power.signal + c.power.nli + c.power.ase for c in si.carriers
            ])
            si = el(si, degree=path[i + 1].uid)
            if el.uid == 'roadm node B':
                print('input', carriers_power_in_roadm)
                # if previous was an EDFA, power level at ROADM input is enough for the ROADM to apply its
                # target power (as specified in equipment ie -20 dBm)
                # if it is a Fused, the input power to the ROADM is smaller than the target power, and the
                # ROADM cannot apply this target. In this case, it is assumed that the ROADM has 0 dB loss
                # so the output power will be the same as the input power, which for this particular case
                # corresponds to -22dBm + power_dbm
                # next step (for ROADM modelling) will be to apply a minimum loss for ROADMs !
                if prev_node_type == 'edfa':
                    assert el.effective_pch_out_db == effective_pch_out_db
                if prev_node_type == 'fused':
                    # then output power == input_power == effective_pch_out_db + power_dbm
                    assert effective_pch_out_db + power_dbm == \
                        pytest.approx(lin2db(carriers_power_in_roadm * 1e3), rel=1e-3)
                    assert el.effective_pch_out_db == effective_pch_out_db + power_dbm
                for carrier in si.carriers:
                    print(carrier.power.signal + carrier.power.nli +
                          carrier.power.ase)
                    power = carrier.power.signal + carrier.power.nli + carrier.power.ase
                    if prev_node_type == 'edfa':
                        # edfa prev_node sets input power to roadm to a high enough value:
                        # Check that egress power of roadm is equal to target power
                        assert power == pytest.approx(
                            db2lin(effective_pch_out_db - 30), rel=1e-3)
                    elif prev_node_type == 'fused':
                        # fused prev_node does reamplfy power after fiber propagation, so input power
                        # to roadm is low.
                        # Check that egress power of roadm is equalized to the min carrier input power.
                        assert power == pytest.approx(carriers_power_in_roadm,
                                                      rel=1e-3)
        else:
            si = el(si)
def test_roadm_target_power(prev_node_type, effective_pch_out_db):
    ''' Check that egress power of roadm is equal to target power if input power is greater
    than target power else, that it is equal to input power. Use a simple two hops A-B-C topology
    for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target
    power can not be met in this last case.
    '''
    equipment = load_equipment(EQPT_LIBRARY_NAME)
    json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json')
    prev_node = next(n for n in json_network['elements']
                     if n['uid'] == 'west edfa in node B to ila2')
    json_network['elements'].remove(prev_node)
    if prev_node_type == 'edfa':
        prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'}
    elif prev_node_type == 'fused':
        prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'}
        prev_node['params'] = {'loss': 0}
    json_network['elements'].append(prev_node)
    network = network_from_json(json_network, 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)

    params = {}
    params['request_id'] = 0
    params['trx_type'] = ''
    params['trx_mode'] = ''
    params['source'] = 'trx node A'
    params['destination'] = 'trx node C'
    params['bidir'] = False
    params['nodes_list'] = ['trx node C']
    params['loose_list'] = ['strict']
    params['format'] = ''
    params['path_bandwidth'] = 100e9
    trx_params = trx_mode_params(equipment)
    params.update(trx_params)
    req = PathRequest(**params)
    path = compute_constrained_path(network, req)
    si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off,
                                           req.baud_rate, req.power,
                                           req.spacing)
    for i, el in enumerate(path):
        if isinstance(el, Roadm):
            carriers_power_in_roadm = min([
                c.power.signal + c.power.nli + c.power.ase for c in si.carriers
            ])
            si = el(si, degree=path[i + 1].uid)
            if el.uid == 'roadm node B':
                print('input', carriers_power_in_roadm)
                assert el.effective_pch_out_db == effective_pch_out_db
                for carrier in si.carriers:
                    print(carrier.power.signal + carrier.power.nli +
                          carrier.power.ase)
                    power = carrier.power.signal + carrier.power.nli + carrier.power.ase
                    if prev_node_type == 'edfa':
                        # edfa prev_node sets input power to roadm to a high enough value:
                        # Check that egress power of roadm is equal to target power
                        assert power == pytest.approx(
                            db2lin(effective_pch_out_db - 30), rel=1e-3)
                    elif prev_node_type == 'fused':
                        # fused prev_node does reamplfy power after fiber propagation, so input power
                        # to roadm is low.
                        # Check that egress power of roadm is equalized to the min carrier input power.
                        assert power == pytest.approx(carriers_power_in_roadm,
                                                      rel=1e-3)
        else:
            si = el(si)