Example #1
0
def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in):
    """return a dict of header_slice
    key = column index
    value = header name"""

    for h0 in input_headers_dict:
        slice_out = read_slice(my_sheet, start_line, slice_in, h0)
        iteration = 1
        while slice_out == (-1, -1) and iteration < 10:
            # try next lines
            slice_out = read_slice(my_sheet, start_line + iteration, slice_in,
                                   h0)
            iteration += 1
        if slice_out == (-1, -1):
            if h0 in ('east', 'Node A', 'Node Z', 'City'):
                print(
                    f'{ansi_escapes.red}CRITICAL{ansi_escapes.reset}: missing _{h0}_ header: EXECUTION ENDS'
                )
                raise NetworkTopologyError(f'Missing _{h0}_ header')
            else:
                print(f'missing header {h0}')
        elif not isinstance(input_headers_dict[h0], dict):
            headers[slice_out[0]] = input_headers_dict[h0]
        else:
            headers = parse_headers(my_sheet, input_headers_dict[h0], headers,
                                    start_line + 1, slice_out)
    if headers == {}:
        print(
            f'{ansi_escapes.red}CRITICAL ERROR{ansi_escapes.reset}: could not find any header to read _ ABORT'
        )
        raise NetworkTopologyError('Could not find any header to read')
    return headers
Example #2
0
def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):

    duplicate_links = []
    for l1 in links:
        for l2 in links:
            if l1 is not l2 and l1 == l2 and l2 not in duplicate_links:
                print(f'\nWARNING\n \
                    link {l1.from_city}-{l1.to_city} is duplicate \
                    \nthe 1st duplicate link will be removed but you should check Links sheet input'
                      )
                duplicate_links.append(l1)
    for l in duplicate_links:
        links.remove(l)

    unreferenced_nodes = [n for n in nodes_by_city if n not in links_by_city]
    if unreferenced_nodes:
        raise NetworkTopologyError(
            f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} The following nodes are not '
            f'referenced from the {ansi_escapes.cyan}Links{ansi_escapes.reset} sheet. '
            f'If unused, remove them from the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} '
            f'sheet:\n' + _format_items(unreferenced_nodes))
    # no need to check "Links" for invalid nodes because that's already in parse_excel()
    wrong_eqpt_from = [n for n in eqpts_by_city if n not in nodes_by_city]
    wrong_eqpt_to = [
        n.to_city for destinations in eqpts_by_city.values()
        for n in destinations if n.to_city not in nodes_by_city
    ]
    wrong_eqpt = wrong_eqpt_from + wrong_eqpt_to
    if wrong_eqpt:
        raise NetworkTopologyError(
            f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} '
            f'The {ansi_escapes.cyan}Eqpt{ansi_escapes.reset} sheet refers to nodes that '
            f'are not defined in the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} sheet:\n'
            + _format_items(wrong_eqpt))

    for city, link in links_by_city.items():
        if nodes_by_city[city].node_type.lower() == 'ila' and len(link) != 2:
            # wrong input: ILA sites can only be Degree 2
            # => correct to make it a ROADM and remove entry in links_by_city
            # TODO: put in log rather than print
            print(f'invalid node type ({nodes_by_city[city].node_type})\
                  specified in {city}, replaced by ROADM')
            nodes_by_city[city].node_type = 'ROADM'
            for n in nodes:
                if n.city == city:
                    n.node_type = 'ROADM'
    return nodes, links
Example #3
0
def add_connector_loss(network, fibers, default_con_in, default_con_out, EOL):
    for fiber in fibers:
        try:
            next_node = next(network.successors(fiber))
        except StopIteration:
            raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
        if fiber.params.con_in is None:
            fiber.params.con_in = default_con_in
        if fiber.params.con_out is None:
            fiber.params.con_out = default_con_out
        if not isinstance(next_node, elements.Fused):
            fiber.params.con_out += EOL
Example #4
0
def prev_node_generator(network, node):
    """fused spans interest:
    iterate over all predecessors while they are either Fused or Fibers succeeded by Fused"""
    try:
        prev_node = next(network.predecessors(node))
    except StopIteration:
        if isinstance(node, elements.Transceiver):
            return
        raise NetworkTopologyError(f'Node {node.uid} is not properly connected, please check network topology')
    if ((isinstance(prev_node, elements.Fused) and isinstance(node, _fiber_fused_types)) or
            (isinstance(prev_node, _fiber_fused_types) and isinstance(node, elements.Fused))):
        yield prev_node
        yield from prev_node_generator(network, prev_node)
Example #5
0
def next_node_generator(network, node):
    """fused spans interest:
    iterate over all successors while they are Fused or Fiber type"""
    try:
        next_node = next(network.successors(node))
    except StopIteration:
        raise NetworkTopologyError('Node {node.uid} is not properly connected, please check network topology')
    # yield and re-iterate
    if isinstance(next_node, elements.Fused) or isinstance(node, elements.Fused):
        yield next_node
        yield from next_node_generator(network, next_node)
    else:
        StopIteration
Example #6
0
def split_fiber(network, fiber, bounds, target_length, equipment):
    new_length, n_spans = calculate_new_length(fiber.length, bounds,
                                               target_length)
    if n_spans == 1:
        return

    try:
        next_node = next(network.successors(fiber))
        prev_node = next(network.predecessors(fiber))
    except StopIteration:
        raise NetworkTopologyError(
            f'Fiber {fiber.uid} is not properly connected, please check network topology'
        )

    network.remove_node(fiber)

    fiber_params = fiber.params._asdict()
    fiber_params['length'] = new_length / UNITS[fiber.params.length_units]
    fiber_params['con_in'] = fiber.con_in
    fiber_params['con_out'] = fiber.con_out

    f = interp1d([prev_node.lng, next_node.lng],
                 [prev_node.lat, next_node.lat])
    xpos = [
        prev_node.lng + (next_node.lng - prev_node.lng) * (n + 1) /
        (n_spans + 1) for n in range(n_spans)
    ]
    ypos = f(xpos)
    for span, lng, lat in zip(range(n_spans), xpos, ypos):
        new_span = Fiber(uid=f'{fiber.uid}_({span+1}/{n_spans})',
                         metadata={
                             'location': {
                                 'latitude': lat,
                                 'longitude': lng,
                                 'city': fiber.loc.city,
                                 'region': fiber.loc.region,
                             }
                         },
                         params=fiber_params)
        if isinstance(prev_node, Fiber):
            edgeweight = prev_node.params.length
        else:
            edgeweight = 0.01
        network.add_edge(prev_node, new_span, weight=edgeweight)
        prev_node = new_span
    if isinstance(prev_node, Fiber):
        edgeweight = prev_node.params.length
    else:
        edgeweight = 0.01
    network.add_edge(prev_node, next_node, weight=edgeweight)
Example #7
0
def split_fiber(network, fiber, bounds, target_length, equipment):
    new_length, n_spans = calculate_new_length(fiber.params.length, bounds,
                                               target_length)
    if n_spans == 1:
        return

    try:
        next_node = next(network.successors(fiber))
        prev_node = next(network.predecessors(fiber))
    except StopIteration:
        raise NetworkTopologyError(
            f'Fiber {fiber.uid} is not properly connected, please check network topology'
        )

    network.remove_node(fiber)

    fiber.params.length = new_length

    xpos = [
        prev_node.lng + (next_node.lng - prev_node.lng) * (n + 0.5) / n_spans
        for n in range(n_spans)
    ]
    ypos = [
        prev_node.lat + (next_node.lat - prev_node.lat) * (n + 0.5) / n_spans
        for n in range(n_spans)
    ]
    for span, lng, lat in zip(range(n_spans), xpos, ypos):
        new_span = elements.Fiber(uid=f'{fiber.uid}_({span+1}/{n_spans})',
                                  type_variety=fiber.type_variety,
                                  metadata={
                                      'location': {
                                          'latitude': lat,
                                          'longitude': lng,
                                          'city': fiber.loc.city,
                                          'region': fiber.loc.region,
                                      }
                                  },
                                  params=fiber.params.asdict())
        if isinstance(prev_node, elements.Fiber):
            edgeweight = prev_node.params.length
        else:
            edgeweight = 0.01
        network.add_edge(prev_node, new_span, weight=edgeweight)
        prev_node = new_span
    if isinstance(prev_node, elements.Fiber):
        edgeweight = prev_node.params.length
    else:
        edgeweight = 0.01
    network.add_edge(prev_node, next_node, weight=edgeweight)
Example #8
0
def prev_node_generator(network, node):
    """fused spans interest:
    iterate over all predecessors while they are Fused or Fiber type"""
    try:
        prev_node = next(n for n in network.predecessors(node))
    except StopIteration:
        raise NetworkTopologyError(
            f'Node {node.uid} is not properly connected, please check network topology'
        )
    # yield and re-iterate
    if isinstance(prev_node, Fused) or isinstance(node, Fused):
        yield prev_node
        yield from prev_node_generator(network, prev_node)
    else:
        StopIteration
Example #9
0
def network_from_json(json_data, equipment):
    # NOTE|dutc: we could use the following, but it would tie our data format
    #            too closely to the graph library
    # from networkx import node_link_graph
    g = DiGraph()
    for el_config in json_data['elements']:
        typ = el_config.pop('type')
        variety = el_config.pop('type_variety', 'default')
        cls = _cls_for(typ)
        if typ == 'Fused':
            # well, there's no variety for the 'Fused' node type
            pass
        elif variety in equipment[typ]:
            extra_params = equipment[typ][variety]
            temp = el_config.setdefault('params', {})
            temp = merge_amplifier_restrictions(temp, extra_params.__dict__)
            el_config['params'] = temp
            el_config['type_variety'] = variety
        elif (typ in ['Fiber', 'RamanFiber'
                      ]) or (typ == 'Edfa' and variety not in ['default', '']):
            raise ConfigurationError(
                f'The {typ} of variety type {variety} was not recognized:'
                '\nplease check it is properly defined in the eqpt_config json file'
            )
        el = cls(**el_config)
        g.add_node(el)

    nodes = {k.uid: k for k in g.nodes()}

    for cx in json_data['connections']:
        from_node, to_node = cx['from_node'], cx['to_node']
        try:
            if isinstance(nodes[from_node], elements.Fiber):
                edge_length = nodes[from_node].params.length
            else:
                edge_length = 0.01
            g.add_edge(nodes[from_node], nodes[to_node], weight=edge_length)
        except KeyError:
            raise NetworkTopologyError(
                f'can not find {from_node} or {to_node} defined in {cx}')

    return g
Example #10
0
def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city):

    duplicate_links = []
    for l1 in links:
        for l2 in links:
            if l1 is not l2 and l1 == l2 and l2 not in duplicate_links:
                print(f'\nWARNING\n \
                    link {l1.from_city}-{l1.to_city} is duplicate \
                    \nthe 1st duplicate link will be removed but you should check Links sheet input'
                      )
                duplicate_links.append(l1)
    for l in duplicate_links:
        links.remove(l)

    try:
        test_nodes = [n for n in nodes_by_city if n not in links_by_city]
        test_links = [n for n in links_by_city if n not in nodes_by_city]
        test_eqpts = [n for n in eqpts_by_city if n not in nodes_by_city]
        assert (test_nodes == [] or test_nodes == [''])\
            and (test_links == [] or test_links == [''])\
            and (test_eqpts == [] or test_eqpts == [''])
    except AssertionError:
        msg = f'CRITICAL error in excel input: Names in Nodes and Links sheets do no match, check:\
            \n{test_nodes} in Nodes sheet\
            \n{test_links} in Links sheet\
            \n{test_eqpts} in Eqpt sheet'

        raise NetworkTopologyError(msg)

    for city, link in links_by_city.items():
        if nodes_by_city[city].node_type.lower() == 'ila' and len(link) != 2:
            # wrong input: ILA sites can only be Degree 2
            # => correct to make it a ROADM and remove entry in links_by_city
            # TODO: put in log rather than print
            print(f'invalid node type ({nodes_by_city[city].node_type})\
 specified in {city}, replaced by ROADM')
            nodes_by_city[city].node_type = 'ROADM'
            for n in nodes:
                if n.city == city:
                    n.node_type = 'ROADM'
    return nodes, links
Example #11
0
def add_fiber_padding(network, fibers, padding):
    """last_fibers = (fiber for n in network.nodes()
                         if not (isinstance(n, elements.Fiber) or isinstance(n, elements.Fused))
                         for fiber in network.predecessors(n)
                         if isinstance(fiber, elements.Fiber))"""
    for fiber in fibers:
        try:
            next_node = next(network.successors(fiber))
        except StopIteration:
            raise NetworkTopologyError(f'Fiber {fiber.uid} is not properly connected, please check network topology')
        if isinstance(next_node, elements.Fused):
            continue
        this_span_loss = span_loss(network, fiber)
        if this_span_loss < padding:
            # add a padding att_in at the input of the 1st fiber:
            # address the case when several fibers are spliced together
            first_fiber = find_first_node(network, fiber)
            # in order to support no booster , fused might be placed
            # just after a roadm: need to check that first_fiber is really a fiber
            if isinstance(first_fiber, elements.Fiber):
                first_fiber.params.att_in = first_fiber.params.att_in + padding - this_span_loss
Example #12
0
def parse_excel(input_filename):
    link_headers = {
        'Node A': 'from_city',
        'Node Z': 'to_city',
        'east': {
            'Distance (km)': 'east_distance',
            'Fiber type': 'east_fiber',
            'lineic att': 'east_lineic',
            'Con_in': 'east_con_in',
            'Con_out': 'east_con_out',
            'PMD': 'east_pmd',
            'Cable id': 'east_cable'
        },
        'west': {
            'Distance (km)': 'west_distance',
            'Fiber type': 'west_fiber',
            'lineic att': 'west_lineic',
            'Con_in': 'west_con_in',
            'Con_out': 'west_con_out',
            'PMD': 'west_pmd',
            'Cable id': 'west_cable'
        }
    }
    node_headers = {
        'City': 'city',
        'State': 'state',
        'Country': 'country',
        'Region': 'region',
        'Latitude': 'latitude',
        'Longitude': 'longitude',
        'Type': 'node_type',
        'Booster_restriction': 'booster_restriction',
        'Preamp_restriction': 'preamp_restriction'
    }
    eqpt_headers = {
        'Node A': 'from_city',
        'Node Z': 'to_city',
        'east': {
            'amp type': 'east_amp_type',
            'att_in': 'east_att_in',
            'amp gain': 'east_amp_gain',
            'delta p': 'east_amp_dp',
            'tilt': 'east_tilt',
            'att_out': 'east_att_out'
        },
        'west': {
            'amp type': 'west_amp_type',
            'att_in': 'west_att_in',
            'amp gain': 'west_amp_gain',
            'delta p': 'west_amp_dp',
            'tilt': 'west_tilt',
            'att_out': 'west_att_out'
        }
    }
    roadm_headers = {
        'Node A': 'from_node',
        'Node Z': 'to_node',
        'per degree target power (dBm)': 'target_pch_out_db'
    }

    with open_workbook(input_filename) as wb:
        nodes_sheet = wb.sheet_by_name('Nodes')
        links_sheet = wb.sheet_by_name('Links')
        try:
            eqpt_sheet = wb.sheet_by_name('Eqpt')
        except Exception:
            # eqpt_sheet is optional
            eqpt_sheet = None
        try:
            roadm_sheet = wb.sheet_by_name('Roadms')
        except Exception:
            # roadm_sheet is optional
            roadm_sheet = None

        nodes = []
        for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE,
                                NODES_LINE + 1, NODES_COLUMN):
            nodes.append(Node(**node))
        expected_node_types = {'ROADM', 'ILA', 'FUSED'}
        for n in nodes:
            if n.node_type not in expected_node_types:
                n.node_type = 'ILA'

        links = []
        for link in parse_sheet(links_sheet, link_headers, LINKS_LINE,
                                LINKS_LINE + 2, LINKS_COLUMN):
            links.append(Link(**link))

        eqpts = []
        if eqpt_sheet is not None:
            for eqpt in parse_sheet(eqpt_sheet, eqpt_headers, EQPTS_LINE,
                                    EQPTS_LINE + 2, EQPTS_COLUMN):
                eqpts.append(Eqpt(**eqpt))

        roadms = []
        if roadm_sheet is not None:
            for roadm in parse_sheet(roadm_sheet, roadm_headers, ROADMS_LINE,
                                     ROADMS_LINE + 2, ROADMS_COLUMN):
                roadms.append(Roadm(**roadm))

    # sanity check
    all_cities = Counter(n.city for n in nodes)
    if len(all_cities) != len(nodes):
        raise ValueError(f'Duplicate city: {all_cities}')
    bad_links = []
    for lnk in links:
        if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
            bad_links.append([lnk.from_city, lnk.to_city])

    if bad_links:
        raise NetworkTopologyError(
            f'{ansi_escapes.red}XLS error:{ansi_escapes.reset} '
            f'The {ansi_escapes.cyan}Links{ansi_escapes.reset} sheet references nodes that '
            f'are not defined in the {ansi_escapes.cyan}Nodes{ansi_escapes.reset} sheet:\n'
            + _format_items(f'{item[0]} -> {item[1]}' for item in bad_links))

    return nodes, links, eqpts, roadms
Example #13
0
def set_egress_amplifier(network, this_node, equipment, pref_ch_db,
                         pref_total_db):
    """ this node can be a transceiver or a ROADM (same function called in both cases)
    """
    power_mode = equipment['Span']['default'].power_mode
    next_oms = (n for n in network.successors(this_node)
                if not isinstance(n, elements.Transceiver))
    this_node_degree = {
        k: v
        for k, v in this_node.per_degree_pch_out_db.items()
    } if hasattr(this_node, 'per_degree_pch_out_db') else {}
    for oms in next_oms:
        # go through all the OMS departing from the ROADM
        prev_node = this_node
        node = oms
        # if isinstance(next_node, elements.Fused): #support ROADM wo egress amp for metro applications
        #     node = find_last_node(next_node)
        #     next_node = next(n for n in network.successors(node))
        #     next_node = find_last_node(next_node)
        if node.uid not in this_node_degree:
            # if no target power is defined on this degree or no per degree target power is given use the global one
            # if target_pch_out_db  is not an attribute, then the element must be a transceiver
            this_node_degree[node.uid] = getattr(this_node.params,
                                                 'target_pch_out_db', 0)
        # use the target power on this degree
        prev_dp = this_node_degree[node.uid] - pref_ch_db
        dp = prev_dp
        prev_voa = 0
        voa = 0
        visited_nodes = []
        while not (isinstance(node, elements.Roadm)
                   or isinstance(node, elements.Transceiver)):
            # go through all nodes in the OMS (loop until next Roadm instance)
            try:
                next_node = next(network.successors(node))
            except StopIteration:
                raise NetworkTopologyError(
                    f'{type(node).__name__} {node.uid} is not properly connected, please check network topology'
                )
            visited_nodes.append(node)
            if next_node in visited_nodes:
                raise NetworkTopologyError(
                    f'Loop detected for {type(node).__name__} {node.uid}, please check network topology'
                )
            if isinstance(node, elements.Edfa):
                node_loss = span_loss(network, prev_node)
                voa = node.out_voa if node.out_voa else 0
                if node.delta_p is None:
                    dp = target_power(network, next_node, equipment) + voa
                else:
                    dp = node.delta_p
                if node.effective_gain is None or power_mode:
                    gain_target = node_loss + dp - prev_dp + prev_voa
                else:  # gain mode with effective_gain
                    gain_target = node.effective_gain
                    dp = prev_dp - node_loss - prev_voa + gain_target

                power_target = pref_total_db + dp

                if isinstance(prev_node, elements.Fiber):
                    max_fiber_lineic_loss_for_raman = \
                        equipment['Span']['default'].max_fiber_lineic_loss_for_raman * 1e-3  # dB/m
                    raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
                else:
                    raman_allowed = False

                if node.params.type_variety == '':
                    if node.variety_list and isinstance(
                            node.variety_list, list):
                        restrictions = node.variety_list
                    elif isinstance(
                            prev_node, elements.Roadm
                    ) and prev_node.restrictions['booster_variety_list']:
                        # implementation of restrictions on roadm boosters
                        restrictions = prev_node.restrictions[
                            'booster_variety_list']
                    elif isinstance(
                            next_node, elements.Roadm
                    ) and next_node.restrictions['preamp_variety_list']:
                        # implementation of restrictions on roadm preamp
                        restrictions = next_node.restrictions[
                            'preamp_variety_list']
                    else:
                        restrictions = None
                    edfa_variety, power_reduction = select_edfa(
                        raman_allowed, gain_target, power_target, equipment,
                        node.uid, restrictions)
                    extra_params = equipment['Edfa'][edfa_variety]
                    node.params.update_params(extra_params.__dict__)
                    dp += power_reduction
                    gain_target += power_reduction
                else:
                    if node.params.raman and not raman_allowed:
                        if isinstance(prev_node, elements.Fiber):
                            print(
                                f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: raman is used in node {node.uid}\n '
                                'but fiber lineic loss is above threshold\n')
                        else:
                            print(
                                f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: raman is used in node {node.uid}\n '
                                'but previous node is not a fiber\n')
                    # if variety is imposed by user, and if the gain_target (computed or imposed) is also above
                    # variety max gain + extended range, then warn that gain > max_gain + extended range
                    if gain_target - equipment['Edfa'][node.params.type_variety].gain_flatmax - \
                            equipment['Span']['default'].target_extended_gain > 1e-2:
                        # 1e-2 to allow a small margin according to round2float min step
                        print(
                            f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: '
                            f'WARNING: effective gain in Node {node.uid} is above user '
                            f'specified amplifier {node.params.type_variety}\n'
                            f'max flat gain: {equipment["Edfa"][node.params.type_variety].gain_flatmax}dB ; '
                            f'required gain: {gain_target}dB. Please check amplifier type.'
                        )

                node.delta_p = dp if power_mode else None
                node.effective_gain = gain_target
                set_amplifier_voa(node, power_target, power_mode)

            prev_dp = dp
            prev_voa = voa
            prev_node = node
            node = next_node
            # print(f'{node.uid}')

    if isinstance(this_node, elements.Roadm):
        this_node.per_degree_pch_out_db = {
            k: v
            for k, v in this_node_degree.items()
        }
Example #14
0
def set_egress_amplifier(network, this_node, equipment, pref_ch_db,
                         pref_total_db):
    """ this node can be a transceiver or a ROADM (same function called in both cases)
    """
    power_mode = equipment['Span']['default'].power_mode
    next_oms = (n for n in network.successors(this_node)
                if not isinstance(n, elements.Transceiver))
    this_node_degree = {
        k: v
        for k, v in this_node.per_degree_pch_out_db.items()
    } if hasattr(this_node, 'per_degree_pch_out_db') else {}
    for oms in next_oms:
        # go through all the OMS departing from the ROADM
        prev_node = this_node
        node = oms
        # if isinstance(next_node, elements.Fused): #support ROADM wo egress amp for metro applications
        #     node = find_last_node(next_node)
        #     next_node = next(n for n in network.successors(node))
        #     next_node = find_last_node(next_node)
        if this_node_degree:
            # find the target power on this degree
            if node.uid in this_node_degree.keys():
                prev_dp = this_node_degree[node.uid] - pref_ch_db
            else:
                # if no target power is defined on this degree use the global one
                # if target_pch_out_db  is not an attribute, then the element must be a transceiver
                prev_dp = getattr(this_node.params, 'target_pch_out_db',
                                  0) - pref_ch_db
                this_node_degree[node.uid] = prev_dp
        else:
            # if no per degree target power is given use the global one
            # if target_pch_out_db  is not an attribute, then the element must be a transceiver
            prev_dp = getattr(this_node.params, 'target_pch_out_db',
                              0) - pref_ch_db
            this_node_degree[node.uid] = prev_dp
        dp = prev_dp
        prev_voa = 0
        voa = 0
        visited_nodes = []
        while not (isinstance(node, elements.Roadm)
                   or isinstance(node, elements.Transceiver)):
            # go through all nodes in the OMS (loop until next Roadm instance)
            try:
                next_node = next(network.successors(node))
            except StopIteration:
                raise NetworkTopologyError(
                    f'{type(node).__name__} {node.uid} is not properly connected, please check network topology'
                )
            visited_nodes.append(node)
            if next_node in visited_nodes:
                raise NetworkTopologyError(
                    f'Loop detected for {type(node).__name__} {node.uid}, please check network topology'
                )
            if isinstance(node, elements.Edfa):
                node_loss = span_loss(network, prev_node)
                voa = node.out_voa if node.out_voa else 0
                if node.delta_p is None:
                    dp = target_power(network, next_node, equipment)
                else:
                    dp = node.delta_p
                if node.effective_gain is None or power_mode:
                    gain_target = node_loss + dp - prev_dp + prev_voa
                else:  # gain mode with effective_gain
                    gain_target = node.effective_gain
                    dp = prev_dp - node_loss - prev_voa + gain_target

                power_target = pref_total_db + dp

                if isinstance(prev_node, elements.Fiber):
                    max_fiber_lineic_loss_for_raman = \
                        equipment['Span']['default'].max_fiber_lineic_loss_for_raman
                    raman_allowed = prev_node.params.loss_coef < max_fiber_lineic_loss_for_raman
                else:
                    raman_allowed = False

                # implementation of restrictions on roadm boosters
                if isinstance(prev_node, elements.Roadm):
                    if prev_node.restrictions['booster_variety_list']:
                        restrictions = prev_node.restrictions[
                            'booster_variety_list']
                    else:
                        restrictions = None
                elif isinstance(next_node, elements.Roadm):
                    # implementation of restrictions on roadm preamp
                    if next_node.restrictions['preamp_variety_list']:
                        restrictions = next_node.restrictions[
                            'preamp_variety_list']
                    else:
                        restrictions = None
                else:
                    restrictions = None

                if node.params.type_variety == '':
                    edfa_variety, power_reduction = select_edfa(
                        raman_allowed, gain_target, power_target, equipment,
                        node.uid, restrictions)
                    extra_params = equipment['Edfa'][edfa_variety]
                    node.params.update_params(extra_params.__dict__)
                    dp += power_reduction
                    gain_target += power_reduction
                elif node.params.raman and not raman_allowed:
                    print(
                        f'{ansi_escapes.red}WARNING{ansi_escapes.reset}: raman is used in node {node.uid}\n but fiber lineic loss is above threshold\n'
                    )

                node.delta_p = dp if power_mode else None
                node.effective_gain = gain_target
                set_amplifier_voa(node, power_target, power_mode)

            prev_dp = dp
            prev_voa = voa
            prev_node = node
            node = next_node
            # print(f'{node.uid}')

    if isinstance(this_node, elements.Roadm):
        this_node.per_degree_pch_out_db = {
            k: v
            for k, v in this_node_degree.items()
        }
Example #15
0
def parse_excel(input_filename):
    link_headers = {
        'Node A': 'from_city',
        'Node Z': 'to_city',
        'east': {
            'Distance (km)': 'east_distance',
            'Fiber type': 'east_fiber',
            'lineic att': 'east_lineic',
            'Con_in': 'east_con_in',
            'Con_out': 'east_con_out',
            'PMD': 'east_pmd',
            'Cable id': 'east_cable'
        },
        'west': {
            'Distance (km)': 'west_distance',
            'Fiber type': 'west_fiber',
            'lineic att': 'west_lineic',
            'Con_in': 'west_con_in',
            'Con_out': 'west_con_out',
            'PMD': 'west_pmd',
            'Cable id': 'west_cable'
        }
    }
    node_headers = {
        'City': 'city',
        'State': 'state',
        'Country': 'country',
        'Region': 'region',
        'Latitude': 'latitude',
        'Longitude': 'longitude',
        'Type': 'node_type',
        'Booster_restriction': 'booster_restriction',
        'Preamp_restriction': 'preamp_restriction'
    }
    eqpt_headers = {
        'Node A': 'from_city',
        'Node Z': 'to_city',
        'east': {
            'amp type': 'east_amp_type',
            'att_in': 'east_att_in',
            'amp gain': 'east_amp_gain',
            'delta p': 'east_amp_dp',
            'tilt': 'east_tilt',
            'att_out': 'east_att_out'
        },
        'west': {
            'amp type': 'west_amp_type',
            'att_in': 'west_att_in',
            'amp gain': 'west_amp_gain',
            'delta p': 'west_amp_dp',
            'tilt': 'west_tilt',
            'att_out': 'west_att_out'
        }
    }

    with open_workbook(input_filename) as wb:
        nodes_sheet = wb.sheet_by_name('Nodes')
        links_sheet = wb.sheet_by_name('Links')
        try:
            eqpt_sheet = wb.sheet_by_name('Eqpt')
        except Exception:
            # eqpt_sheet is optional
            eqpt_sheet = None

        nodes = []
        for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE,
                                NODES_LINE + 1, NODES_COLUMN):
            nodes.append(Node(**node))
        expected_node_types = {'ROADM', 'ILA', 'FUSED'}
        for n in nodes:
            if n.node_type not in expected_node_types:
                n.node_type = 'ILA'

        links = []
        for link in parse_sheet(links_sheet, link_headers, LINKS_LINE,
                                LINKS_LINE + 2, LINKS_COLUMN):
            links.append(Link(**link))

        eqpts = []
        if eqpt_sheet is not None:
            for eqpt in parse_sheet(eqpt_sheet, eqpt_headers, EQPTS_LINE,
                                    EQPTS_LINE + 2, EQPTS_COLUMN):
                eqpts.append(Eqpt(**eqpt))

    # sanity check
    all_cities = Counter(n.city for n in nodes)
    if len(all_cities) != len(nodes):
        raise ValueError(f'Duplicate city: {all_cities}')
    bad_links = []
    for lnk in links:
        if lnk.from_city not in all_cities or lnk.to_city not in all_cities:
            bad_links.append([lnk.from_city, lnk.to_city])
    if bad_links:
        raise NetworkTopologyError(f'Bad link(s): {bad_links}.')

    return nodes, links, eqpts