From 833fe006af2de9d246754b66385ec9daf48b27cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Mon, 22 Oct 2018 14:19:52 +0200 Subject: [PATCH 001/108] Update README.rst --- README.rst | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 54f478102..7726883da 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ and optimization tools in real-world mesh optical networks.** `gnpy `__ is: -- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_. +- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_ - fully community-driven, fully open source library - driven by a consortium of operators, vendors, and academic researchers - intended for rapid development of production-grade route planning tools @@ -118,8 +118,8 @@ By default, this script operates on a single span network defined in `examples/edfa_example_network.json `_ You can specify a different network at the command line as follows. For -example, to use the CORONET Continental US (CONUS) network defined in -`examples/coronet_conus_example.json `_: +example, to use the CORONET Global network defined in +`examples/CORONET_Global_Topology.json `_: .. code-block:: shell @@ -133,10 +133,10 @@ further instructions on how to prepare the Excel input file, see `Excel_userguide.rst `_. The main transmission example will calculate the average signal OSNR and SNR -across 93 network elements (transceiver, ROADMs, fibers, and amplifiers) -between two transceivers selected by the user. (By default, for the CORONET US -network, it will show the transmission of spectral information between Abilene, -Texas and Albany, New York.) +across network elements (transceiver, ROADMs, fibers, and amplifiers) +between two transceivers selected by the user. (By default, for the CORONET Global +network, it will show the transmission of spectral information between Albuquerque, +New Mexico and Atlanta, Georgia.) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. @@ -194,7 +194,7 @@ The fiber library currently describes SSMF but additional fiber types can be ent +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the amplifier in the| +| `type_variety` | (string) | a unique name to ID the fiber in the | | | | JSON or Excel template topology input | | | | file | +----------------------+-----------+-----------------------------------------+ @@ -211,7 +211,7 @@ path_request_run.py routine. +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the amplifier in | +| `type_variety` | (string) | a unique name to ID the transceiver in | | | | the JSON or Excel template topology | | | | input file | +----------------------+-----------+-----------------------------------------+ @@ -237,7 +237,7 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `bit_rate` | (number) | in bit/s | +----------------------+-----------+-----------------------------------------+ -| `roll_off` | (number) | | +| `roll_off` | (number) | Not used. | +----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. @@ -254,8 +254,8 @@ For amplifiers defined in the topology JSON input but whose gain = 0 (placeholder), auto-design will set its gain automatically: see `power_mode` in the `Spans` library to find out how the gain is calculated. -Span configuration is performed as followws. It is not a list (which may change -in later releases,) and the user can only modify the value of existing +Span configuration is performed as follows. It is not a list (which may change +in later releases) and the user can only modify the value of existing parameters: +------------------------+-----------+---------------------------------------------+ @@ -453,11 +453,6 @@ dBm/channel. These are not yet parametrized but can be modified directly in the script (via the SpectralInformation structure) to accomodate any baud rate, spacing, power or channel count demand. -The amplifier's gain is set to exactly compensate for the loss in each network -element. The amplifier is currently defined with gain range of 15 dB to 25 dB -and 21 dBm max output power. Ripple and NF models are defined in -`examples/std_medium_gain_advanced_config.json `_ - Use `examples/path_requests_run.py `_ to run multiple optimizations as follows: .. code-block:: shell From fb49f7fb5da94bd363016a15025719017353bebf Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 14 Sep 2018 18:50:55 +0100 Subject: [PATCH 002/108] Implementing disjunction in path_requests_run.py - Implementing a conflict table for path disjoint choices - adding disjunction in the parser and json (read and write) Signed-off-by: EstherLerouzic --- examples/convert_service_sheet.py | 182 ++++++++++++++++++++++++++++++ examples/path_requests_run.py | 177 ++++++++++++++++++++++++++--- gnpy/core/request.py | 44 +++++--- gnpy/core/service_sheet.py | 4 +- 4 files changed, 374 insertions(+), 33 deletions(-) diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py index 4fbe8881a..6c5523fb0 100644 --- a/examples/convert_service_sheet.py +++ b/examples/convert_service_sheet.py @@ -20,6 +20,188 @@ logger = getLogger(__name__) +parser = ArgumentParser() +parser.add_argument('workbook', nargs='?', type = Path , default='meshTopologyExampleV2.xls') +parser.add_argument('-v', '--verbose', action='count') +parser.add_argument('-o', '--output', default=None) + +# Type for input data +class Request(namedtuple('Request', 'request_id source destination trx_type mode \ + spacing power nb_channel disjoint_from nodes_list is_loose')): + def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''): + return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose) + +# Type for output data: // from dutc +class Element: + def __eq__(self, other): + return type(self) == type(other) and self.uid == other.uid + def __hash__(self): + return hash((type(self), self.uid)) + +class Request_element(Element): + def __init__(self,Request,eqpt_filename): + # request_id is str + # excel has automatic number formatting that adds .0 on integer values + # the next lines recover the pure int value, assuming this .0 is unwanted + if not isinstance(Request.request_id,str): + value = str(int(Request.request_id)) + if value.endswith('.0'): + value = value[:-2] + self.request_id = value + else: + self.request_id = Request.request_id + self.source = Request.source + self.destination = Request.destination + self.srctpid = f'trx {Request.source}' + self.dsttpid = f'trx {Request.destination}' + # equipment already tests that trx_type belongs to eqpt_config.json + equipment = load_equipment(eqpt_filename) + self.trx_type = Request.trx_type + self.mode = Request.mode + # excel input are in GHz and dBm + self.spacing = Request.spacing * 1e9 + self.power = db2lin(Request.power) * 1e-3 + self.nb_channel = int(Request.nb_channel) + if not isinstance(Request.disjoint_from,str): + value = str(int(Request.disjoint_from)) + if value.endswith('.0'): + value = value[:-2] + else: + value = Request.disjoint_from + self.disjoint_from = [n for n in value.split()] + self.nodes_list = [] + if Request.nodes_list : + self.nodes_list = Request.nodes_list.split(' | ') + try : + self.nodes_list.remove(self.source) + msg = f'{self.source} removed from explicit path node-list' + logger.info(msg) + # print(msg) + except ValueError: + msg = f'{self.source} already removed from explicit path node-list' + logger.info(msg) + # print(msg) + try : + self.nodes_list.remove(self.destination) + msg = f'{self.destination} removed from explicit path node-list' + logger.info(msg) + # print(msg) + except ValueError: + msg = f'{self.destination} already removed from explicit path node-list' + logger.info(msg) + # print(msg) + + self.loose = 'loose' + if Request.is_loose == 'no' : + self.loose = 'strict' + + uid = property(lambda self: repr(self)) + @property + def pathrequest(self): + return { + 'request-id':self.request_id, + 'source': self.source, + 'destination': self.destination, + 'src-tp-id': self.srctpid, + 'dst-tp-id': self.dsttpid, + 'path-constraints':{ + 'te-bandwidth': { + 'technology': 'flexi-grid', + 'trx_type' : self.trx_type, + 'trx_mode' : self.mode, + 'effective-freq-slot':[{'n': 'null','m': 'null'}] , + 'spacing' : self.spacing, + 'max-nb-of-channel' : self.nb_channel, + 'output-power' : self.power + } + }, + 'optimizations': { + 'explicit-route-include-objects': [ + { + 'index': self.nodes_list.index(node), + 'unnumbered-hop':{ + 'node-id': f'{node}', + 'link-tp-id': 'link-tp-id is not used', + 'hop-type': 'loose', + 'direction': 'direction is not used' + }, + 'label-hop':{ + 'te-label': { + 'generic': 'generic is not used', + 'direction': 'direction is not used' + } + } + } + for node in self.nodes_list + ] + + } + } + @property + def pathsync(self): + if self.disjoint_from : + return {'synchronization-id':self.request_id, + 'svec': { + 'relaxable' : 'False', + 'link-diverse': 'True', + 'node-diverse': 'True', + 'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] + } + } + # TO-DO: avoid multiple entries with same synchronisation vectors + @property + def json(self): + return self.pathrequest , self.pathsync + +def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]): + service = parse_excel(input_filename) + req = [Request_element(n,eqpt_filename) for n in service] + # dumps the output into a json file with name + # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] + if output_filename=='': + output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' + # for debug + # print(json_filename) + data = { + 'path-request': [n.json[0] for n in req], + 'synchronization': [n.json[1] for n in req + if n.json[1] is not None] + } + with open(output_filename, 'w') as f: + f.write(dumps(data, indent=2)) + return data + +# to be used from dutc +def parse_row(row, fieldnames): + return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN]) + if r.ctype != XL_CELL_EMPTY} +# + +def parse_excel(input_filename): + with open_workbook(input_filename) as wb: + service_sheet = wb.sheet_by_name('Service') + services = list(parse_service_sheet(service_sheet)) + return services + +def parse_service_sheet(service_sheet): + logger.info(f'Validating headers on {service_sheet.name!r}') + header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]] + expected = ['route id', 'Source', 'Destination', 'TRX type', \ + 'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\ + 'routing: disjoint from', 'routing: path', 'routing: is loose?'] + if header != expected: + msg = f'Malformed header on Service sheet: {header} != {expected}' + logger.critical(msg) + raise ValueError(msg) + + service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split() + # Important Note: it reads all colum on each row so that + # it is not possible to write annotation in the excel sheet + # outside the SERVICES_COLUMN ... TO BE IMPROVED + # request_id should be unique for disjunction constraints (not used yet) + for row in all_rows(service_sheet, start=5): + yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames)) + if __name__ == '__main__': args = parser.parse_args() basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL)) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index c72e3c2ef..1752d2c10 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -19,15 +19,17 @@ from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO from json import dumps, loads from networkx import (draw_networkx_nodes, draw_networkx_edges, - draw_networkx_labels, dijkstra_path, NetworkXNoPath) + draw_networkx_labels, dijkstra_path, NetworkXNoPath, all_simple_paths) +from networkx.utils import pairwise from numpy import mean -from examples.convert_service_sheet import convert_service_sheet, Request_element, Element +from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element from gnpy.core.utils import load_json from gnpy.core.network import load_network, build_network, set_roadm_loss -from gnpy.core.equipment import load_equipment, trx_mode_params +from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db -from gnpy.core.request import Path_request, Result_element, compute_constrained_path, propagate, jsontocsv +from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, + propagate, jsontocsv, Disjunction) from copy import copy, deepcopy #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -46,7 +48,8 @@ def requests_from_json(json_data,equipment): requests_list = [] for req in json_data['path-request']: - #print(f'{req}') + # print(f'{req}') + # init all params from request params = {} params['request_id'] = req['request-id'] params['source'] = req['src-tp-id'] @@ -59,15 +62,26 @@ def requests_from_json(json_data,equipment): params['loose_list'] = [n['unnumbered-hop']['hop-type'] for n in nd_list] params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] + # recover trx physical param (baudrate, ...) from type and mode trx_params = trx_mode_params(equipment,params['trx_type'],params['trx_mode'],True) params.update(trx_params) params['power'] = req['path-constraints']['te-bandwidth']['output-power'] params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] - requests_list.append(Path_request(**params)) - return requests_list +def disjunctions_from_json(json_data): + disjunctions_list = [] + + for snc in json_data['synchronization']: + params = {} + params['relaxable'] = snc['svec']['relaxable'] + params['link_diverse'] = snc['svec']['link-diverse'] + params['node_diverse'] = snc['svec']['node-diverse'] + params['disjunctions_req'] = snc['svec']['request-id-number'] + disjunctions_list.append(Disjunction(**params)) + return disjunctions_list + def load_requests(filename,eqpt_filename): if filename.suffix.lower() == '.xls': @@ -114,6 +128,131 @@ def compute_path(network, equipment, pathreqlist): path_res_list.append(deepcopy(total_path)) return path_res_list +def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): + + path_res_list = [] + + # 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) + # todo : disjctn must be computed at once together to avoid blocking + # 1 1 + # eg a----b-----c + # |1 |0.5 |1 + # e----f--h--g + # 1 0.5 0.5 + # if I have to compute a to g and a to h + # I must not compute a-b-f-h-g, otherwise there is no disjoint path remaining for a to h + # instead I should list all most disjoint path and select the one that have the less + # number of commonalities + # \ path abfh aefh abcgh + # \___cost 2 2.5 3.5 + # path| cost + # abfhg| 2.5 x x x + # abcg | 3 x x + # aefhg| 3 x x x + # from this table abcg and aefh have no common links and should be preferred + # even they are not the shorpths path + # build the list of pathreqlist elements not concerned by disjunction + global_disjunctions_list = [e for d in disjunctions_list for e in d.disjunctions_req ] + pathreqlist_simple = [e for e in pathreqlist if e.request_id not in global_disjunctions_list] + pathreqlist_disjt = [e for e in pathreqlist if e.request_id in global_disjunctions_list] + # compute paths for this simple path + pathreslist_simple = compute_path(network, equipment, pathreqlist_simple) + + # build a conflict table where each path has a count for + # conflicts with the paths from the requests to be disjoint + # step 1 + # for each remaining request compute a set of simple path + rqs = {} + simple_rqs = {} + for pathreq in pathreqlist_disjt : + print(pathreq.request_id) + all_simp_pths = list(all_simple_paths(network,\ + source=next(el for el in network.nodes() if el.uid == pathreq.source),\ + target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + rqs[pathreq.request_id] = all_simp_pths + temp =[] + for p in all_simp_pths : + # build a short list representing each roadm+direction with the first item + # start enumeration at 1 to avoid Trx in the list + temp.append([e.uid for i,e in enumerate(p[1:-1]) \ + if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) + simple_rqs[pathreq.request_id] = temp + for p in all_simp_pths : + print ([e.uid for e in p if isinstance (e,Roadm)]) + tab = {} + tab2 = {} + # step 2 + # for each pair in the set of requests that need to be disjoint + # count the non disjoint cases tab[path] = list of disjoint path + for d in disjunctions_list : + print(d) + temp = d.disjunctions_req.copy() + for e1 in temp : + for i,p1 in enumerate(simple_rqs[e1]): + if temp: + for e2 in temp : + if e1 != e2 : + for j,p2 in enumerate(simple_rqs[e2]): + # print(f'{id(p1)} {id(p2)}') + try : + tab[id(p1)] += isdisjoint(p1,p2) + if isdisjoint(p1,p2)==0: + tab2[id(p1)] = tab2[id(p1)].append(p2) + print(f'{e1} is {isdisjoint(p1,p2)} {e2} tab : {tab2[id(p1),e2]}') + except KeyError: + tab[id(p1)] = isdisjoint(p1,p2) + if isdisjoint(p1,p2)==0: + tab2[id(p1)] = [p2] + try : + tab[id(p2)] += isdisjoint(p1,p2) + if isdisjoint(p1,p2)==0: + tab2[id(p2)] = tab2[id(p2)].append(p1) + except KeyError: + tab[id(p2)] = isdisjoint(p1,p2) + if isdisjoint(p1,p2)==0: + tab2[id(p2)] = [p1] + # remove the request from the list to avoid computind ij and ji cases + temp = temp.remove(e1) + + + # print(tab) + # print(len(tab)) + el = disjunctions_list[0].disjunctions_req[0] + # print(tab[id(simple_rqs[el][0])]) + + # now for each request, select the path that has the least nb of disjunction + # and completely disjoined from the already selected paths in the constraint + for pathreq in pathreqlist_disjt : + pths = [ tab[id(e)] for e in simple_rqs[pathreq.request_id]] + pths2 = [] + for p in simple_rqs[pathreq.request_id] : + print(f'{p} disjoint de {tab2[id(p)]}') + + i = pths.index(min(pths)) + print(simple_rqs[pathreq.request_id][i]) + print(pths) + print(pths2) + + +def isdisjoint(p1,p2) : + # returns 0 if disjoint + # TODO add reverse direction in the test + edge1 = list(pairwise(p1)) + edge2 = list(pairwise(p2)) + edge3 = list(pairwise(reversed(p2))) + for e in edge1 : + if (e in edge2) | (e in edge3) : + return 1 + return 0 + + def path_result_json(pathresult): data = { 'path': [n.json for n in pathresult] @@ -130,22 +269,26 @@ def path_result_json(pathresult): data = load_requests(args.service_filename,args.eqpt_filename) equipment = load_equipment(args.eqpt_filename) network = load_network(args.network_filename,equipment) - pths = requests_from_json(data, equipment) - print(pths) - test = compute_path(network, equipment, pths) + rqs = requests_from_json(data, equipment) + print('The following services have been requested:') + print(rqs) + pths = compute_path(network, equipment, rqs) + dsjn = disjunctions_from_json(data) + toto = compute_path_dsjctn(network, equipment, rqs,dsjn) + #TODO write results header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] data = [] data.append(header) - for i, p in enumerate(test): + for i, p in enumerate(pths): if p: - line = [f'{pths[i].source} to {pths[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ - f'{round(mean(p[-1].snr+lin2db(pths[i].baud_rate/(12.5e9))),2)}',\ - f'{pths[i].OSNR}'] + line = [f'{rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ + f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ + f'{rqs[i].OSNR}'] else: - line = [f'no path from {pths[i].source} to {pths[i].destination} '] + line = [f'no path from {rqs[i].source} to {rqs[i].destination} '] data.append(line) col_width = max(len(word) for row in data for word in row) # padding @@ -156,8 +299,8 @@ def path_result_json(pathresult): if args.output : result = [] - for p in test: - result.append(Result_element(pths[test.index(p)],p)) + for p in pths: + result.append(Result_element(rqs[pths.index(p)],p)) with open(args.output, 'w') as f: f.write(dumps(path_result_json(result), indent=2)) fnamecsv = next(s for s in args.output.split('.')) + '.csv' diff --git a/gnpy/core/request.py b/gnpy/core/request.py index c711e20a7..9717d5553 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -32,6 +32,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ ' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off') +DisjunctionParams = namedtuple('DisjunctionParams','relaxable link_diverse node_diverse disjunctions_req') class Path_request: def __init__(self, *args, **params): @@ -68,6 +69,26 @@ def __repr__(self): f'spacing:\t{self.spacing * 1e-9} GHz', f'power: \t{round(lin2db(self.power)+30,2)} dBm' '\n']) +class Disjunction: + def __init__(self, *args, **params): + params = DisjunctionParams(**params) + self.relaxable = params.relaxable + self.link_diverse = params.link_diverse + self.node_diverse = params.node_diverse + self.disjunctions_req = params.disjunctions_req + + def __str__(self): + return '\n\t'.join([f'relaxable: {self.relaxable}', + f'link-diverse: {self.link_diverse}', + f'node-diverse: {self.node_diverse}', + f'request-id-numbers: {self.disjunctions_req}'] + ) + def __repr__(self): + return '\n\t'.join([f'relaxable: {self.relaxable}', + f'link-diverse: {self.link_diverse}', + f'node-diverse: {self.node_diverse}', + f'request-id-numbers: {self.disjunctions_req}'] + ) class Result_element(Element): def __init__(self,path_request,computed_path): @@ -226,8 +247,10 @@ def compute_constrained_path(network, req): node = next(el for el in roadm if el.uid == f'roadm {n}') except StopIteration: try: - node = next(el for el in edfa - if el.uid.startswith(f'egress edfa in {n}')) + # TODO this test is not giving good results: full name of the + # amp is required to avoid ambiguity on the direction + node = next(el for el in edfa + if el.uid.find(f'{n}')) except StopIteration: msg = f'could not find node : {n} in network topology: \ not a trx, roadm, edfa or fused element' @@ -251,7 +274,7 @@ def compute_constrained_path(network, req): print(msg) total_path = [] -# preparing disjonction feature +# preparing disjunction feature # for p in all_simple_paths(network,\ # source=next(el for el in trx if el.uid == req.source),\ # target=next(el for el in trx if el.uid == req.destination)): @@ -297,17 +320,10 @@ def jsontocsv(json_data,equipment,fileout): ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') # find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format) - try: - [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] - for m in equipment['Transceiver'][tsp].mode if m['format']==mode) - - # for debug - # print(f'coucou {baud_rate}') - except IndexError: - msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library' - - raise ValueError(msg) - output_snr = next(e['accumulative-value'] + # loading equipment already tests the existence of tsp type and mode: + [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] + for m in equipment['Transceiver'][tsp].mode if m['format']==mode) + output_snr = next(e['accumulative-value'] for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') output_snrbandwidth = next(e['accumulative-value'] for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth') diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 1145bb01a..4af835c35 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -153,7 +153,7 @@ def pathrequest(self): @property def pathsync(self): if self.disjoint_from : - return {'synchonization-id':self.request_id, + return {'synchronization-id':self.request_id, 'svec': { 'relaxable' : 'False', 'link-diverse': 'True', @@ -177,7 +177,7 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil # print(json_filename) data = { 'path-request': [n.json[0] for n in req], - 'synchronisation': [n.json[1] for n in req + 'synchronization': [n.json[1] for n in req if n.json[1] is not None] } with open(output_filename, 'w') as f: From 7558721642550f558226439324dc74635903d19c Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 18 Sep 2018 18:03:35 +0100 Subject: [PATCH 003/108] Disjunction feature step 2 - selection of disjoint path set for each synchronization vector Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 140 ++++++++++++++++++++-------------- gnpy/core/request.py | 3 +- 2 files changed, 86 insertions(+), 57 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 1752d2c10..006e8c62d 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -75,6 +75,7 @@ def disjunctions_from_json(json_data): for snc in json_data['synchronization']: params = {} + params['disjunction_id'] = snc['synchronization-id'] params['relaxable'] = snc['svec']['relaxable'] params['link_diverse'] = snc['svec']['link-diverse'] params['node_diverse'] = snc['svec']['node-diverse'] @@ -171,12 +172,17 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): # for each remaining request compute a set of simple path rqs = {} simple_rqs = {} + simple_rqs_reversed = {} for pathreq in pathreqlist_disjt : print(pathreq.request_id) all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ target=next(el for el in network.nodes() if el.uid == pathreq.destination))) - rqs[pathreq.request_id] = all_simp_pths + # reversed direction paths required to check disjunction on both direction + all_simp_pths_reversed = [] + for pth in all_simp_pths: + all_simp_pths_reversed.append(find_reversed_path(pth,network)) + # rqs[pathreq.request_id] = all_simp_pths temp =[] for p in all_simp_pths : # build a short list representing each roadm+direction with the first item @@ -184,74 +190,96 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): temp.append([e.uid for i,e in enumerate(p[1:-1]) \ if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) simple_rqs[pathreq.request_id] = temp - for p in all_simp_pths : - print ([e.uid for e in p if isinstance (e,Roadm)]) - tab = {} - tab2 = {} + temp =[] + for p in all_simp_pths_reversed : + # build a short list representing each roadm+direction with the first item + # start enumeration at 1 to avoid Trx in the list + temp.append([e.uid for i,e in enumerate(p[1:-1]) \ + if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) + simple_rqs_reversed[pathreq.request_id] = temp # step 2 - # for each pair in the set of requests that need to be disjoint - # count the non disjoint cases tab[path] = list of disjoint path + # for each set of requests that need to be disjoint + # select the disjoint path combination + + candidates = {} for d in disjunctions_list : print(d) - temp = d.disjunctions_req.copy() - for e1 in temp : - for i,p1 in enumerate(simple_rqs[e1]): - if temp: - for e2 in temp : - if e1 != e2 : - for j,p2 in enumerate(simple_rqs[e2]): - # print(f'{id(p1)} {id(p2)}') - try : - tab[id(p1)] += isdisjoint(p1,p2) - if isdisjoint(p1,p2)==0: - tab2[id(p1)] = tab2[id(p1)].append(p2) - print(f'{e1} is {isdisjoint(p1,p2)} {e2} tab : {tab2[id(p1),e2]}') - except KeyError: - tab[id(p1)] = isdisjoint(p1,p2) - if isdisjoint(p1,p2)==0: - tab2[id(p1)] = [p2] - try : - tab[id(p2)] += isdisjoint(p1,p2) - if isdisjoint(p1,p2)==0: - tab2[id(p2)] = tab2[id(p2)].append(p1) - except KeyError: - tab[id(p2)] = isdisjoint(p1,p2) - if isdisjoint(p1,p2)==0: - tab2[id(p2)] = [p1] - # remove the request from the list to avoid computind ij and ji cases - temp = temp.remove(e1) - - - # print(tab) - # print(len(tab)) - el = disjunctions_list[0].disjunctions_req[0] - # print(tab[id(simple_rqs[el][0])]) - - # now for each request, select the path that has the least nb of disjunction - # and completely disjoined from the already selected paths in the constraint - for pathreq in pathreqlist_disjt : - pths = [ tab[id(e)] for e in simple_rqs[pathreq.request_id]] - pths2 = [] - for p in simple_rqs[pathreq.request_id] : - print(f'{p} disjoint de {tab2[id(p)]}') - - i = pths.index(min(pths)) - print(simple_rqs[pathreq.request_id][i]) - print(pths) - print(pths2) + dlist = d.disjunctions_req.copy() + # each line of dpath is one combination of path that satisfies disjunction + dpath = [] + for p in simple_rqs[dlist[0]]: + dpath.append([p]) + # in each loop, dpath is updated with a path for rq that satisfies + # disjunction with each path in dpath + # for example, assume set of disjunction_list is {rq1,rq2, rq3} + # rq1 p1: abcg + # p2: aefhg + # p3: abfhg + # rq2 p8: bf + # rq2 p4: abcgh + # p6: aefh + # p7: abfh + # initiate with rq1 + # dpath = [p1 + # p2 + # p3] + # after first loop: + # dpath = [p1 p8 + # p3 p8] + # since p2 and p8 are not disjoint + # after second loop: + # dpath = [ p1 P8 p6 ] + # since p1 and p4 are not disjoint + # p1 and p7 are not disjoint + # p3 and p4 are not disjoint + # p3 and p7 are not disjoint + + for e1 in dlist[1:] : + temp = [] + for j,p1 in enumerate(simple_rqs[e1]): + # can use index j in simple_rqs_reversed because index + # of direct and reversed paths have been kept identical + p1_reversed = simple_rqs_reversed[e1][j] + # print(p1_reversed) + # print('\n\n') + for c in dpath : + # print(f' c: \t{c}') + temp2 = c.copy() + for p in c : + if isdisjoint(p1,p)+ isdisjoint(p1_reversed,p)==0 : + temp2.append(p1) + temp.append(temp2) + # print(f' coucou {e1}: \t{temp}') + dpath = temp + # print(f' coucou : \t{temp}') + # print(dpath) + candidates[d.disjunction_id] = dpath + print( candidates) + + # now for each request, select the path that satisfies all disjunctions + # path must be in candidates[id] for all concerned ids def isdisjoint(p1,p2) : # returns 0 if disjoint - # TODO add reverse direction in the test edge1 = list(pairwise(p1)) edge2 = list(pairwise(p2)) - edge3 = list(pairwise(reversed(p2))) for e in edge1 : - if (e in edge2) | (e in edge3) : + if e in edge2 : return 1 return 0 +def find_reversed_path(p,network) : + # select of intermediate roadms and find the path between them + reversed_roadm_path = list(reversed([e for e in p if isinstance (e,Roadm)])) + source = p[-1] + destination = p[0] + total_path = [source] + for node in reversed_roadm_path : + total_path.extend(dijkstra_path(network, source, node)[1:]) + source = node + total_path.append(destination) + return total_path def path_result_json(pathresult): data = { diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 9717d5553..577e9f636 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -32,7 +32,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ ' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off') -DisjunctionParams = namedtuple('DisjunctionParams','relaxable link_diverse node_diverse disjunctions_req') +DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: def __init__(self, *args, **params): @@ -72,6 +72,7 @@ def __repr__(self): class Disjunction: def __init__(self, *args, **params): params = DisjunctionParams(**params) + self.disjunction_id = params.disjunction_id self.relaxable = params.relaxable self.link_diverse = params.link_diverse self.node_diverse = params.node_diverse From 44312125abf37c7a34111c673755c423cadad8f5 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 21 Sep 2018 18:37:46 +0100 Subject: [PATCH 004/108] Disjunction feature step 3 - select the first path that is satisfying disjunction not finished: - constraints on nodes not taken into account - modification on the build network not added - not clean, need some refactoring Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 179 ++++++++++++++++++++++++++++------ 1 file changed, 147 insertions(+), 32 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 006e8c62d..c8548777e 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -129,18 +129,45 @@ def compute_path(network, equipment, pathreqlist): path_res_list.append(deepcopy(total_path)) return path_res_list -def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): +def compute_path_2(network, equipment, pathreqlist, pathlist): path_res_list = [] - # 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 + for i,pathreq in enumerate(pathreqlist): + #need to rebuid the network for each path because the total power + #can be different and the choice of amplifiers in autodesign is power dependant + #but the design is the same if the total power is the same + #TODO parametrize the total spectrum power so the same design can be shared + p_db = lin2db(pathreq.power*1e3) + p_total_db = p_db + lin2db(pathreq.nb_channel) + pathreq.nodes_list.append(pathreq.destination) + #we assume that the destination is a strict constraint + total_path = pathlist[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 total_path : + total_path = propagate(total_path,pathreq,equipment, show=False) + else: + total_path = [] + # we record the last tranceiver object in order to have th whole + # information about spectrum. Important Note: since transceivers + # attached to roadms are actually logical elements to simulate + # performance, several demands having the same destination may use + # the same transponder for the performance simaulation. This is why + # we use deepcopy: to ensure each propagation is recorded and not + # overwritten + + path_res_list.append(deepcopy(total_path)) + return path_res_list + + +def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): - 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) + # need to return list + path_res_list = [] + + # todo : disjctn must be computed at once together to avoid blocking # 1 1 # eg a----b-----c @@ -163,13 +190,18 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): global_disjunctions_list = [e for d in disjunctions_list for e in d.disjunctions_req ] pathreqlist_simple = [e for e in pathreqlist if e.request_id not in global_disjunctions_list] pathreqlist_disjt = [e for e in pathreqlist if e.request_id in global_disjunctions_list] - # compute paths for this simple path - pathreslist_simple = compute_path(network, equipment, pathreqlist_simple) + + class Pth: + def __init__(self, req, pth, simplepth): + self.req = req + self.pth = pth + self.simplepth = simplepth # build a conflict table where each path has a count for # conflicts with the paths from the requests to be disjoint # step 1 # for each remaining request compute a set of simple path + allpaths = {} rqs = {} simple_rqs = {} simple_rqs_reversed = {} @@ -178,17 +210,21 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + # sort them + all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) # reversed direction paths required to check disjunction on both direction all_simp_pths_reversed = [] for pth in all_simp_pths: all_simp_pths_reversed.append(find_reversed_path(pth,network)) - # rqs[pathreq.request_id] = all_simp_pths + rqs[pathreq.request_id] = all_simp_pths temp =[] for p in all_simp_pths : # build a short list representing each roadm+direction with the first item # start enumeration at 1 to avoid Trx in the list - temp.append([e.uid for i,e in enumerate(p[1:-1]) \ - if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) + s = [e.uid for i,e in enumerate(p[1:-1]) \ + if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] + temp.append(s) + allpaths[id(s)] = Pth(pathreq,p,s) simple_rqs[pathreq.request_id] = temp temp =[] for p in all_simp_pths_reversed : @@ -201,34 +237,36 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): # for each set of requests that need to be disjoint # select the disjoint path combination + candidates = {} for d in disjunctions_list : print(d) dlist = d.disjunctions_req.copy() # each line of dpath is one combination of path that satisfies disjunction dpath = [] - for p in simple_rqs[dlist[0]]: + for i,p in enumerate(simple_rqs[dlist[0]]): dpath.append([p]) + allpaths[id(p)].d_id = d.disjunction_id # in each loop, dpath is updated with a path for rq that satisfies # disjunction with each path in dpath - # for example, assume set of disjunction_list is {rq1,rq2, rq3} - # rq1 p1: abcg + # for example, assume set of requests in the vector (disjunction_list) is {rq1,rq2, rq3} + # rq1 p1: abfhg # p2: aefhg - # p3: abfhg + # p3: abcg # rq2 p8: bf - # rq2 p4: abcgh + # rq3 p4: abcgh # p6: aefh # p7: abfh # initiate with rq1 - # dpath = [p1 - # p2 - # p3] + # dpath = [[p1] + # [p2] + # [p3]] # after first loop: - # dpath = [p1 p8 - # p3 p8] + # dpath = [[p1 p8] + # [p3 p8]] # since p2 and p8 are not disjoint # after second loop: - # dpath = [ p1 P8 p6 ] + # dpath = [ p3 p8 p6 ] # since p1 and p4 are not disjoint # p1 and p7 are not disjoint # p3 and p4 are not disjoint @@ -237,28 +275,93 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): for e1 in dlist[1:] : temp = [] for j,p1 in enumerate(simple_rqs[e1]): + allpaths[id(p1)].d_id = d.disjunction_id # can use index j in simple_rqs_reversed because index # of direct and reversed paths have been kept identical p1_reversed = simple_rqs_reversed[e1][j] # print(p1_reversed) # print('\n\n') - for c in dpath : + for k,c in enumerate(dpath) : # print(f' c: \t{c}') temp2 = c.copy() + all_disjoint = 0 for p in c : - if isdisjoint(p1,p)+ isdisjoint(p1_reversed,p)==0 : - temp2.append(p1) - temp.append(temp2) + all_disjoint += isdisjoint(p1,p)+ isdisjoint(p1_reversed,p) + if all_disjoint ==0: + temp2.append(p1) + temp.append(temp2) # print(f' coucou {e1}: \t{temp}') dpath = temp - # print(f' coucou : \t{temp}') # print(dpath) candidates[d.disjunction_id] = dpath - print( candidates) + # for i in disjunctions_list : + # print(f'\n{candidates[i.disjunction_id]}') + + # step 3 # now for each request, select the path that satisfies all disjunctions # path must be in candidates[id] for all concerned ids - + # for example, assume set of sync vectors (disjunction groups) is + # s1 = {rq1 rq2} s2 = {rq1 rq3} + # candidate[s1] = [[p1 p8] + # [p3 p8]] + # candidate[s2] = [[p3 p6]] + # for rq1 p3 should be preferred + + + for pathreq in pathreqlist_disjt: + concerned_d_id = [d.disjunction_id for d in disjunctions_list if pathreq.request_id in d.disjunctions_req] + # for each set of solution, verify that the same path is used + candidate_paths = simple_rqs[pathreq.request_id] + for p in candidate_paths : + iscandidate = 0 + for sol in concerned_d_id : + test = 1 + # for each solution test if p is part of the solution + # if yes, then p can remain a candidate + for m in candidates[sol] : + if p in m: + test = 0 + break + iscandidate += test + if iscandidate != 0: + for l in concerned_d_id : + for m in candidates[l] : + if p in m : + candidates[l].remove(m) + + # for i in disjunctions_list : + # print(f'\n{candidates[i.disjunction_id]}') + + # step 3 select the first combination that works + pathreslist_disjoint = {} + for d in disjunctions_list : + sol = next(s for s in candidates[d.disjunction_id]) + # print(f'\n\n\n{sol}') + if sol : + for p in sol: + print(id(p)) + if allpaths[id(p)].req in pathreqlist_disjt: + pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth + pathreqlist_disjt.remove(allpaths[id(p)].req) + else: + msg = f'No disjoint path found' + print(msg) + for req in pathreqlist : + if req in pathreqlist_simple: + # path_res_list.append([e.uid for e in compute_constrained_path(network, req) + # if isinstance(e,Roadm) ]) + print('cucou') + req.nodes_list.append(req.destination) + #we assume that the destination is a strict constraint + req.loose_list.append('strict') + path_res_list.append(compute_constrained_path(network, req)) + print([e.uid for e in compute_constrained_path(network, req)]) + else: + print("---") + path_res_list.append(pathreslist_disjoint[req]) + print([e.uid for e in pathreslist_disjoint[req]]) + return path_res_list def isdisjoint(p1,p2) : # returns 0 if disjoint @@ -298,12 +401,24 @@ def path_result_json(pathresult): equipment = load_equipment(args.eqpt_filename) network = load_network(args.network_filename,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) print('The following services have been requested:') print(rqs) - pths = compute_path(network, equipment, rqs) + # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) toto = compute_path_dsjctn(network, equipment, rqs,dsjn) + pths = compute_path_2(network, equipment, rqs, toto) + print('toto') + #print(toto) #TODO write results From d99e8ca56536fd4c098d0fd671177510cebabb99 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 25 Sep 2018 14:25:53 +0100 Subject: [PATCH 005/108] routing constraint added on top of disjunction feature : step 4 select disjoint routes that satisfy route constraint Signed-off-by: EstherLerouzic --- examples/convert_service_sheet.py | 29 +++---- examples/path_requests_run.py | 126 +++++++++++++++++++++++------- 2 files changed, 109 insertions(+), 46 deletions(-) diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py index 6c5523fb0..fcd7c05f9 100644 --- a/examples/convert_service_sheet.py +++ b/examples/convert_service_sheet.py @@ -72,25 +72,16 @@ def __init__(self,Request,eqpt_filename): self.nodes_list = [] if Request.nodes_list : self.nodes_list = Request.nodes_list.split(' | ') - try : - self.nodes_list.remove(self.source) - msg = f'{self.source} removed from explicit path node-list' - logger.info(msg) - # print(msg) - except ValueError: - msg = f'{self.source} already removed from explicit path node-list' - logger.info(msg) - # print(msg) - try : - self.nodes_list.remove(self.destination) - msg = f'{self.destination} removed from explicit path node-list' - logger.info(msg) - # print(msg) - except ValueError: - msg = f'{self.destination} already removed from explicit path node-list' - logger.info(msg) - # print(msg) - + toberemoved = [self.source, self.destination, self.srctpid, self.dsttpid] + for n in toberemoved: + try : + self.nodes_list.remove(n) + msg = f'{n} removed from explicit path node-list' + logger.info(msg) + # print(msg) + except ValueError: + pass + # print(msg) self.loose = 'loose' if Request.is_loose == 'no' : self.loose = 'strict' diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index c8548777e..38f276375 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -112,8 +112,7 @@ def compute_path(network, equipment, pathreqlist): print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output total_path = compute_constrained_path(network, pathreq) 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 total_path : total_path = propagate(total_path,pathreq,equipment, show=False) else: @@ -132,16 +131,24 @@ def compute_path(network, equipment, pathreqlist): def compute_path_2(network, equipment, pathreqlist, pathlist): path_res_list = [] - + # # 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) + # TODO : get the designed power to set it when it is not an input + # pathreq.power to be adapted for i,pathreq in enumerate(pathreqlist): - #need to rebuid the network for each path because the total power - #can be different and the choice of amplifiers in autodesign is power dependant - #but the design is the same if the total power is the same - #TODO parametrize the total spectrum power so the same design can be shared + p_db = lin2db(pathreq.power*1e3) p_total_db = p_db + lin2db(pathreq.nb_channel) - pathreq.nodes_list.append(pathreq.destination) - #we assume that the destination is a strict constraint + print(f'request {pathreq.request_id}') + print(f'Computing path from {pathreq.source} to {pathreq.destination}') + print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output + total_path = pathlist[i] print(f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n') # for debug @@ -161,14 +168,34 @@ def compute_path_2(network, equipment, pathreqlist, pathlist): path_res_list.append(deepcopy(total_path)) return path_res_list +def correct_route_list(network, pathreqlist): + + roadms = [n.uid for n in network.nodes() if isinstance(n, Roadm)] + for pathreq in pathreqlist: + for i,n_id in enumerate(pathreq.nodes_list): + # replace possibly wrong name with a formated roadm name + if n_id not in [n.uid for n in network.nodes()]: + nodes_suggestion = [uid for uid in roadms \ + if n_id.lower() in uid.lower()] + if len(nodes_suggestion)>0 : + new_n = nodes_suggestion[0] + print(f'invalid route node specified:\ + \n\'{n_id}\', replaced with \'{new_n}\'') + pathreq.nodes_list[i] = new_n + else: + print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') + print(pathreq.nodes_list) + pathreq.nodes_list.remove(n_id) + pathreq.loose_list.pop(i) + # remove endpoints from this list in case they were added by the user in the xls or json files + return pathreqlist def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): # need to return list path_res_list = [] - - # todo : disjctn must be computed at once together to avoid blocking + # all disjctn must be computed at once together to avoid blocking # 1 1 # eg a----b-----c # |1 |0.5 |1 @@ -185,12 +212,14 @@ def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): # abcg | 3 x x # aefhg| 3 x x x # from this table abcg and aefh have no common links and should be preferred - # even they are not the shorpths path + # even they are not the shortest paths + # build the list of pathreqlist elements not concerned by disjunction global_disjunctions_list = [e for d in disjunctions_list for e in d.disjunctions_req ] pathreqlist_simple = [e for e in pathreqlist if e.request_id not in global_disjunctions_list] pathreqlist_disjt = [e for e in pathreqlist if e.request_id in global_disjunctions_list] + # use a mirror class to record path and the corresponding requests class Pth: def __init__(self, req, pth, simplepth): self.req = req @@ -206,10 +235,10 @@ def __init__(self, req, pth, simplepth): simple_rqs = {} simple_rqs_reversed = {} for pathreq in pathreqlist_disjt : - print(pathreq.request_id) all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + print(f'source {pathreq.source} dest {pathreq.destination}') # sort them all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) # reversed direction paths required to check disjunction on both direction @@ -224,6 +253,7 @@ def __init__(self, req, pth, simplepth): s = [e.uid for i,e in enumerate(p[1:-1]) \ if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] temp.append(s) + # attention il peu y avoir plusieur req avec le meme id(s): à corriger allpaths[id(s)] = Pth(pathreq,p,s) simple_rqs[pathreq.request_id] = temp temp =[] @@ -237,7 +267,6 @@ def __init__(self, req, pth, simplepth): # for each set of requests that need to be disjoint # select the disjoint path combination - candidates = {} for d in disjunctions_list : print(d) @@ -311,7 +340,7 @@ def __init__(self, req, pth, simplepth): for pathreq in pathreqlist_disjt: concerned_d_id = [d.disjunction_id for d in disjunctions_list if pathreq.request_id in d.disjunctions_req] - # for each set of solution, verify that the same path is used + # for each set of solution, verify that the same path is used for the same request candidate_paths = simple_rqs[pathreq.request_id] for p in candidate_paths : iscandidate = 0 @@ -332,35 +361,62 @@ def __init__(self, req, pth, simplepth): # for i in disjunctions_list : # print(f'\n{candidates[i.disjunction_id]}') - - # step 3 select the first combination that works + # step 4 apply route constraints : remove candidate path that do not satisfy the constraint + # only in the case of disjounction: the simple path is processed in request.compute_constrained_path + for d in disjunctions_list : + print('a') + + for sol in candidates[d.disjunction_id] : + print('b') + for p in sol : + if allpaths[id(p)].req.nodes_list : + # if p does not containt the ordered list node, remove sol from the candidate + print(allpaths[id(p)].req.nodes_list) + print(p) + print('coucou') + if not ispart(allpaths[id(p)].req.nodes_list, p) : + if len(candidates[d.disjunction_id])>1 : + candidates[d.disjunction_id].remove(sol) + print('removing') + print(sol) + print(allpaths[id(p)].req.loose_list) + + break + else: + if 'loose' in allpaths[id(p)].req.loose_list: + pass + else : + candidates[d.disjunction_id].remove(sol) + print('removinglast solution from candidate paths') + print(sol) + break + # step 5 select the first combination that works pathreslist_disjoint = {} for d in disjunctions_list : sol = next(s for s in candidates[d.disjunction_id]) # print(f'\n\n\n{sol}') if sol : for p in sol: - print(id(p)) if allpaths[id(p)].req in pathreqlist_disjt: pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth pathreqlist_disjt.remove(allpaths[id(p)].req) else: msg = f'No disjoint path found' - print(msg) + logger.critical(msg) + + # list the results in the same order as initial pathreqlist for req in pathreqlist : if req in pathreqlist_simple: # path_res_list.append([e.uid for e in compute_constrained_path(network, req) # if isinstance(e,Roadm) ]) - print('cucou') req.nodes_list.append(req.destination) - #we assume that the destination is a strict constraint + # we assume that the destination is a strict constraint req.loose_list.append('strict') path_res_list.append(compute_constrained_path(network, req)) - print([e.uid for e in compute_constrained_path(network, req)]) + logger.info(f'{[e.uid for e in compute_constrained_path(network, req)]}') else: - print("---") path_res_list.append(pathreslist_disjoint[req]) - print([e.uid for e in pathreslist_disjoint[req]]) + logger.info(f'{[e.uid for e in pathreslist_disjoint[req]]}') return path_res_list def isdisjoint(p1,p2) : @@ -384,6 +440,18 @@ def find_reversed_path(p,network) : total_path.append(destination) return total_path +def ispart(a,b) : + j = 0 + for i, el in enumerate(a): + if el in b : + if b.index(el) > j : + j = b.index(el) + else: + return False + else: + return False + return True + def path_result_json(pathresult): data = { 'path': [n.json for n in pathresult] @@ -411,6 +479,12 @@ def path_result_json(pathresult): build_network(network, equipment, p_db, p_total_db) rqs = requests_from_json(data, equipment) + for req in rqs: + print(f'source {req.source} dest {req.destination}') + rqs = correct_route_list(network, rqs) + print('coucou\n') + for req in rqs: + print(f'source {req.source} dest {req.destination}') print('The following services have been requested:') print(rqs) # pths = compute_path(network, equipment, rqs) @@ -418,10 +492,8 @@ def path_result_json(pathresult): toto = compute_path_dsjctn(network, equipment, rqs,dsjn) pths = compute_path_2(network, equipment, rqs, toto) print('toto') - #print(toto) + # print(toto) - #TODO write results - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] data = [] data.append(header) From 9f37cb8ce645d28f767ce40eac859a475d9db8dd Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 26 Sep 2018 13:25:51 +0100 Subject: [PATCH 006/108] Corrections, cleanup and debugging Added (partly): - relax one constraint when no more path are available Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 136 +++++++++++++++++++--------------- gnpy/core/request.py | 7 +- 2 files changed, 78 insertions(+), 65 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 38f276375..558e72b60 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -184,7 +184,6 @@ def correct_route_list(network, pathreqlist): pathreq.nodes_list[i] = new_n else: print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') - print(pathreq.nodes_list) pathreq.nodes_list.remove(n_id) pathreq.loose_list.pop(i) # remove endpoints from this list in case they were added by the user in the xls or json files @@ -238,7 +237,6 @@ def __init__(self, req, pth, simplepth): all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ target=next(el for el in network.nodes() if el.uid == pathreq.destination))) - print(f'source {pathreq.source} dest {pathreq.destination}') # sort them all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) # reversed direction paths required to check disjunction on both direction @@ -253,7 +251,8 @@ def __init__(self, req, pth, simplepth): s = [e.uid for i,e in enumerate(p[1:-1]) \ if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] temp.append(s) - # attention il peu y avoir plusieur req avec le meme id(s): à corriger + # id(s) is unique even if path is the same: two objects with same + # path have two different ids allpaths[id(s)] = Pth(pathreq,p,s) simple_rqs[pathreq.request_id] = temp temp =[] @@ -269,13 +268,12 @@ def __init__(self, req, pth, simplepth): candidates = {} for d in disjunctions_list : - print(d) dlist = d.disjunctions_req.copy() # each line of dpath is one combination of path that satisfies disjunction dpath = [] for i,p in enumerate(simple_rqs[dlist[0]]): dpath.append([p]) - allpaths[id(p)].d_id = d.disjunction_id + # allpaths[id(p)].d_id = d.disjunction_id # in each loop, dpath is updated with a path for rq that satisfies # disjunction with each path in dpath # for example, assume set of requests in the vector (disjunction_list) is {rq1,rq2, rq3} @@ -304,7 +302,7 @@ def __init__(self, req, pth, simplepth): for e1 in dlist[1:] : temp = [] for j,p1 in enumerate(simple_rqs[e1]): - allpaths[id(p1)].d_id = d.disjunction_id + # allpaths[id(p1)].d_id = d.disjunction_id # can use index j in simple_rqs_reversed because index # of direct and reversed paths have been kept identical p1_reversed = simple_rqs_reversed[e1][j] @@ -342,16 +340,19 @@ def __init__(self, req, pth, simplepth): concerned_d_id = [d.disjunction_id for d in disjunctions_list if pathreq.request_id in d.disjunctions_req] # for each set of solution, verify that the same path is used for the same request candidate_paths = simple_rqs[pathreq.request_id] + # print('coucou') + # print(pathreq.request_id) for p in candidate_paths : iscandidate = 0 for sol in concerned_d_id : test = 1 # for each solution test if p is part of the solution # if yes, then p can remain a candidate - for m in candidates[sol] : + for i,m in enumerate(candidates[sol]) : if p in m: - test = 0 - break + if allpaths[id(m[m.index(p)])].req.request_id == pathreq.request_id : + test = 0 + break iscandidate += test if iscandidate != 0: for l in concerned_d_id : @@ -359,64 +360,74 @@ def __init__(self, req, pth, simplepth): if p in m : candidates[l].remove(m) - # for i in disjunctions_list : - # print(f'\n{candidates[i.disjunction_id]}') +# for i in disjunctions_list : +# print(i.disjunction_id) +# print(f'\n{candidates[i.disjunction_id]}') + # step 4 apply route constraints : remove candidate path that do not satisfy the constraint # only in the case of disjounction: the simple path is processed in request.compute_constrained_path + # TODO : keep a version without the loose constraint for d in disjunctions_list : - print('a') - - for sol in candidates[d.disjunction_id] : - print('b') - for p in sol : + temp = [] + for j,sol in enumerate(candidates[d.disjunction_id]) : + testispartok = True + for i,p in enumerate(sol) : + # print(f'test {allpaths[id(p)].req.request_id}') + # print(f'length of route {len(allpaths[id(p)].req.nodes_list)}') if allpaths[id(p)].req.nodes_list : # if p does not containt the ordered list node, remove sol from the candidate - print(allpaths[id(p)].req.nodes_list) - print(p) - print('coucou') + # except if this was the last solution: then check if the constraint is loose or not if not ispart(allpaths[id(p)].req.nodes_list, p) : - if len(candidates[d.disjunction_id])>1 : - candidates[d.disjunction_id].remove(sol) - print('removing') - print(sol) - print(allpaths[id(p)].req.loose_list) - - break + # print(f'nb of solutions {len(temp)}') + if j < len(candidates[d.disjunction_id])-1 : + msg = f'removing {sol}' + logger.info(msg) + testispartok = False + #break else: if 'loose' in allpaths[id(p)].req.loose_list: - pass + logger.info(f'Could not apply route constraint'+ + f'{allpaths[id(p)].req.nodes_list} on request {allpaths[id(p)].req.request_id}') else : - candidates[d.disjunction_id].remove(sol) - print('removinglast solution from candidate paths') - print(sol) - break + logger.info(f'removing last solution from candidate paths\n{sol}') + testispartok = False + if testispartok : + temp.append(sol) + candidates[d.disjunction_id] = temp + # step 5 select the first combination that works pathreslist_disjoint = {} for d in disjunctions_list : - sol = next(s for s in candidates[d.disjunction_id]) - # print(f'\n\n\n{sol}') - if sol : - for p in sol: - if allpaths[id(p)].req in pathreqlist_disjt: - pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth - pathreqlist_disjt.remove(allpaths[id(p)].req) - else: - msg = f'No disjoint path found' - logger.critical(msg) + test_sol = True + while test_sol: + if candidates[d.disjunction_id] : + for p in candidates[d.disjunction_id][0]: + if allpaths[id(p)].req in pathreqlist_disjt: + # print(f'selected path :{p} for req {allpaths[id(p)].req.request_id}') + pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth + pathreqlist_disjt.remove(allpaths[id(p)].req) + candidates = remove_candidate(candidates, allpaths, allpaths[id(p)].req, p) + test_sol = False + else: + msg = f'No disjoint path found with added constraint' + logger.critical(msg) + print(f'{msg}\nComputation stopped.') + # TODO in this case: replay step 5 with the candidate without constraints + exit() + + # for i in disjunctions_list : + # print(i.disjunction_id) + # print(f'\n{candidates[i.disjunction_id]}') # list the results in the same order as initial pathreqlist for req in pathreqlist : + req.nodes_list.append(req.destination) + # we assume that the destination is a strict constraint + req.loose_list.append('strict') if req in pathreqlist_simple: - # path_res_list.append([e.uid for e in compute_constrained_path(network, req) - # if isinstance(e,Roadm) ]) - req.nodes_list.append(req.destination) - # we assume that the destination is a strict constraint - req.loose_list.append('strict') path_res_list.append(compute_constrained_path(network, req)) - logger.info(f'{[e.uid for e in compute_constrained_path(network, req)]}') else: path_res_list.append(pathreslist_disjoint[req]) - logger.info(f'{[e.uid for e in pathreslist_disjoint[req]]}') return path_res_list def isdisjoint(p1,p2) : @@ -444,7 +455,7 @@ def ispart(a,b) : j = 0 for i, el in enumerate(a): if el in b : - if b.index(el) > j : + if b.index(el) >= j : j = b.index(el) else: return False @@ -452,6 +463,19 @@ def ispart(a,b) : return False return True +def remove_candidate(candidates, allpaths, rq, pth) : + # print(f'coucou {rq.request_id}') + for key, candidate in candidates.items() : + temp = candidate.copy() + for i,sol in enumerate(candidate) : + for p in sol : + if allpaths[id(p)].req.request_id == rq.request_id : + if id(p) != id(pth) : + temp.remove(sol) + break + candidates[key] = temp + return candidates + def path_result_json(pathresult): data = { 'path': [n.json for n in pathresult] @@ -479,25 +503,19 @@ def path_result_json(pathresult): build_network(network, equipment, p_db, p_total_db) rqs = requests_from_json(data, equipment) - for req in rqs: - print(f'source {req.source} dest {req.destination}') rqs = correct_route_list(network, rqs) - print('coucou\n') - for req in rqs: - print(f'source {req.source} dest {req.destination}') print('The following services have been requested:') print(rqs) # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) - toto = compute_path_dsjctn(network, equipment, rqs,dsjn) - pths = compute_path_2(network, equipment, rqs, toto) - print('toto') - # print(toto) + pths = compute_path_dsjctn(network, equipment, rqs,dsjn) + propagatedpths = compute_path_2(network, equipment, rqs, pths) + header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] data = [] data.append(header) - for i, p in enumerate(pths): + for i, p in enumerate(propagatedpths): if p: line = [f'{rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 577e9f636..cde94714e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -240,12 +240,11 @@ def compute_constrained_path(network, req): # avoid crashing if on req is not correct total_path = [source] for n in req.nodes_list: - # print(n) try : node = next(el for el in trx if el.uid == n) except StopIteration: try: - node = next(el for el in roadm if el.uid == f'roadm {n}') + node = next(el for el in roadm if el.uid == n) except StopIteration: try: # TODO this test is not giving good results: full name of the @@ -262,16 +261,12 @@ def compute_constrained_path(network, req): total_path.extend(dijkstra_path(network, source, node)[1:]) source = node except NetworkXNoPath: - # for debug - # print(req.loose_list) - # print(req.nodes_list.index(n)) if req.loose_list[req.nodes_list.index(n)] == 'loose': print(f'could not find a path from {source.uid} to loose node : {n} in network topology') print(f'node {n} is skipped') else: msg = f'could not find a path from {source.uid} to node : {n} in network topology' logger.critical(msg) - #raise ValueError(msg) print(msg) total_path = [] From c9693d355fff91cd126ec2434720c2b63514efc0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 8 Oct 2018 12:46:24 +0100 Subject: [PATCH 007/108] some cleaning and renaming Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 558e72b60..20ec16da6 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -95,6 +95,8 @@ def load_requests(filename,eqpt_filename): def compute_path(network, equipment, pathreqlist): + # This function is obsolete + path_res_list = [] for pathreq in pathreqlist: @@ -128,7 +130,7 @@ def compute_path(network, equipment, pathreqlist): path_res_list.append(deepcopy(total_path)) return path_res_list -def compute_path_2(network, equipment, pathreqlist, pathlist): +def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): path_res_list = [] # # Build the network once using the default power defined in SI in eqpt config @@ -169,7 +171,8 @@ def compute_path_2(network, equipment, pathreqlist, pathlist): return path_res_list def correct_route_list(network, pathreqlist): - + # prepares the format of route list of nodes to be consistant + # remove wrong names, remove endpoints roadms = [n.uid for n in network.nodes() if isinstance(n, Roadm)] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): @@ -186,7 +189,7 @@ def correct_route_list(network, pathreqlist): print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') pathreq.nodes_list.remove(n_id) pathreq.loose_list.pop(i) - # remove endpoints from this list in case they were added by the user in the xls or json files + # TODO remove endpoints from this list in case they were added by the user in the xls or json files return pathreqlist def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): @@ -509,7 +512,7 @@ def path_result_json(pathresult): # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) pths = compute_path_dsjctn(network, equipment, rqs,dsjn) - propagatedpths = compute_path_2(network, equipment, rqs, pths) + propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] From 826af4a9fd89595c1a8991f5f0202969118d9456 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 22 Oct 2018 16:41:56 +0100 Subject: [PATCH 008/108] Minor corrections of tests due to orthograph change - "synchronisation" replaced with synchronization - "synchonzation" replaced with synchronization Signed-off-by: EstherLerouzic --- tests/compare.py | 4 ++-- tests/data/excelTestFile_services_expected.json | 6 +++--- .../data/meshTopologyExampleV2Eqpt_services_expected.json | 8 ++++---- tests/data/meshTopologyExampleV2_services_expected.json | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/compare.py b/tests/compare.py index d208969b8..90d03ce82 100644 --- a/tests/compare.py +++ b/tests/compare.py @@ -77,8 +77,8 @@ def compare_networks(expected, actual): def compare_services(expected, actual): requests = compare(expected['path-request'], actual['path-request'], key=lambda el: el['request-id']) - synchronizations = compare(expected['synchronisation'], actual['synchronisation'], - key=lambda el: el['synchonization-id']) + synchronizations = compare(expected['synchronization'], actual['synchronization'], + key=lambda el: el['synchronization-id']) return ServicesResults(requests, synchronizations) def compare_paths(expected_output, actual_output): diff --git a/tests/data/excelTestFile_services_expected.json b/tests/data/excelTestFile_services_expected.json index 14a23abd4..a77666b14 100644 --- a/tests/data/excelTestFile_services_expected.json +++ b/tests/data/excelTestFile_services_expected.json @@ -53,9 +53,9 @@ } } ], - "synchronisation": [ + "synchronization": [ { - "synchonization-id": "0", + "synchronization-id": "0", "svec": { "relaxable": "False", "link-diverse": "True", @@ -67,7 +67,7 @@ } }, { - "synchonization-id": "1", + "synchronization-id": "1", "svec": { "relaxable": "False", "link-diverse": "True", diff --git a/tests/data/meshTopologyExampleV2Eqpt_services_expected.json b/tests/data/meshTopologyExampleV2Eqpt_services_expected.json index 19e405e8a..df872965b 100644 --- a/tests/data/meshTopologyExampleV2Eqpt_services_expected.json +++ b/tests/data/meshTopologyExampleV2Eqpt_services_expected.json @@ -178,9 +178,9 @@ } } ], - "synchronisation": [ + "synchronization": [ { - "synchonization-id": "0", + "synchronization-id": "0", "svec": { "relaxable": "False", "link-diverse": "True", @@ -192,7 +192,7 @@ } }, { - "synchonization-id": "3", + "synchronization-id": "3", "svec": { "relaxable": "False", "link-diverse": "True", @@ -204,7 +204,7 @@ } }, { - "synchonization-id": "5", + "synchronization-id": "5", "svec": { "relaxable": "False", "link-diverse": "True", diff --git a/tests/data/meshTopologyExampleV2_services_expected.json b/tests/data/meshTopologyExampleV2_services_expected.json index 19e405e8a..df872965b 100644 --- a/tests/data/meshTopologyExampleV2_services_expected.json +++ b/tests/data/meshTopologyExampleV2_services_expected.json @@ -178,9 +178,9 @@ } } ], - "synchronisation": [ + "synchronization": [ { - "synchonization-id": "0", + "synchronization-id": "0", "svec": { "relaxable": "False", "link-diverse": "True", @@ -192,7 +192,7 @@ } }, { - "synchonization-id": "3", + "synchronization-id": "3", "svec": { "relaxable": "False", "link-diverse": "True", @@ -204,7 +204,7 @@ } }, { - "synchonization-id": "5", + "synchronization-id": "5", "svec": { "relaxable": "False", "link-diverse": "True", From d112c728fc5e5348f87b6c6ebd44126666df2d86 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Mon, 22 Oct 2018 19:11:35 +0100 Subject: [PATCH 009/108] Moving the disjunction function into core functions and minor corrections Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 295 +------------------------------ gnpy/core/request.py | 318 +++++++++++++++++++++++++++++++++- 2 files changed, 315 insertions(+), 298 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 20ec16da6..29af975ff 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -19,8 +19,7 @@ from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO from json import dumps, loads from networkx import (draw_networkx_nodes, draw_networkx_edges, - draw_networkx_labels, dijkstra_path, NetworkXNoPath, all_simple_paths) -from networkx.utils import pairwise + draw_networkx_labels) from numpy import mean from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element from gnpy.core.utils import load_json @@ -29,7 +28,7 @@ from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, - propagate, jsontocsv, Disjunction) + propagate, jsontocsv, Disjunction, compute_path_dsjctn) from copy import copy, deepcopy #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -95,7 +94,8 @@ def load_requests(filename,eqpt_filename): def compute_path(network, equipment, pathreqlist): - # This function is obsolete + # This function is obsolete and not relevant with respect to network building: suggest either to correct + # or to suppress it path_res_list = [] @@ -192,292 +192,7 @@ def correct_route_list(network, pathreqlist): # TODO remove endpoints from this list in case they were added by the user in the xls or json files return pathreqlist -def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): - - # need to return list - path_res_list = [] - # all disjctn must be computed at once together to avoid blocking - # 1 1 - # eg a----b-----c - # |1 |0.5 |1 - # e----f--h--g - # 1 0.5 0.5 - # if I have to compute a to g and a to h - # I must not compute a-b-f-h-g, otherwise there is no disjoint path remaining for a to h - # instead I should list all most disjoint path and select the one that have the less - # number of commonalities - # \ path abfh aefh abcgh - # \___cost 2 2.5 3.5 - # path| cost - # abfhg| 2.5 x x x - # abcg | 3 x x - # aefhg| 3 x x x - # from this table abcg and aefh have no common links and should be preferred - # even they are not the shortest paths - - # build the list of pathreqlist elements not concerned by disjunction - global_disjunctions_list = [e for d in disjunctions_list for e in d.disjunctions_req ] - pathreqlist_simple = [e for e in pathreqlist if e.request_id not in global_disjunctions_list] - pathreqlist_disjt = [e for e in pathreqlist if e.request_id in global_disjunctions_list] - - # use a mirror class to record path and the corresponding requests - class Pth: - def __init__(self, req, pth, simplepth): - self.req = req - self.pth = pth - self.simplepth = simplepth - - # build a conflict table where each path has a count for - # conflicts with the paths from the requests to be disjoint - # step 1 - # for each remaining request compute a set of simple path - allpaths = {} - rqs = {} - simple_rqs = {} - simple_rqs_reversed = {} - for pathreq in pathreqlist_disjt : - all_simp_pths = list(all_simple_paths(network,\ - source=next(el for el in network.nodes() if el.uid == pathreq.source),\ - target=next(el for el in network.nodes() if el.uid == pathreq.destination))) - # sort them - all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) - # reversed direction paths required to check disjunction on both direction - all_simp_pths_reversed = [] - for pth in all_simp_pths: - all_simp_pths_reversed.append(find_reversed_path(pth,network)) - rqs[pathreq.request_id] = all_simp_pths - temp =[] - for p in all_simp_pths : - # build a short list representing each roadm+direction with the first item - # start enumeration at 1 to avoid Trx in the list - s = [e.uid for i,e in enumerate(p[1:-1]) \ - if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] - temp.append(s) - # id(s) is unique even if path is the same: two objects with same - # path have two different ids - allpaths[id(s)] = Pth(pathreq,p,s) - simple_rqs[pathreq.request_id] = temp - temp =[] - for p in all_simp_pths_reversed : - # build a short list representing each roadm+direction with the first item - # start enumeration at 1 to avoid Trx in the list - temp.append([e.uid for i,e in enumerate(p[1:-1]) \ - if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) - simple_rqs_reversed[pathreq.request_id] = temp - # step 2 - # for each set of requests that need to be disjoint - # select the disjoint path combination - - candidates = {} - for d in disjunctions_list : - dlist = d.disjunctions_req.copy() - # each line of dpath is one combination of path that satisfies disjunction - dpath = [] - for i,p in enumerate(simple_rqs[dlist[0]]): - dpath.append([p]) - # allpaths[id(p)].d_id = d.disjunction_id - # in each loop, dpath is updated with a path for rq that satisfies - # disjunction with each path in dpath - # for example, assume set of requests in the vector (disjunction_list) is {rq1,rq2, rq3} - # rq1 p1: abfhg - # p2: aefhg - # p3: abcg - # rq2 p8: bf - # rq3 p4: abcgh - # p6: aefh - # p7: abfh - # initiate with rq1 - # dpath = [[p1] - # [p2] - # [p3]] - # after first loop: - # dpath = [[p1 p8] - # [p3 p8]] - # since p2 and p8 are not disjoint - # after second loop: - # dpath = [ p3 p8 p6 ] - # since p1 and p4 are not disjoint - # p1 and p7 are not disjoint - # p3 and p4 are not disjoint - # p3 and p7 are not disjoint - - for e1 in dlist[1:] : - temp = [] - for j,p1 in enumerate(simple_rqs[e1]): - # allpaths[id(p1)].d_id = d.disjunction_id - # can use index j in simple_rqs_reversed because index - # of direct and reversed paths have been kept identical - p1_reversed = simple_rqs_reversed[e1][j] - # print(p1_reversed) - # print('\n\n') - for k,c in enumerate(dpath) : - # print(f' c: \t{c}') - temp2 = c.copy() - all_disjoint = 0 - for p in c : - all_disjoint += isdisjoint(p1,p)+ isdisjoint(p1_reversed,p) - if all_disjoint ==0: - temp2.append(p1) - temp.append(temp2) - # print(f' coucou {e1}: \t{temp}') - dpath = temp - # print(dpath) - candidates[d.disjunction_id] = dpath - - # for i in disjunctions_list : - # print(f'\n{candidates[i.disjunction_id]}') - - # step 3 - # now for each request, select the path that satisfies all disjunctions - # path must be in candidates[id] for all concerned ids - # for example, assume set of sync vectors (disjunction groups) is - # s1 = {rq1 rq2} s2 = {rq1 rq3} - # candidate[s1] = [[p1 p8] - # [p3 p8]] - # candidate[s2] = [[p3 p6]] - # for rq1 p3 should be preferred - - - for pathreq in pathreqlist_disjt: - concerned_d_id = [d.disjunction_id for d in disjunctions_list if pathreq.request_id in d.disjunctions_req] - # for each set of solution, verify that the same path is used for the same request - candidate_paths = simple_rqs[pathreq.request_id] - # print('coucou') - # print(pathreq.request_id) - for p in candidate_paths : - iscandidate = 0 - for sol in concerned_d_id : - test = 1 - # for each solution test if p is part of the solution - # if yes, then p can remain a candidate - for i,m in enumerate(candidates[sol]) : - if p in m: - if allpaths[id(m[m.index(p)])].req.request_id == pathreq.request_id : - test = 0 - break - iscandidate += test - if iscandidate != 0: - for l in concerned_d_id : - for m in candidates[l] : - if p in m : - candidates[l].remove(m) - -# for i in disjunctions_list : -# print(i.disjunction_id) -# print(f'\n{candidates[i.disjunction_id]}') - - # step 4 apply route constraints : remove candidate path that do not satisfy the constraint - # only in the case of disjounction: the simple path is processed in request.compute_constrained_path - # TODO : keep a version without the loose constraint - for d in disjunctions_list : - temp = [] - for j,sol in enumerate(candidates[d.disjunction_id]) : - testispartok = True - for i,p in enumerate(sol) : - # print(f'test {allpaths[id(p)].req.request_id}') - # print(f'length of route {len(allpaths[id(p)].req.nodes_list)}') - if allpaths[id(p)].req.nodes_list : - # if p does not containt the ordered list node, remove sol from the candidate - # except if this was the last solution: then check if the constraint is loose or not - if not ispart(allpaths[id(p)].req.nodes_list, p) : - # print(f'nb of solutions {len(temp)}') - if j < len(candidates[d.disjunction_id])-1 : - msg = f'removing {sol}' - logger.info(msg) - testispartok = False - #break - else: - if 'loose' in allpaths[id(p)].req.loose_list: - logger.info(f'Could not apply route constraint'+ - f'{allpaths[id(p)].req.nodes_list} on request {allpaths[id(p)].req.request_id}') - else : - logger.info(f'removing last solution from candidate paths\n{sol}') - testispartok = False - if testispartok : - temp.append(sol) - candidates[d.disjunction_id] = temp - - # step 5 select the first combination that works - pathreslist_disjoint = {} - for d in disjunctions_list : - test_sol = True - while test_sol: - if candidates[d.disjunction_id] : - for p in candidates[d.disjunction_id][0]: - if allpaths[id(p)].req in pathreqlist_disjt: - # print(f'selected path :{p} for req {allpaths[id(p)].req.request_id}') - pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth - pathreqlist_disjt.remove(allpaths[id(p)].req) - candidates = remove_candidate(candidates, allpaths, allpaths[id(p)].req, p) - test_sol = False - else: - msg = f'No disjoint path found with added constraint' - logger.critical(msg) - print(f'{msg}\nComputation stopped.') - # TODO in this case: replay step 5 with the candidate without constraints - exit() - - # for i in disjunctions_list : - # print(i.disjunction_id) - # print(f'\n{candidates[i.disjunction_id]}') - - # list the results in the same order as initial pathreqlist - for req in pathreqlist : - req.nodes_list.append(req.destination) - # we assume that the destination is a strict constraint - req.loose_list.append('strict') - if req in pathreqlist_simple: - path_res_list.append(compute_constrained_path(network, req)) - else: - path_res_list.append(pathreslist_disjoint[req]) - return path_res_list - -def isdisjoint(p1,p2) : - # returns 0 if disjoint - edge1 = list(pairwise(p1)) - edge2 = list(pairwise(p2)) - for e in edge1 : - if e in edge2 : - return 1 - return 0 - -def find_reversed_path(p,network) : - # select of intermediate roadms and find the path between them - reversed_roadm_path = list(reversed([e for e in p if isinstance (e,Roadm)])) - source = p[-1] - destination = p[0] - total_path = [source] - for node in reversed_roadm_path : - total_path.extend(dijkstra_path(network, source, node)[1:]) - source = node - total_path.append(destination) - return total_path - -def ispart(a,b) : - j = 0 - for i, el in enumerate(a): - if el in b : - if b.index(el) >= j : - j = b.index(el) - else: - return False - else: - return False - return True - -def remove_candidate(candidates, allpaths, rq, pth) : - # print(f'coucou {rq.request_id}') - for key, candidate in candidates.items() : - temp = candidate.copy() - for i,sol in enumerate(candidate) : - for p in sol : - if allpaths[id(p)].req.request_id == rq.request_id : - if id(p) != id(pth) : - temp.remove(sol) - break - candidates[key] = temp - return candidates def path_result_json(pathresult): data = { @@ -511,7 +226,7 @@ def path_result_json(pathresult): print(rqs) # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) - pths = compute_path_dsjctn(network, equipment, rqs,dsjn) + pths = compute_path_dsjctn(network, equipment, rqs, dsjn) propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index cde94714e..042226412 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -17,7 +17,8 @@ from collections import namedtuple from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO -from networkx import (dijkstra_path, NetworkXNoPath) +from networkx import (dijkstra_path, NetworkXNoPath, all_simple_paths) +from networkx.utils import pairwise from numpy import mean from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused @@ -236,7 +237,7 @@ def compute_constrained_path(network, req): edfa = [n for n in network.nodes() if isinstance(n, Edfa)] source = next(el for el in trx if el.uid == req.source) # start the path with its source - # TODO : avoid loops due to constraints , guess name base on string, + # TODO : avoid loops due to constraints , guess name based on string, # avoid crashing if on req is not correct total_path = [source] for n in req.nodes_list: @@ -270,12 +271,6 @@ def compute_constrained_path(network, req): print(msg) total_path = [] -# preparing disjunction feature - # for p in all_simple_paths(network,\ - # source=next(el for el in trx if el.uid == req.source),\ - # target=next(el for el in trx if el.uid == req.destination)): - # print([e.uid for e in p if isinstance(e,Roadm)]) - return total_path def propagate(path, req, equipment, show=False): @@ -347,3 +342,310 @@ def jsontocsv(json_data,equipment,fileout): output_snr, isok )) + + +def compute_path_dsjctn(network, equipment, pathreqlist, disjunctions_list): + # pathreqlist is a list of Path_request objects + # disjunctions_list a list of Disjunction objects + + # given a network, a list of requests with the set of disjunction features between + # request, the function computes the set of path satisfying : first the disjunction + # constraint and second the routing constraint if the request include an explicit + # set of elements to pass through. + # the algorithm used allows to specify disjunction for demands not sharing source or + # destination. + # a request might be declared as disjoint from several requests + # it is a iterative process: + # first computes a list of all shortest path (this may add computation time) + # second elaborate the set of path solution for each synchronization vector + # third select only the candidates that satisfy all synchronization vectors they belong to + # fourth apply route constraints : remove candidate path that do not satisfy the constraint + # fifth select the first candidate among the set of candidates. + # the example network used in comments has been added to the set of data tests files + + # define the list to be returned + path_res_list = [] + + # all disjctn must be computed at once together to avoid blocking + # 1 1 + # eg a----b-----c + # |1 |0.5 |1 + # e----f--h--g + # 1 0.5 0.5 + # if I have to compute a to g and a to h + # I must not compute a-b-f-h-g, otherwise there is no disjoint path remaining for a to h + # instead I should list all most disjoint path and select the one that have the less + # number of commonalities + # \ path abfh aefh abcgh + # \___cost 2 2.5 3.5 + # path| cost + # abfhg| 2.5 x x x + # abcg | 3 x x + # aefhg| 3 x x x + # from this table abcg and aefh have no common links and should be preferred + # even they are not the shortest paths + + # build the list of pathreqlist elements not concerned by disjunction + global_disjunctions_list = [e for d in disjunctions_list for e in d.disjunctions_req ] + pathreqlist_simple = [e for e in pathreqlist if e.request_id not in global_disjunctions_list] + pathreqlist_disjt = [e for e in pathreqlist if e.request_id in global_disjunctions_list] + + # use a mirror class to record path and the corresponding requests + class Pth: + def __init__(self, req, pth, simplepth): + self.req = req + self.pth = pth + self.simplepth = simplepth + + # step 1 + # for each remaining request compute a set of simple path + allpaths = {} + rqs = {} + simple_rqs = {} + simple_rqs_reversed = {} + for pathreq in pathreqlist_disjt : + all_simp_pths = list(all_simple_paths(network,\ + source=next(el for el in network.nodes() if el.uid == pathreq.source),\ + target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + # sort them + all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) + # reversed direction paths required to check disjunction on both direction + all_simp_pths_reversed = [] + for pth in all_simp_pths: + all_simp_pths_reversed.append(find_reversed_path(pth,network)) + rqs[pathreq.request_id] = all_simp_pths + temp =[] + for p in all_simp_pths : + # build a short list representing each roadm+direction with the first item + # start enumeration at 1 to avoid Trx in the list + s = [e.uid for i,e in enumerate(p[1:-1]) \ + if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] + temp.append(s) + # id(s) is unique even if path is the same: two objects with same + # path have two different ids + allpaths[id(s)] = Pth(pathreq,p,s) + simple_rqs[pathreq.request_id] = temp + temp =[] + for p in all_simp_pths_reversed : + # build a short list representing each roadm+direction with the first item + # start enumeration at 1 to avoid Trx in the list + temp.append([e.uid for i,e in enumerate(p[1:-1]) \ + if (isinstance(e,Roadm) | (isinstance(p[i],Roadm) ))] ) + simple_rqs_reversed[pathreq.request_id] = temp + # step 2 + # for each set of requests that need to be disjoint + # select the disjoint path combination + + candidates = {} + for d in disjunctions_list : + dlist = d.disjunctions_req.copy() + # each line of dpath is one combination of path that satisfies disjunction + dpath = [] + for i,p in enumerate(simple_rqs[dlist[0]]): + dpath.append([p]) + # allpaths[id(p)].d_id = d.disjunction_id + # in each loop, dpath is updated with a path for rq that satisfies + # disjunction with each path in dpath + # for example, assume set of requests in the vector (disjunction_list) is {rq1,rq2, rq3} + # rq1 p1: abfhg + # p2: aefhg + # p3: abcg + # rq2 p8: bf + # rq3 p4: abcgh + # p6: aefh + # p7: abfh + # initiate with rq1 + # dpath = [[p1] + # [p2] + # [p3]] + # after first loop: + # dpath = [[p1 p8] + # [p3 p8]] + # since p2 and p8 are not disjoint + # after second loop: + # dpath = [ p3 p8 p6 ] + # since p1 and p4 are not disjoint + # p1 and p7 are not disjoint + # p3 and p4 are not disjoint + # p3 and p7 are not disjoint + + for e1 in dlist[1:] : + temp = [] + for j,p1 in enumerate(simple_rqs[e1]): + # allpaths[id(p1)].d_id = d.disjunction_id + # can use index j in simple_rqs_reversed because index + # of direct and reversed paths have been kept identical + p1_reversed = simple_rqs_reversed[e1][j] + # print(p1_reversed) + # print('\n\n') + for k,c in enumerate(dpath) : + # print(f' c: \t{c}') + temp2 = c.copy() + all_disjoint = 0 + for p in c : + all_disjoint += isdisjoint(p1,p)+ isdisjoint(p1_reversed,p) + if all_disjoint ==0: + temp2.append(p1) + temp.append(temp2) + # print(f' coucou {e1}: \t{temp}') + dpath = temp + # print(dpath) + candidates[d.disjunction_id] = dpath + + # for i in disjunctions_list : + # print(f'\n{candidates[i.disjunction_id]}') + + # step 3 + # now for each request, select the path that satisfies all disjunctions + # path must be in candidates[id] for all concerned ids + # for example, assume set of sync vectors (disjunction groups) is + # s1 = {rq1 rq2} s2 = {rq1 rq3} + # candidate[s1] = [[p1 p8] + # [p3 p8]] + # candidate[s2] = [[p3 p6]] + # for rq1 p3 should be preferred + + + for pathreq in pathreqlist_disjt: + concerned_d_id = [d.disjunction_id for d in disjunctions_list if pathreq.request_id in d.disjunctions_req] + # for each set of solution, verify that the same path is used for the same request + candidate_paths = simple_rqs[pathreq.request_id] + # print('coucou') + # print(pathreq.request_id) + for p in candidate_paths : + iscandidate = 0 + for sol in concerned_d_id : + test = 1 + # for each solution test if p is part of the solution + # if yes, then p can remain a candidate + for i,m in enumerate(candidates[sol]) : + if p in m: + if allpaths[id(m[m.index(p)])].req.request_id == pathreq.request_id : + test = 0 + break + iscandidate += test + if iscandidate != 0: + for l in concerned_d_id : + for m in candidates[l] : + if p in m : + candidates[l].remove(m) + +# for i in disjunctions_list : +# print(i.disjunction_id) +# print(f'\n{candidates[i.disjunction_id]}') + + # step 4 apply route constraints : remove candidate path that do not satisfy the constraint + # only in the case of disjounction: the simple path is processed in request.compute_constrained_path + # TODO : keep a version without the loose constraint + for d in disjunctions_list : + temp = [] + for j,sol in enumerate(candidates[d.disjunction_id]) : + testispartok = True + for i,p in enumerate(sol) : + # print(f'test {allpaths[id(p)].req.request_id}') + # print(f'length of route {len(allpaths[id(p)].req.nodes_list)}') + if allpaths[id(p)].req.nodes_list : + # if p does not containt the ordered list node, remove sol from the candidate + # except if this was the last solution: then check if the constraint is loose or not + if not ispart(allpaths[id(p)].req.nodes_list, p) : + # print(f'nb of solutions {len(temp)}') + if j < len(candidates[d.disjunction_id])-1 : + msg = f'removing {sol}' + logger.info(msg) + testispartok = False + #break + else: + if 'loose' in allpaths[id(p)].req.loose_list: + logger.info(f'Could not apply route constraint'+ + f'{allpaths[id(p)].req.nodes_list} on request {allpaths[id(p)].req.request_id}') + else : + logger.info(f'removing last solution from candidate paths\n{sol}') + testispartok = False + if testispartok : + temp.append(sol) + candidates[d.disjunction_id] = temp + + # step 5 select the first combination that works + pathreslist_disjoint = {} + for d in disjunctions_list : + test_sol = True + while test_sol: + if candidates[d.disjunction_id] : + for p in candidates[d.disjunction_id][0]: + if allpaths[id(p)].req in pathreqlist_disjt: + # print(f'selected path :{p} for req {allpaths[id(p)].req.request_id}') + pathreslist_disjoint[allpaths[id(p)].req] = allpaths[id(p)].pth + pathreqlist_disjt.remove(allpaths[id(p)].req) + candidates = remove_candidate(candidates, allpaths, allpaths[id(p)].req, p) + test_sol = False + else: + msg = f'No disjoint path found with added constraint' + logger.critical(msg) + print(f'{msg}\nComputation stopped.') + # TODO in this case: replay step 5 with the candidate without constraints + exit() + + # for i in disjunctions_list : + # print(i.disjunction_id) + # print(f'\n{candidates[i.disjunction_id]}') + + # list the results in the same order as initial pathreqlist + for req in pathreqlist : + req.nodes_list.append(req.destination) + # we assume that the destination is a strict constraint + req.loose_list.append('strict') + if req in pathreqlist_simple: + path_res_list.append(compute_constrained_path(network, req)) + else: + path_res_list.append(pathreslist_disjoint[req]) + return path_res_list + +def isdisjoint(p1,p2) : + # returns 0 if disjoint + edge1 = list(pairwise(p1)) + edge2 = list(pairwise(p2)) + for e in edge1 : + if e in edge2 : + return 1 + return 0 + +def find_reversed_path(p,network) : + # select of intermediate roadms and find the path between them + # note that this function may not give an exact result in case of multiple + # links between two adjacent nodes. + # TODO add some indication on elements to indicate from which other they + # are the reversed direction + reversed_roadm_path = list(reversed([e for e in p if isinstance (e,Roadm)])) + source = p[-1] + destination = p[0] + total_path = [source] + for node in reversed_roadm_path : + total_path.extend(dijkstra_path(network, source, node)[1:]) + source = node + total_path.append(destination) + return total_path + +def ispart(a,b) : + j = 0 + for i, el in enumerate(a): + if el in b : + if b.index(el) >= j : + j = b.index(el) + else: + return False + else: + return False + return True + +def remove_candidate(candidates, allpaths, rq, pth) : + # print(f'coucou {rq.request_id}') + for key, candidate in candidates.items() : + temp = candidate.copy() + for i,sol in enumerate(candidate) : + for p in sol : + if allpaths[id(p)].req.request_id == rq.request_id : + if id(p) != id(pth) : + temp.remove(sol) + break + candidates[key] = temp + return candidates \ No newline at end of file From 13aaa174e1befe8d4d0b0387af3f3bf8edb32ff2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 23 Oct 2018 09:30:20 +0100 Subject: [PATCH 010/108] Adding this toy example network for disjunction testing 1 1 a----b-------c | | | |1 |0.5 |1 | | | e----f---h---g 1 0.5 0.5 Signed-off-by: EstherLerouzic --- tests/data/meshTopologyToy.json | 730 +++++ .../meshTopologyToy_results_expected.json | 2884 +++++++++++++++++ tests/data/meshTopologyToy_services.json | 408 +++ 3 files changed, 4022 insertions(+) create mode 100644 tests/data/meshTopologyToy.json create mode 100644 tests/data/meshTopologyToy_results_expected.json create mode 100644 tests/data/meshTopologyToy_services.json diff --git a/tests/data/meshTopologyToy.json b/tests/data/meshTopologyToy.json new file mode 100644 index 000000000..c1592f6b1 --- /dev/null +++ b/tests/data/meshTopologyToy.json @@ -0,0 +1,730 @@ +{ + "elements": [ + { + "uid": "trx a", + "metadata": { + "location": { + "city": "a", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx b", + "metadata": { + "location": { + "city": "b", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx c", + "metadata": { + "location": { + "city": "c", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx d", + "metadata": { + "location": { + "city": "d", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx e", + "metadata": { + "location": { + "city": "e", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx f", + "metadata": { + "location": { + "city": "f", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx g", + "metadata": { + "location": { + "city": "g", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx h", + "metadata": { + "location": { + "city": "h", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Transceiver" + }, + { + "uid": "roadm a", + "metadata": { + "location": { + "city": "a", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm b", + "metadata": { + "location": { + "city": "b", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm c", + "metadata": { + "location": { + "city": "c", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm d", + "metadata": { + "location": { + "city": "d", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm e", + "metadata": { + "location": { + "city": "e", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm f", + "metadata": { + "location": { + "city": "f", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm g", + "metadata": { + "location": { + "city": "g", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm h", + "metadata": { + "location": { + "city": "h", + "region": "", + "latitude": 0, + "longitude": 0 + } + }, + "type": "Roadm" + }, + { + "uid": "fiber (a \u2192 b)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 30.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (a \u2192 c)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 30.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (c \u2192 d)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 50.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (c \u2192 f)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 60.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (b \u2192 f)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 70.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (e \u2192 d)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 80.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (e \u2192 g)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 90.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (f \u2192 h)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 100.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (h \u2192 g)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 110.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (b \u2192 a)-F061", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 30.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (c \u2192 a)-F010", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 30.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (d \u2192 c)-F054", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 50.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (f \u2192 c)-F055", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 60.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (f \u2192 b)-F056", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 70.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (d \u2192 e)-F057", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 80.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (g \u2192 e)-F059", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 90.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (h \u2192 f)-F060", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 100.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (g \u2192 h)-", + "metadata": { + "location": { + "latitude": 0.0, + "longitude": 0.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 110.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + } + ], + "connections": [ + { + "from_node": "roadm a", + "to_node": "fiber (a \u2192 b)-" + }, + { + "from_node": "fiber (b \u2192 a)-F061", + "to_node": "roadm a" + }, + { + "from_node": "roadm a", + "to_node": "fiber (a \u2192 c)-" + }, + { + "from_node": "fiber (c \u2192 a)-F010", + "to_node": "roadm a" + }, + { + "from_node": "roadm b", + "to_node": "fiber (b \u2192 a)-F061" + }, + { + "from_node": "fiber (a \u2192 b)-", + "to_node": "roadm b" + }, + { + "from_node": "roadm b", + "to_node": "fiber (b \u2192 f)-" + }, + { + "from_node": "fiber (f \u2192 b)-F056", + "to_node": "roadm b" + }, + { + "from_node": "roadm c", + "to_node": "fiber (c \u2192 a)-F010" + }, + { + "from_node": "fiber (a \u2192 c)-", + "to_node": "roadm c" + }, + { + "from_node": "roadm c", + "to_node": "fiber (c \u2192 d)-" + }, + { + "from_node": "fiber (d \u2192 c)-F054", + "to_node": "roadm c" + }, + { + "from_node": "roadm c", + "to_node": "fiber (c \u2192 f)-" + }, + { + "from_node": "fiber (f \u2192 c)-F055", + "to_node": "roadm c" + }, + { + "from_node": "roadm d", + "to_node": "fiber (d \u2192 c)-F054" + }, + { + "from_node": "fiber (c \u2192 d)-", + "to_node": "roadm d" + }, + { + "from_node": "roadm d", + "to_node": "fiber (d \u2192 e)-F057" + }, + { + "from_node": "fiber (e \u2192 d)-", + "to_node": "roadm d" + }, + { + "from_node": "roadm e", + "to_node": "fiber (e \u2192 d)-" + }, + { + "from_node": "fiber (d \u2192 e)-F057", + "to_node": "roadm e" + }, + { + "from_node": "roadm e", + "to_node": "fiber (e \u2192 g)-" + }, + { + "from_node": "fiber (g \u2192 e)-F059", + "to_node": "roadm e" + }, + { + "from_node": "roadm f", + "to_node": "fiber (f \u2192 c)-F055" + }, + { + "from_node": "fiber (c \u2192 f)-", + "to_node": "roadm f" + }, + { + "from_node": "roadm f", + "to_node": "fiber (f \u2192 b)-F056" + }, + { + "from_node": "fiber (b \u2192 f)-", + "to_node": "roadm f" + }, + { + "from_node": "roadm f", + "to_node": "fiber (f \u2192 h)-" + }, + { + "from_node": "fiber (h \u2192 f)-F060", + "to_node": "roadm f" + }, + { + "from_node": "roadm g", + "to_node": "fiber (g \u2192 e)-F059" + }, + { + "from_node": "fiber (e \u2192 g)-", + "to_node": "roadm g" + }, + { + "from_node": "roadm g", + "to_node": "fiber (g \u2192 h)-" + }, + { + "from_node": "fiber (h \u2192 g)-", + "to_node": "roadm g" + }, + { + "from_node": "roadm h", + "to_node": "fiber (h \u2192 f)-F060" + }, + { + "from_node": "fiber (f \u2192 h)-", + "to_node": "roadm h" + }, + { + "from_node": "roadm h", + "to_node": "fiber (h \u2192 g)-" + }, + { + "from_node": "fiber (g \u2192 h)-", + "to_node": "roadm h" + }, + { + "from_node": "trx a", + "to_node": "roadm a" + }, + { + "from_node": "roadm a", + "to_node": "trx a" + }, + { + "from_node": "trx b", + "to_node": "roadm b" + }, + { + "from_node": "roadm b", + "to_node": "trx b" + }, + { + "from_node": "trx c", + "to_node": "roadm c" + }, + { + "from_node": "roadm c", + "to_node": "trx c" + }, + { + "from_node": "trx d", + "to_node": "roadm d" + }, + { + "from_node": "roadm d", + "to_node": "trx d" + }, + { + "from_node": "trx e", + "to_node": "roadm e" + }, + { + "from_node": "roadm e", + "to_node": "trx e" + }, + { + "from_node": "trx f", + "to_node": "roadm f" + }, + { + "from_node": "roadm f", + "to_node": "trx f" + }, + { + "from_node": "trx g", + "to_node": "roadm g" + }, + { + "from_node": "roadm g", + "to_node": "trx g" + }, + { + "from_node": "trx h", + "to_node": "roadm h" + }, + { + "from_node": "roadm h", + "to_node": "trx h" + } + ] +} \ No newline at end of file diff --git a/tests/data/meshTopologyToy_results_expected.json b/tests/data/meshTopologyToy_results_expected.json new file mode 100644 index 000000000..61177f56d --- /dev/null +++ b/tests/data/meshTopologyToy_results_expected.json @@ -0,0 +1,2884 @@ +{ + "path": [ + { + "path-id": "1", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 18.0 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 22.08 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 18.91 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.99 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx a", + "link-tp-id": "trx a", + "hop-type": "Voyager - 16QAM", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm a", + "link-tp-id": "roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa1_roadm a", + "link-tp-id": "Edfa1_roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (a \u2192 c)-", + "link-tp-id": "fiber (a \u2192 c)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (a \u2192 c)-", + "link-tp-id": "Edfa0_fiber (a \u2192 c)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm c", + "link-tp-id": "roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm c", + "link-tp-id": "Edfa1_roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (c \u2192 d)-", + "link-tp-id": "fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (c \u2192 d)-", + "link-tp-id": "Edfa0_fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm d", + "link-tp-id": "roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa1_roadm d", + "link-tp-id": "Edfa1_roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (d \u2192 e)-F057", + "link-tp-id": "fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (d \u2192 e)-F057", + "link-tp-id": "Edfa0_fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm e", + "link-tp-id": "roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "Edfa1_roadm e", + "link-tp-id": "Edfa1_roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 15, + "unnumbered-hop": { + "node-id": "fiber (e \u2192 g)-", + "link-tp-id": "fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 16, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (e \u2192 g)-", + "link-tp-id": "Edfa0_fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 17, + "unnumbered-hop": { + "node-id": "roadm g", + "link-tp-id": "roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 18, + "unnumbered-hop": { + "node-id": "trx g", + "link-tp-id": "trx g", + "hop-type": "Voyager - 16QAM", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "2a", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 19.88 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 23.96 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 20.83 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 24.91 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx a", + "link-tp-id": "trx a", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm a", + "link-tp-id": "roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa0_roadm a", + "link-tp-id": "Edfa0_roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (a \u2192 b)-", + "link-tp-id": "fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (a \u2192 b)-", + "link-tp-id": "Edfa0_fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm b", + "link-tp-id": "Edfa1_roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (b \u2192 f)-", + "link-tp-id": "fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (b \u2192 f)-", + "link-tp-id": "Edfa0_fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa2_roadm f", + "link-tp-id": "Edfa2_roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (f \u2192 h)-", + "link-tp-id": "fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (f \u2192 h)-", + "link-tp-id": "Edfa0_fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm h", + "link-tp-id": "roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "trx h", + "link-tp-id": "trx h", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "3", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 24.83 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 28.91 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 26.28 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 30.36 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa1_roadm f", + "link-tp-id": "Edfa1_roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (f \u2192 b)-F056", + "link-tp-id": "fiber (f \u2192 b)-F056", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (f \u2192 b)-F056", + "link-tp-id": "Edfa0_fiber (f \u2192 b)-F056", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "trx b", + "link-tp-id": "trx b", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "ee", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 16.81 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 20.89 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 17.94 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.02 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx c", + "link-tp-id": "trx c", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm c", + "link-tp-id": "roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa1_roadm c", + "link-tp-id": "Edfa1_roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (c \u2192 d)-", + "link-tp-id": "fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (c \u2192 d)-", + "link-tp-id": "Edfa0_fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm d", + "link-tp-id": "roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm d", + "link-tp-id": "Edfa1_roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (d \u2192 e)-F057", + "link-tp-id": "fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (d \u2192 e)-F057", + "link-tp-id": "Edfa0_fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm e", + "link-tp-id": "roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa1_roadm e", + "link-tp-id": "Edfa1_roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (e \u2192 g)-", + "link-tp-id": "fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (e \u2192 g)-", + "link-tp-id": "Edfa0_fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm g", + "link-tp-id": "roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "Edfa1_roadm g", + "link-tp-id": "Edfa1_roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 15, + "unnumbered-hop": { + "node-id": "fiber (g \u2192 h)-", + "link-tp-id": "fiber (g \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 16, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (g \u2192 h)-", + "link-tp-id": "Edfa0_fiber (g \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 17, + "unnumbered-hop": { + "node-id": "roadm h", + "link-tp-id": "roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 18, + "unnumbered-hop": { + "node-id": "Edfa0_roadm h", + "link-tp-id": "Edfa0_roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 19, + "unnumbered-hop": { + "node-id": "fiber (h \u2192 f)-F060", + "link-tp-id": "fiber (h \u2192 f)-F060", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 20, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (h \u2192 f)-F060", + "link-tp-id": "Edfa0_fiber (h \u2192 f)-F060", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 21, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 22, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "ff", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 16.81 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 20.89 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 17.94 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.02 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx c", + "link-tp-id": "trx c", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm c", + "link-tp-id": "roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa0_roadm c", + "link-tp-id": "Edfa0_roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (c \u2192 a)-F010", + "link-tp-id": "fiber (c \u2192 a)-F010", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (c \u2192 a)-F010", + "link-tp-id": "Edfa0_fiber (c \u2192 a)-F010", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm a", + "link-tp-id": "roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa0_roadm a", + "link-tp-id": "Edfa0_roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (a \u2192 b)-", + "link-tp-id": "fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (a \u2192 b)-", + "link-tp-id": "Edfa0_fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa1_roadm b", + "link-tp-id": "Edfa1_roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (b \u2192 f)-", + "link-tp-id": "fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (b \u2192 f)-", + "link-tp-id": "Edfa0_fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "10", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 18.0 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 22.08 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 18.91 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.99 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx a", + "link-tp-id": "trx a", + "hop-type": "Voyager - 16QAM", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm a", + "link-tp-id": "roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa0_roadm a", + "link-tp-id": "Edfa0_roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (a \u2192 b)-", + "link-tp-id": "fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (a \u2192 b)-", + "link-tp-id": "Edfa0_fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm b", + "link-tp-id": "Edfa1_roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (b \u2192 f)-", + "link-tp-id": "fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (b \u2192 f)-", + "link-tp-id": "Edfa0_fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa2_roadm f", + "link-tp-id": "Edfa2_roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (f \u2192 h)-", + "link-tp-id": "fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (f \u2192 h)-", + "link-tp-id": "Edfa0_fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm h", + "link-tp-id": "roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "Edfa1_roadm h", + "link-tp-id": "Edfa1_roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 15, + "unnumbered-hop": { + "node-id": "fiber (h \u2192 g)-", + "link-tp-id": "fiber (h \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 16, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (h \u2192 g)-", + "link-tp-id": "Edfa0_fiber (h \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 17, + "unnumbered-hop": { + "node-id": "roadm g", + "link-tp-id": "roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 18, + "unnumbered-hop": { + "node-id": "trx g", + "link-tp-id": "trx g", + "hop-type": "Voyager - 16QAM", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "2a", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 19.88 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 23.96 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 20.83 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 24.91 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx a", + "link-tp-id": "trx a", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm a", + "link-tp-id": "roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa0_roadm a", + "link-tp-id": "Edfa0_roadm a", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (a \u2192 b)-", + "link-tp-id": "fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (a \u2192 b)-", + "link-tp-id": "Edfa0_fiber (a \u2192 b)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm b", + "link-tp-id": "Edfa1_roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (b \u2192 f)-", + "link-tp-id": "fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (b \u2192 f)-", + "link-tp-id": "Edfa0_fiber (b \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa2_roadm f", + "link-tp-id": "Edfa2_roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (f \u2192 h)-", + "link-tp-id": "fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (f \u2192 h)-", + "link-tp-id": "Edfa0_fiber (f \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm h", + "link-tp-id": "roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "trx h", + "link-tp-id": "trx h", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "3", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 24.83 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 28.91 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 26.28 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 30.36 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa1_roadm f", + "link-tp-id": "Edfa1_roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (f \u2192 b)-F056", + "link-tp-id": "fiber (f \u2192 b)-F056", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (f \u2192 b)-F056", + "link-tp-id": "Edfa0_fiber (f \u2192 b)-F056", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm b", + "link-tp-id": "roadm b", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "trx b", + "link-tp-id": "trx b", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "13", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 16.81 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 20.89 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 17.94 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.02 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx c", + "link-tp-id": "trx c", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm c", + "link-tp-id": "roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa2_roadm c", + "link-tp-id": "Edfa2_roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (c \u2192 f)-", + "link-tp-id": "fiber (c \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (c \u2192 f)-", + "link-tp-id": "Edfa0_fiber (c \u2192 f)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + }, + { + "path-id": "ee", + "path-properties": { + "path-metric": [ + { + "metric-type": "SNR@bandwidth", + "accumulative-value": 16.81 + }, + { + "metric-type": "SNR@0.1nm", + "accumulative-value": 20.89 + }, + { + "metric-type": "OSNR@bandwidth", + "accumulative-value": 17.94 + }, + { + "metric-type": "OSNR@0.1nm", + "accumulative-value": 22.02 + }, + { + "metric-type": "reference_power", + "accumulative-value": 0.001 + } + ], + "path-srlgs": { + "usage": "not used yet", + "values": "not used yet" + }, + "path-route-objects": [ + { + "path-route-object": { + "index": 0, + "unnumbered-hop": { + "node-id": "trx c", + "link-tp-id": "trx c", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm c", + "link-tp-id": "roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 2, + "unnumbered-hop": { + "node-id": "Edfa1_roadm c", + "link-tp-id": "Edfa1_roadm c", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 3, + "unnumbered-hop": { + "node-id": "fiber (c \u2192 d)-", + "link-tp-id": "fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 4, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (c \u2192 d)-", + "link-tp-id": "Edfa0_fiber (c \u2192 d)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 5, + "unnumbered-hop": { + "node-id": "roadm d", + "link-tp-id": "roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 6, + "unnumbered-hop": { + "node-id": "Edfa1_roadm d", + "link-tp-id": "Edfa1_roadm d", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 7, + "unnumbered-hop": { + "node-id": "fiber (d \u2192 e)-F057", + "link-tp-id": "fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 8, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (d \u2192 e)-F057", + "link-tp-id": "Edfa0_fiber (d \u2192 e)-F057", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 9, + "unnumbered-hop": { + "node-id": "roadm e", + "link-tp-id": "roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 10, + "unnumbered-hop": { + "node-id": "Edfa1_roadm e", + "link-tp-id": "Edfa1_roadm e", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 11, + "unnumbered-hop": { + "node-id": "fiber (e \u2192 g)-", + "link-tp-id": "fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 12, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (e \u2192 g)-", + "link-tp-id": "Edfa0_fiber (e \u2192 g)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 13, + "unnumbered-hop": { + "node-id": "roadm g", + "link-tp-id": "roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 14, + "unnumbered-hop": { + "node-id": "Edfa1_roadm g", + "link-tp-id": "Edfa1_roadm g", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 15, + "unnumbered-hop": { + "node-id": "fiber (g \u2192 h)-", + "link-tp-id": "fiber (g \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 16, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (g \u2192 h)-", + "link-tp-id": "Edfa0_fiber (g \u2192 h)-", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 17, + "unnumbered-hop": { + "node-id": "roadm h", + "link-tp-id": "roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 18, + "unnumbered-hop": { + "node-id": "Edfa0_roadm h", + "link-tp-id": "Edfa0_roadm h", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 19, + "unnumbered-hop": { + "node-id": "fiber (h \u2192 f)-F060", + "link-tp-id": "fiber (h \u2192 f)-F060", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 20, + "unnumbered-hop": { + "node-id": "Edfa0_fiber (h \u2192 f)-F060", + "link-tp-id": "Edfa0_fiber (h \u2192 f)-F060", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 21, + "unnumbered-hop": { + "node-id": "roadm f", + "link-tp-id": "roadm f", + "hop-type": "not recorded", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + }, + { + "path-route-object": { + "index": 22, + "unnumbered-hop": { + "node-id": "trx f", + "link-tp-id": "trx f", + "hop-type": "vendorA_trx-type1 - mode 1", + "direction": "not used" + }, + "label-hop": { + "te-label": { + "generic": "not used yet", + "direction": "not used yet" + } + } + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/tests/data/meshTopologyToy_services.json b/tests/data/meshTopologyToy_services.json new file mode 100644 index 000000000..4f6803cd6 --- /dev/null +++ b/tests/data/meshTopologyToy_services.json @@ -0,0 +1,408 @@ +{ + "path-request": [ + { + "request-id": "1", + "source": "a", + "destination": "g", + "src-tp-id": "trx a", + "dst-tp-id": "trx g", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "16QAM", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "2a", + "source": "a", + "destination": "h", + "src-tp-id": "trx a", + "dst-tp-id": "trx h", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "3", + "source": "f", + "destination": "b", + "src-tp-id": "trx f", + "dst-tp-id": "trx b", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "ee", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [ + { + "index": 0, + "unnumbered-hop": { + "node-id": "e", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + }, + { + "index": 1, + "unnumbered-hop": { + "node-id": "g", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + } + ] + } + }, + { + "request-id": "ff", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "10", + "source": "a", + "destination": "g", + "src-tp-id": "trx a", + "dst-tp-id": "trx g", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "16QAM", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "11", + "source": "a", + "destination": "h", + "src-tp-id": "trx a", + "dst-tp-id": "trx h", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [ + { + "index": 0, + "unnumbered-hop": { + "node-id": "bb", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + } + ] + } + }, + { + "request-id": "12", + "source": "f", + "destination": "b", + "src-tp-id": "trx f", + "dst-tp-id": "trx b", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [ + { + "index": 0, + "unnumbered-hop": { + "node-id": "trx b", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + } + ] + } + }, + { + "request-id": "13", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "14", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001 + } + }, + "optimizations": { + "explicit-route-include-objects": [ + { + "index": 0, + "unnumbered-hop": { + "node-id": "e", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + }, + { + "index": 1, + "unnumbered-hop": { + "node-id": "g", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + } + ] + } + } + ], + "synchronization": [ + { + "synchronization-id": "1", + "svec": { + "relaxable": "False", + "link-diverse": "True", + "node-diverse": "True", + "request-id-number": [ + "1", + "2a" + ] + } + }, + { + "synchronization-id": "3", + "svec": { + "relaxable": "False", + "link-diverse": "True", + "node-diverse": "True", + "request-id-number": [ + "3", + "1" + ] + } + }, + { + "synchronization-id": "ff", + "svec": { + "relaxable": "False", + "link-diverse": "True", + "node-diverse": "True", + "request-id-number": [ + "ff", + "13" + ] + } + }, + { + "synchronization-id": "13", + "svec": { + "relaxable": "False", + "link-diverse": "True", + "node-diverse": "True", + "request-id-number": [ + "13", + "14" + ] + } + } + ] +} \ No newline at end of file From 75660febc18ed614d9d7739ddce4a3258cc4a62b Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 23 Oct 2018 11:11:06 +0100 Subject: [PATCH 011/108] Create test_disjunction - create some test to check that produced paths are really disjoint - add some TODOs on optical power for requests computation First check of disjunction feature added to tests Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 7 +++ tests/data/meshTopologyToy_services.json | 20 +++---- tests/test_disjunction.py | 66 ++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 10 deletions(-) create mode 100644 tests/test_disjunction.py diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 29af975ff..83432c1ae 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -132,7 +132,11 @@ def compute_path(network, equipment, pathreqlist): def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): + # use a list but a dictionnary might be helpful to find path bathsed on request_id + # TODO change all these req, dsjct, res lists into dict ! path_res_list = [] + + # # 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 @@ -145,6 +149,9 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): # pathreq.power to be adapted for i,pathreq in enumerate(pathreqlist): + # use the power specified in requests but might be different from the one specified for design + # TODO: set the power as 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}') diff --git a/tests/data/meshTopologyToy_services.json b/tests/data/meshTopologyToy_services.json index 4f6803cd6..2eed9ee59 100644 --- a/tests/data/meshTopologyToy_services.json +++ b/tests/data/meshTopologyToy_services.json @@ -9,7 +9,7 @@ "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", - "trx_type": "Voyager", + "trx_type": "Voyager_16QAM", "trx_mode": "16QAM", "effective-freq-slot": [ { @@ -36,7 +36,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -62,7 +62,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -88,7 +88,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -145,7 +145,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -170,7 +170,7 @@ "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", - "trx_type": "Voyager", + "trx_type": "Voyager_16QAM", "trx_mode": "16QAM", "effective-freq-slot": [ { @@ -197,7 +197,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -239,7 +239,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -281,7 +281,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", @@ -307,7 +307,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 1", + "trx_mode": "PS_SP64_1", "effective-freq-slot": [ { "n": "null", diff --git a/tests/test_disjunction.py b/tests/test_disjunction.py new file mode 100644 index 000000000..1e1f9aa40 --- /dev/null +++ b/tests/test_disjunction.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# TelecomInfraProject/gnpy/examples +# Module name : test_disjunction.py +# Version : +# License : BSD 3-Clause Licence +# Copyright (c) 2018, Telecom Infra Project + +""" +@author: esther.lerouzic +checks that computed paths are disjoint as specified in the json service file +""" + +from pathlib import Path +import pytest +from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch +from gnpy.core.network import load_network, build_network +from examples.path_requests_run import (requests_from_json , correct_route_list , + load_requests , disjunctions_from_json) +from gnpy.core.request import compute_path_dsjctn, isdisjoint , find_reversed_path +from gnpy.core.utils import db2lin, lin2db +from gnpy.core.elements import Roadm + +network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json' +service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' +result_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json' +eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' + +@pytest.mark.parametrize("net",[network_file_name]) +@pytest.mark.parametrize("eqpt", [eqpt_library_name]) +@pytest.mark.parametrize("serv",[service_file_name]) +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 + From b271c1ca3c0d19a34c27d0f608a1f2cf14ca0af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 23 Oct 2018 17:13:08 +0200 Subject: [PATCH 012/108] Update equipment.py --- gnpy/core/equipment.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index eac94e1b3..28338adc9 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -13,7 +13,7 @@ from operator import itemgetter from math import isclose from pathlib import Path -from json import loads +from json import load from gnpy.core.utils import lin2db, db2lin, load_json from collections import namedtuple from gnpy.core.elements import Edfa @@ -44,13 +44,13 @@ def __new__(cls, @classmethod def from_advanced_json(cls, filename, **kwargs): with open(filename) as f: - json_data = loads(f.read()) + json_data = load(f) return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None}) @classmethod def from_default_json(cls, filename, **kwargs): with open(filename) as f: - json_data = loads(f.read()) + json_data = load(f) type_variety = kwargs['type_variety'] type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files nf_def = None @@ -208,17 +208,17 @@ def equipment_from_json(json_data, filename): """ equipment = {} for key, entries in json_data.items(): + equipment[key] = {} + typ = globals()[key] for entry in entries: - if key not in equipment: - equipment[key] = {} - subkey = entry.get('type_variety', 'default') - typ = globals()[key] + subkey = entry.get('type_variety', 'default') if key == 'Edfa': if 'advanced_config_from_json' in entry: config = Path(filename).parent / entry.pop('advanced_config_from_json') - typ = lambda **kws: Amp.from_advanced_json(config, **kws) + equipment[key][subkey] = Amp.from_advanced_json(config, **entry) else: config = Path(filename).parent / 'default_edfa_config.json' - typ = lambda **kws: Amp.from_default_json(config, **kws) - equipment[key][subkey] = typ(**entry) + equipment[key][subkey] = Amp.from_default_json(config, **entry) + else: + equipment[key][subkey] = typ(**entry) return equipment From 0bfacd84f433b7f6caf3e10446ceab544ad90c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 23 Oct 2018 17:16:51 +0200 Subject: [PATCH 013/108] Update equipment.py --- gnpy/core/equipment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 28338adc9..6f2a02045 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -215,7 +215,7 @@ def equipment_from_json(json_data, filename): if key == 'Edfa': if 'advanced_config_from_json' in entry: config = Path(filename).parent / entry.pop('advanced_config_from_json') - equipment[key][subkey] = Amp.from_advanced_json(config, **entry) + equipment[key][subkey] = Amp.from_advanced_json(config, **entry) else: config = Path(filename).parent / 'default_edfa_config.json' equipment[key][subkey] = Amp.from_default_json(config, **entry) From 2413bd9e0def5bf9ea6fa84303e12567c1875233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Thu, 25 Oct 2018 11:31:39 +0200 Subject: [PATCH 014/108] Update eqpt_config.json Add fiber types. --- examples/eqpt_config.json | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index dc255f34d..0e64a6e12 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -64,6 +64,16 @@ "type_variety": "SSMF", "dispersion": 1.67e-05, "gamma": 0.00127 + }, + { + "type_variety": "NZDF", + "dispersion": 0.5e-05, + "gamma": 0.00146 + }, + { + "type_variety": "LOF", + "dispersion": 2.2e-05, + "gamma": 0.000843 } ], "Spans":[{ From 215295efb1c1c787dd5ee632fc855a78dadfc596 Mon Sep 17 00:00:00 2001 From: ojnas Date: Thu, 25 Oct 2018 15:25:28 +0200 Subject: [PATCH 015/108] changed source and destination handling --- examples/transmission_main_example.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 71edc5c6b..f225e884c 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -150,16 +150,14 @@ def main(network, equipment, source, destination, req = None): exit() if args.source: - try: - source = next(transceivers[uid] for uid in transceivers if uid == args.source) - except StopIteration as e: + source = transceivers.get(args.source) + if not source: #TODO code a more advanced regex to find nodes match nodes_suggestion = [uid for uid in transceivers \ if args.source.lower() in uid.lower()] source = transceivers[nodes_suggestion[0]] \ if len(nodes_suggestion)>0 else list(transceivers.values())[0] - print(f'invalid souce node specified, did you mean:\ - \n{nodes_suggestion}?\ + print(f'invalid souce node specified,\ \n{args.source!r}, replaced with {source.uid}') del transceivers[source.uid] else: @@ -167,15 +165,13 @@ def main(network, equipment, source, destination, req = None): source = list(transceivers.values())[0] if args.destination: - try: - destination = next(transceivers[uid] for uid in transceivers if uid == args.destination) - except StopIteration as e: + destination = transceivers.get(args.destination) + if not destination: nodes_suggestion = [uid for uid in transceivers \ if args.destination.lower() in uid.lower()] destination = transceivers[nodes_suggestion[0]] \ if len(nodes_suggestion)>0 else list(transceivers.values())[0] - print(f'invalid destination node specified, did you mean:\ - \n{nodes_suggestion}?\ + print(f'invalid destination node specified,\ \n{args.destination!r}, replaced with {destination.uid}') else: logger.info('No source node specified: picking random transceiver') From 90a75a9b3d091beb9575a3424220918265bb5147 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 23 Oct 2018 11:59:06 +0100 Subject: [PATCH 016/108] Removing the duplicate convert_service_sheet.py in examples directory service_sheet.py contains exactly the same function: I suggest to remove this file to clean the examples directory Signed-off-by: EstherLerouzic --- examples/convert_service_sheet.py | 204 ------------------------------ 1 file changed, 204 deletions(-) delete mode 100644 examples/convert_service_sheet.py diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py deleted file mode 100644 index fcd7c05f9..000000000 --- a/examples/convert_service_sheet.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -""" -convert_service_sheet.py -======================== - -XLS parser that can be called to create a JSON request file in accordance with -Yang model for requesting path computation. - -See: draft-ietf-teas-yang-path-computation-01.txt -""" - -from argparse import ArgumentParser -from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO -from json import dumps - -from gnpy.core.service_sheet import Request, Element, Request_element -from gnpy.core.service_sheet import parse_row, parse_excel, convert_service_sheet - -logger = getLogger(__name__) - -parser = ArgumentParser() -parser.add_argument('workbook', nargs='?', type = Path , default='meshTopologyExampleV2.xls') -parser.add_argument('-v', '--verbose', action='count') -parser.add_argument('-o', '--output', default=None) - -# Type for input data -class Request(namedtuple('Request', 'request_id source destination trx_type mode \ - spacing power nb_channel disjoint_from nodes_list is_loose')): - def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''): - return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose) - -# Type for output data: // from dutc -class Element: - def __eq__(self, other): - return type(self) == type(other) and self.uid == other.uid - def __hash__(self): - return hash((type(self), self.uid)) - -class Request_element(Element): - def __init__(self,Request,eqpt_filename): - # request_id is str - # excel has automatic number formatting that adds .0 on integer values - # the next lines recover the pure int value, assuming this .0 is unwanted - if not isinstance(Request.request_id,str): - value = str(int(Request.request_id)) - if value.endswith('.0'): - value = value[:-2] - self.request_id = value - else: - self.request_id = Request.request_id - self.source = Request.source - self.destination = Request.destination - self.srctpid = f'trx {Request.source}' - self.dsttpid = f'trx {Request.destination}' - # equipment already tests that trx_type belongs to eqpt_config.json - equipment = load_equipment(eqpt_filename) - self.trx_type = Request.trx_type - self.mode = Request.mode - # excel input are in GHz and dBm - self.spacing = Request.spacing * 1e9 - self.power = db2lin(Request.power) * 1e-3 - self.nb_channel = int(Request.nb_channel) - if not isinstance(Request.disjoint_from,str): - value = str(int(Request.disjoint_from)) - if value.endswith('.0'): - value = value[:-2] - else: - value = Request.disjoint_from - self.disjoint_from = [n for n in value.split()] - self.nodes_list = [] - if Request.nodes_list : - self.nodes_list = Request.nodes_list.split(' | ') - toberemoved = [self.source, self.destination, self.srctpid, self.dsttpid] - for n in toberemoved: - try : - self.nodes_list.remove(n) - msg = f'{n} removed from explicit path node-list' - logger.info(msg) - # print(msg) - except ValueError: - pass - # print(msg) - self.loose = 'loose' - if Request.is_loose == 'no' : - self.loose = 'strict' - - uid = property(lambda self: repr(self)) - @property - def pathrequest(self): - return { - 'request-id':self.request_id, - 'source': self.source, - 'destination': self.destination, - 'src-tp-id': self.srctpid, - 'dst-tp-id': self.dsttpid, - 'path-constraints':{ - 'te-bandwidth': { - 'technology': 'flexi-grid', - 'trx_type' : self.trx_type, - 'trx_mode' : self.mode, - 'effective-freq-slot':[{'n': 'null','m': 'null'}] , - 'spacing' : self.spacing, - 'max-nb-of-channel' : self.nb_channel, - 'output-power' : self.power - } - }, - 'optimizations': { - 'explicit-route-include-objects': [ - { - 'index': self.nodes_list.index(node), - 'unnumbered-hop':{ - 'node-id': f'{node}', - 'link-tp-id': 'link-tp-id is not used', - 'hop-type': 'loose', - 'direction': 'direction is not used' - }, - 'label-hop':{ - 'te-label': { - 'generic': 'generic is not used', - 'direction': 'direction is not used' - } - } - } - for node in self.nodes_list - ] - - } - } - @property - def pathsync(self): - if self.disjoint_from : - return {'synchronization-id':self.request_id, - 'svec': { - 'relaxable' : 'False', - 'link-diverse': 'True', - 'node-diverse': 'True', - 'request-id-number': [self.request_id]+ [n for n in self.disjoint_from] - } - } - # TO-DO: avoid multiple entries with same synchronisation vectors - @property - def json(self): - return self.pathrequest , self.pathsync - -def convert_service_sheet(input_filename, eqpt_filename, output_filename='', filter_region=[]): - service = parse_excel(input_filename) - req = [Request_element(n,eqpt_filename) for n in service] - # dumps the output into a json file with name - # split_filename = [input_filename[0:len(input_filename)-len(suffix_filename)] , suffix_filename[1:]] - if output_filename=='': - output_filename = f'{str(input_filename)[0:len(str(input_filename))-len(str(input_filename.suffixes[0]))]}_services.json' - # for debug - # print(json_filename) - data = { - 'path-request': [n.json[0] for n in req], - 'synchronization': [n.json[1] for n in req - if n.json[1] is not None] - } - with open(output_filename, 'w') as f: - f.write(dumps(data, indent=2)) - return data - -# to be used from dutc -def parse_row(row, fieldnames): - return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN]) - if r.ctype != XL_CELL_EMPTY} -# - -def parse_excel(input_filename): - with open_workbook(input_filename) as wb: - service_sheet = wb.sheet_by_name('Service') - services = list(parse_service_sheet(service_sheet)) - return services - -def parse_service_sheet(service_sheet): - logger.info(f'Validating headers on {service_sheet.name!r}') - header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]] - expected = ['route id', 'Source', 'Destination', 'TRX type', \ - 'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\ - 'routing: disjoint from', 'routing: path', 'routing: is loose?'] - if header != expected: - msg = f'Malformed header on Service sheet: {header} != {expected}' - logger.critical(msg) - raise ValueError(msg) - - service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split() - # Important Note: it reads all colum on each row so that - # it is not possible to write annotation in the excel sheet - # outside the SERVICES_COLUMN ... TO BE IMPROVED - # request_id should be unique for disjunction constraints (not used yet) - for row in all_rows(service_sheet, start=5): - yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames)) - -if __name__ == '__main__': - args = parser.parse_args() - basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL)) - logger.info(f'Converting Service sheet {args.workbook!r} into gnpy JSON format') - if args.output is None: - data = convert_service_sheet(args.workbook,'eqpt_config.json') - print(dumps(data, indent=2)) - else: - data = convert_service_sheet(args.workbook,'eqpt_config.json',args.output) From 1ba748f2a4138d4600a72f9371c85d8bedfb1c36 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 24 Oct 2018 13:22:13 +0100 Subject: [PATCH 017/108] Changing optical power and nb channels as optional input for requests - normal way is usually to apply the design optical power for all channels. This change uses the default power (same power as used for design) but enables to force an arbitrary power if needed. TODO : introduce spectral power density to apply power depending on baudrate. - definition of min max frequency and spacing define the nb of channels: uses min max frequencies and spacing to determine nb-channels. It is possible to force a different spacing for the request. TODO: check that the value is consistant with baudrate and min max values. Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 11 +++++++++-- gnpy/core/request.py | 4 +++- gnpy/core/service_sheet.py | 12 +++++++++--- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 83432c1ae..43fd8d9fd 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -62,10 +62,17 @@ def requests_from_json(json_data,equipment): params['spacing'] = req['path-constraints']['te-bandwidth']['spacing'] # 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) - params['power'] = req['path-constraints']['te-bandwidth']['output-power'] - params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + # optical power might be set differently in the request. if it is indicated then the + # params['power'] is updated + if req['path-constraints']['te-bandwidth']['output-power']: + params['power'] = req['path-constraints']['te-bandwidth']['output-power'] + # same process for nb-channel + if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] : + params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] requests_list.append(Path_request(**params)) return requests_list diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 042226412..580d6759e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -68,7 +68,8 @@ def __repr__(self): f'baud_rate:\t{self.baud_rate * 1e-9} Gbaud', f'bit_rate:\t{self.bit_rate * 1e-9} Gb/s', f'spacing:\t{self.spacing * 1e-9} GHz', - f'power: \t{round(lin2db(self.power)+30,2)} dBm' + f'power: \t{round(lin2db(self.power)+30,2)} dBm', + f'nb channels: \t{self.nb_channel}' '\n']) class Disjunction: def __init__(self, *args, **params): @@ -570,6 +571,7 @@ def __init__(self, req, pth, simplepth): for d in disjunctions_list : test_sol = True while test_sol: + # print('coucou') if candidates[d.disjunction_id] : for p in candidates[d.disjunction_id][0]: if allpaths[id(p)].req in pathreqlist_disjt: diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 4af835c35..84e2fcbc6 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -32,7 +32,7 @@ # Type for input data class Request(namedtuple('Request', 'request_id source destination trx_type mode \ spacing power nb_channel disjoint_from nodes_list is_loose')): - def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power , nb_channel , disjoint_from ='' , nodes_list = None, is_loose = ''): + def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power = None, nb_channel = None , disjoint_from ='' , nodes_list = None, is_loose = ''): return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose) # Type for output data: // from dutc @@ -73,8 +73,14 @@ def __init__(self,Request,eqpt_filename): exit() # excel input are in GHz and dBm self.spacing = Request.spacing * 1e9 - self.power = db2lin(Request.power) * 1e-3 - self.nb_channel = int(Request.nb_channel) + if Request.power : + self.power = db2lin(Request.power) * 1e-3 + else: + self.power = None + if Request.nb_channel : + self.nb_channel = int(Request.nb_channel) + else: + self.nb_channel = None if not isinstance(Request.disjoint_from,str): value = str(int(Request.disjoint_from)) if value.endswith('.0'): From 185a62958f062040176cf15e38429300e3be9171 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 24 Oct 2018 15:51:19 +0100 Subject: [PATCH 018/108] adding safe check on baudrate and spacing Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 22 ++++++++++++++++++++-- gnpy/core/equipment.py | 5 ++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 43fd8d9fd..369579cf0 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -24,7 +24,7 @@ from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element from gnpy.core.utils import load_json from gnpy.core.network import load_network, build_network, set_roadm_loss -from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch +from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, @@ -72,7 +72,25 @@ def requests_from_json(json_data,equipment): params['power'] = req['path-constraints']['te-bandwidth']['output-power'] # same process for nb-channel if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] : - params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + # check if requested nb_channels is consistant with baudrate and min-max frequencies + min_recommanded_spacing = automatic_spacing(trx_params['baud_rate']) + fmin = trx_params['frequency']['min'] + fmax = trx_params['frequency']['max'] + max_recommanded_nb_channels = automatic_nch(fmin,fmax, + min_recommanded_spacing) + + if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] <= max_recommanded_nb_channels : + params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + else: + temp = params['baud_rate'] + + msg = f'Requested channel number is not consistant with frequency range : \ + \n{fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp*1e-9} GHz \ + \nmin recommanded spacing is {min_recommanded_spacing}\ + \nmax recommanded nb of channels is {max_recommanded_nb_channels}\ + \nComputation stopped.' + logger.critical(msg) + raise ValueError(msg) requests_list.append(Path_request(**params)) return requests_list diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index eac94e1b3..52ad2db4f 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -158,6 +158,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency # TODO: novel automatic feature maybe unwanted if spacing is specified trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) + temp = trx_params['spacing'] + print(f'spacing {temp}') except StopIteration : if error_message: print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') @@ -180,7 +182,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F def automatic_spacing(baud_rate): """return the min possible channel spacing for a given baud rate""" - spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples + # TODO : this should parametrized in a cfg file + spacing_list = [(33e9,37.5e9), (38e9,50e9), (50e9,62.5e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples #[(max_baud_rate, spacing_for_this_baud_rate)] acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list)) if len(acceptable_spacing_list) < 1: From 27885a4cbcb02c49eede7a3436070a4e75762598 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 25 Oct 2018 15:29:35 +0100 Subject: [PATCH 019/108] Correction of route constraint to support any type of node correct loose or strict constraint in the new function correct_route_list correct test to find the name with existing uids remove the \u2192 char that was not well supported with excel reading Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 32 ++++++++++++++++++++------------ gnpy/core/convert.py | 8 ++++---- gnpy/core/request.py | 7 ++++--- gnpy/core/service_sheet.py | 2 ++ 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 369579cf0..be3bf875a 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -23,7 +23,7 @@ from numpy import mean from gnpy.core.service_sheet import convert_service_sheet, Request_element, Element from gnpy.core.utils import load_json -from gnpy.core.network import load_network, build_network, set_roadm_loss +from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db @@ -205,22 +205,29 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints - roadms = [n.uid for n in network.nodes() if isinstance(n, Roadm)] + anytype = [n.uid for n in network.nodes()] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): # replace possibly wrong name with a formated roadm name - if n_id not in [n.uid for n in network.nodes()]: - nodes_suggestion = [uid for uid in roadms \ + print(n_id) + if n_id not in anytype : + nodes_suggestion = [uid for uid in anytype \ if n_id.lower() in uid.lower()] - if len(nodes_suggestion)>0 : - new_n = nodes_suggestion[0] - print(f'invalid route node specified:\ - \n\'{n_id}\', replaced with \'{new_n}\'') - pathreq.nodes_list[i] = new_n + if pathreq.loose_list[i] == 'loose': + if len(nodes_suggestion)>0 : + new_n = nodes_suggestion[0] + print(f'invalid route node specified:\ + \n\'{n_id}\', replaced with \'{new_n}\'') + pathreq.nodes_list[i] = new_n + else: + print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') + pathreq.nodes_list.remove(n_id) + pathreq.loose_list.pop(i) else: - print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') - pathreq.nodes_list.remove(n_id) - pathreq.loose_list.pop(i) + msg = f'could not find node : {n_id} in network topology. Strict constraint can not be applied.' + logger.critical(msg) + raise ValueError(msg) + # TODO remove endpoints from this list in case they were added by the user in the xls or json files return pathreqlist @@ -251,6 +258,7 @@ def path_result_json(pathresult): 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) rqs = correct_route_list(network, rqs) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 38d252174..ca0c43cee 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -155,7 +155,7 @@ def convert_file(input_filename, filter_region=[]): 'longitude': x.longitude}}, 'type': 'Fused'} for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] + - [{'uid': f'fiber ({x.from_city} → {x.to_city})-{x.east_cable}', + [{'uid': f'fiber ({x.from_city} to {x.to_city})-{x.east_cable}', 'metadata': {'location': midpoint(nodes_by_city[x.from_city], nodes_by_city[x.to_city])}, 'type': 'Fiber', @@ -167,7 +167,7 @@ def convert_file(input_filename, filter_region=[]): 'con_out':x.east_con_out} } for x in links] + - [{'uid': f'fiber ({x.to_city} → {x.from_city})-{x.west_cable}', + [{'uid': f'fiber ({x.to_city} to {x.from_city})-{x.west_cable}', 'metadata': {'location': midpoint(nodes_by_city[x.from_city], nodes_by_city[x.to_city])}, 'type': 'Fiber', @@ -352,9 +352,9 @@ def fiber_link(from_city, to_city): link = links_by_city[from_city] l = next(l for l in link if l.from_city in source_dest and l.to_city in source_dest) if l.from_city == from_city: - fiber = f'fiber ({l.from_city} → {l.to_city})-{l.east_cable}' + fiber = f'fiber ({l.from_city} to {l.to_city})-{l.east_cable}' else: - fiber = f'fiber ({l.to_city} → {l.from_city})-{l.west_cable}' + fiber = f'fiber ({l.to_city} to {l.from_city})-{l.west_cable}' return fiber diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 580d6759e..6bf756386 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -236,6 +236,7 @@ def compute_constrained_path(network, req): trx = [n for n in network.nodes() if isinstance(n, Transceiver)] roadm = [n for n in network.nodes() if isinstance(n, Roadm)] edfa = [n for n in network.nodes() if isinstance(n, Edfa)] + anytypenode = [n for n in network.nodes()] source = next(el for el in trx if el.uid == req.source) # start the path with its source # TODO : avoid loops due to constraints , guess name based on string, @@ -246,16 +247,16 @@ def compute_constrained_path(network, req): node = next(el for el in trx if el.uid == n) except StopIteration: try: - node = next(el for el in roadm if el.uid == n) + node = next(el for el in anytypenode if el.uid == n) except StopIteration: try: # TODO this test is not giving good results: full name of the # amp is required to avoid ambiguity on the direction - node = next(el for el in edfa + node = next(el for el in anytypenode if el.uid.find(f'{n}')) except StopIteration: msg = f'could not find node : {n} in network topology: \ - not a trx, roadm, edfa or fused element' + not a trx, roadm, edfa, fiber or fused element' logger.critical(msg) raise ValueError(msg) # extend path list without repeating source -> skip first element in the list diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 84e2fcbc6..469a15f8c 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -111,7 +111,9 @@ def __init__(self,Request,eqpt_filename): # print(msg) self.loose = 'loose' + print(Request.is_loose) if Request.is_loose == 'no' : + print(Request.is_loose) self.loose = 'strict' uid = property(lambda self: repr(self)) From fbb4f3e5ddc9afa118d5d3a6991ecda24bbae1b1 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 26 Oct 2018 10:12:23 +0100 Subject: [PATCH 020/108] small fixes to comply with test files - re-changing to to \u2192 in convert.py to comply with test files - changing route constraint in data file : the constraint had a too ambiguous naming in the service file - test on None was incorrect in convert_sheet Signed-off-by: EstherLerouzic --- gnpy/core/convert.py | 8 ++++---- gnpy/core/service_sheet.py | 4 ++-- tests/data/meshTopologyToy_services.json | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index ca0c43cee..f07a288db 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -155,7 +155,7 @@ def convert_file(input_filename, filter_region=[]): 'longitude': x.longitude}}, 'type': 'Fused'} for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] + - [{'uid': f'fiber ({x.from_city} to {x.to_city})-{x.east_cable}', + [{'uid': f'fiber ({x.from_city} \u2192 {x.to_city})-{x.east_cable}', 'metadata': {'location': midpoint(nodes_by_city[x.from_city], nodes_by_city[x.to_city])}, 'type': 'Fiber', @@ -167,7 +167,7 @@ def convert_file(input_filename, filter_region=[]): 'con_out':x.east_con_out} } for x in links] + - [{'uid': f'fiber ({x.to_city} to {x.from_city})-{x.west_cable}', + [{'uid': f'fiber ({x.to_city} \u2192 {x.from_city})-{x.west_cable}', 'metadata': {'location': midpoint(nodes_by_city[x.from_city], nodes_by_city[x.to_city])}, 'type': 'Fiber', @@ -352,9 +352,9 @@ def fiber_link(from_city, to_city): link = links_by_city[from_city] l = next(l for l in link if l.from_city in source_dest and l.to_city in source_dest) if l.from_city == from_city: - fiber = f'fiber ({l.from_city} to {l.to_city})-{l.east_cable}' + fiber = f'fiber ({l.from_city} \u2192 {l.to_city})-{l.east_cable}' else: - fiber = f'fiber ({l.to_city} to {l.from_city})-{l.west_cable}' + fiber = f'fiber ({l.to_city} \u2192 {l.from_city})-{l.west_cable}' return fiber diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 469a15f8c..ecfbc0e13 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -73,11 +73,11 @@ def __init__(self,Request,eqpt_filename): exit() # excel input are in GHz and dBm self.spacing = Request.spacing * 1e9 - if Request.power : + if Request.power is not None: self.power = db2lin(Request.power) * 1e-3 else: self.power = None - if Request.nb_channel : + if Request.nb_channel is not None : self.nb_channel = int(Request.nb_channel) else: self.nb_channel = None diff --git a/tests/data/meshTopologyToy_services.json b/tests/data/meshTopologyToy_services.json index 2eed9ee59..4fb23d406 100644 --- a/tests/data/meshTopologyToy_services.json +++ b/tests/data/meshTopologyToy_services.json @@ -105,7 +105,7 @@ { "index": 0, "unnumbered-hop": { - "node-id": "e", + "node-id": "roadm e", "link-tp-id": "link-tp-id is not used", "hop-type": "loose", "direction": "direction is not used" @@ -120,7 +120,7 @@ { "index": 1, "unnumbered-hop": { - "node-id": "g", + "node-id": "roadm g", "link-tp-id": "link-tp-id is not used", "hop-type": "loose", "direction": "direction is not used" @@ -324,7 +324,7 @@ { "index": 0, "unnumbered-hop": { - "node-id": "e", + "node-id": "roadm e", "link-tp-id": "link-tp-id is not used", "hop-type": "loose", "direction": "direction is not used" @@ -339,7 +339,7 @@ { "index": 1, "unnumbered-hop": { - "node-id": "g", + "node-id": "roadm g", "link-tp-id": "link-tp-id is not used", "hop-type": "loose", "direction": "direction is not used" @@ -405,4 +405,4 @@ } } ] -} \ No newline at end of file +} From 48198bdd893ad0f4b80de12f9c00922b30e5cd9d Mon Sep 17 00:00:00 2001 From: ojnas Date: Sun, 28 Oct 2018 08:11:22 +0100 Subject: [PATCH 021/108] Simplify automatic spacing. --- gnpy/core/equipment.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 6f2a02045..1159cc5fa 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -182,13 +182,7 @@ def automatic_spacing(baud_rate): """return the min possible channel spacing for a given baud rate""" spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples #[(max_baud_rate, spacing_for_this_baud_rate)] - acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list)) - if len(acceptable_spacing_list) < 1: - #can't find an adequate spacing from the list, so default to: - return baud_rate*1.2 - else: - #chose the lowest possible spacing - return min(acceptable_spacing_list, key=itemgetter(0))[1] + return next((s[1] for s in spacing_list if s[0] > baud_rate), baud_rate*1.2) def automatic_nch(f_min, f_max, spacing): return int((f_max - f_min)//spacing) From bcf93e1d9f9909972c8fe7cf1e6e662a549f24cb Mon Sep 17 00:00:00 2001 From: James Powell Date: Mon, 29 Oct 2018 11:14:31 -0400 Subject: [PATCH 022/108] enforce utf-8 encoding for reading/writing JSON --- examples/create_eqpt_sheet.py | 2 +- examples/path_requests_run.py | 2 +- examples/write_path_jsontocsv.py | 4 ++-- gnpy/core/convert.py | 2 +- gnpy/core/equipment.py | 4 ++-- gnpy/core/utils.py | 6 +++--- tests/compare.py | 6 +++--- tests/test_parser.py | 8 ++++---- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/create_eqpt_sheet.py b/examples/create_eqpt_sheet.py index f1e247f73..b0f7b4ca7 100644 --- a/examples/create_eqpt_sheet.py +++ b/examples/create_eqpt_sheet.py @@ -70,7 +70,7 @@ def read_excel(input_filename): def create_eqt_template(links,nodes, links_by_src , links_by_dest, input_filename): output_filename = f'{input_filename[:-4]}_eqpt_sheet.txt' - with open(output_filename,'w') as my_file : + with open(output_filename, 'w', encoding='utf-8') as my_file: # print header similar to excel my_file.write('OPTIONAL\n\n\n\ \t\tNode a egress amp (from a to z)\t\t\t\t\tNode a ingress amp (from z to a) \ diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index c72e3c2ef..ebb201231 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -74,7 +74,7 @@ def load_requests(filename,eqpt_filename): logger.info('Automatically converting requests from XLS to JSON') json_data = convert_service_sheet(filename,eqpt_filename) else: - with open(filename) as f: + with open(filename, encoding='utf-8') as f: json_data = loads(f.read()) return json_data diff --git a/examples/write_path_jsontocsv.py b/examples/write_path_jsontocsv.py index 1a7584b32..b7b50dd27 100644 --- a/examples/write_path_jsontocsv.py +++ b/examples/write_path_jsontocsv.py @@ -26,8 +26,8 @@ if __name__ == '__main__': args = parser.parse_args() - with open(args.output_filename,"w") as file : - with open(args.filename) as f: + with open(args.output_filename, 'w', encoding='utf-8') as file: + with open(args.filename, encoding='utf-8') as f: print(f'Reading {args.filename}') json_data = loads(f.read()) equipment = load_equipment(args.eqpt_filename) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 38d252174..89c5d23d7 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -220,7 +220,7 @@ def convert_file(input_filename, filter_region=[]): full_input_filename = str(input_filename) split_filename = [full_input_filename[0:len(full_input_filename)-len(suffix_filename)] , suffix_filename[1:]] output_json_file_name = split_filename[0]+'.json' - with open(output_json_file_name,'w') as edfa_json_file: + with open(output_json_file_name, 'w', encoding='utf-8') as edfa_json_file: edfa_json_file.write(dumps(data, indent=2)) return output_json_file_name diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 6f2a02045..41bf867f2 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -43,13 +43,13 @@ def __new__(cls, @classmethod def from_advanced_json(cls, filename, **kwargs): - with open(filename) as f: + with open(filename, encoding='utf-8') as f: json_data = load(f) return cls(**{**kwargs, **json_data, 'type_def':None, 'nf_model':None}) @classmethod def from_default_json(cls, filename, **kwargs): - with open(filename) as f: + with open(filename, encoding='utf-8') as f: json_data = load(f) type_variety = kwargs['type_variety'] type_def = kwargs.get('type_def', 'variable_gain') #default compatibility with older json eqpt files diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 96d1716f3..05c46cf38 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -18,13 +18,13 @@ def load_json(filename): - with open(filename, 'r') as f: + with open(filename, 'r', encoding='utf-8') as f: data = json.load(f) return data def save_json(obj, filename): - with open(filename, 'w') as f: + with open(filename, 'w', encoding='utf-8') as f: json.dump(obj, f, indent=2) def write_csv(obj, filename): @@ -55,7 +55,7 @@ def write_csv(obj, filename): result_category 2 ... """ - with open(filename, 'w') as f: + with open(filename, 'w', encoding='utf-8') as f: w = writer(f) for data_key, data_list in obj.items(): #main header diff --git a/tests/compare.py b/tests/compare.py index d208969b8..1b5ed9e0a 100644 --- a/tests/compare.py +++ b/tests/compare.py @@ -105,16 +105,16 @@ def encode_sets(obj): if __name__ == '__main__': args = parser.parse_args() - with open(args.expected_output) as f: + with open(args.expected_output, encoding='utf-8') as f: expected = load(f) - with open(args.actual_output) as f: + with open(args.actual_output, encoding='utf-8') as f: actual = load(f) result = COMPARISONS[args.comparison](expected, actual) if args.output: - with open(args.output, 'w') as f: + with open(args.output, 'w', encoding='utf-8') as f: dump(result, f, default=encode_sets, indent=2) else: print(str(result)) diff --git a/tests/test_parser.py b/tests/test_parser.py index dbfe94b9e..751bcb382 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -37,11 +37,11 @@ def test_excel_json_generation(xls_input, expected_json_output): convert_file(xls_input) actual_json_output = xls_input.with_suffix('.json') - with open(actual_json_output) as f: + with open(actual_json_output, encoding='utf-8') as f: actual = load(f) unlink(actual_json_output) - with open(expected_json_output) as f: + with open(expected_json_output, encoding='utf-8') as f: expected = load(f) results = compare_networks(expected, actual) @@ -65,11 +65,11 @@ def test_excel_service_json_generation(xls_input, expected_json_output): convert_service_sheet(xls_input, eqpt_filename) actual_json_output = f'{str(xls_input)[:-4]}_services.json' - with open(actual_json_output) as f: + with open(actual_json_output, encoding='utf-8') as f: actual = load(f) unlink(actual_json_output) - with open(expected_json_output) as f: + with open(expected_json_output, encoding='utf-8') as f: expected = load(f) results = compare_services(expected, actual) From 4f4f05abdfbabe7b46da085ff4f1af7a763e4b98 Mon Sep 17 00:00:00 2001 From: James Powell Date: Mon, 29 Oct 2018 11:17:13 -0400 Subject: [PATCH 023/108] allow JSON to encode UTF-8 --- examples/convert_service_sheet.py | 4 ++-- examples/path_requests_run.py | 2 +- gnpy/core/convert.py | 4 +--- gnpy/core/service_sheet.py | 2 +- gnpy/core/utils.py | 2 +- tests/compare.py | 2 +- tests/test_amplifier.py | 2 +- tests/test_propagation.py | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/examples/convert_service_sheet.py b/examples/convert_service_sheet.py index 4fbe8881a..b05f62c92 100644 --- a/examples/convert_service_sheet.py +++ b/examples/convert_service_sheet.py @@ -25,7 +25,7 @@ basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL)) logger.info(f'Converting Service sheet {args.workbook!r} into gnpy JSON format') if args.output is None: - data = convert_service_sheet(args.workbook,'eqpt_config.json') - print(dumps(data, indent=2)) + data = convert_service_sheet(args.workbook, 'eqpt_config.json') + print(dumps(data, indent=2, ensure_ascii=False)) else: data = convert_service_sheet(args.workbook,'eqpt_config.json',args.output) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index ebb201231..c97d9468b 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -159,7 +159,7 @@ def path_result_json(pathresult): for p in test: result.append(Result_element(pths[test.index(p)],p)) with open(args.output, 'w') as f: - f.write(dumps(path_result_json(result), indent=2)) + f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) fnamecsv = next(s for s in args.output.split('.')) + '.csv' with open(fnamecsv,"w") as fcsv : jsontocsv(path_result_json(result),equipment,fcsv) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 89c5d23d7..ec41641cb 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -214,14 +214,12 @@ def convert_file(input_filename, filter_region=[]): for x in nodes_by_city.values() if x.node_type.lower()=='roadm']))) } - #print(dumps(data, indent=2)) - # output_json_file_name = input_filename.split(".")[0]+".json" suffix_filename = str(input_filename.suffixes[0]) full_input_filename = str(input_filename) split_filename = [full_input_filename[0:len(full_input_filename)-len(suffix_filename)] , suffix_filename[1:]] output_json_file_name = split_filename[0]+'.json' with open(output_json_file_name, 'w', encoding='utf-8') as edfa_json_file: - edfa_json_file.write(dumps(data, indent=2)) + edfa_json_file.write(dumps(data, indent=2, ensure_ascii=False)) return output_json_file_name def parse_excel(input_filename): diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 1145bb01a..450b99d13 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -181,7 +181,7 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil if n.json[1] is not None] } with open(output_filename, 'w') as f: - f.write(dumps(data, indent=2)) + f.write(dumps(data, indent=2, ensure_ascii=False)) return data # to be used from dutc diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 05c46cf38..44c4c0999 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -25,7 +25,7 @@ def load_json(filename): def save_json(obj, filename): with open(filename, 'w', encoding='utf-8') as f: - json.dump(obj, f, indent=2) + json.dump(obj, f, indent=2, ensure_ascii=False) def write_csv(obj, filename): """ diff --git a/tests/compare.py b/tests/compare.py index 1b5ed9e0a..0a44826bd 100644 --- a/tests/compare.py +++ b/tests/compare.py @@ -115,6 +115,6 @@ def encode_sets(obj): if args.output: with open(args.output, 'w', encoding='utf-8') as f: - dump(result, f, default=encode_sets, indent=2) + dump(result, f, default=encode_sets, indent=2, ensure_ascii=False) else: print(str(result)) diff --git a/tests/test_amplifier.py b/tests/test_amplifier.py index e6eceffb7..ace6b0941 100644 --- a/tests/test_amplifier.py +++ b/tests/test_amplifier.py @@ -5,7 +5,7 @@ from gnpy.core.elements import Edfa from numpy import zeros, array -from json import load, dumps +from json import load from gnpy.core.elements import Transceiver, Fiber, Edfa from gnpy.core.utils import lin2db, db2lin from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref diff --git a/tests/test_propagation.py b/tests/test_propagation.py index 084000243..c2e77ec76 100644 --- a/tests/test_propagation.py +++ b/tests/test_propagation.py @@ -5,7 +5,7 @@ from gnpy.core.elements import Edfa import numpy as np -from json import load, dumps +from json import load import pytest from gnpy.core.elements import Transceiver, Fiber, Edfa from gnpy.core.utils import lin2db, db2lin From e04afdbe4c0913dd9cfb668a6b32d099f9b4b5e6 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 30 Oct 2018 09:27:12 +0100 Subject: [PATCH 024/108] Update examples/path_requests_run.py Co-Authored-By: EstherLerouzic --- examples/path_requests_run.py | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index be3bf875a..ddd1751a3 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -47,7 +47,6 @@ def requests_from_json(json_data,equipment): requests_list = [] for req in json_data['path-request']: - # print(f'{req}') # init all params from request params = {} params['request_id'] = req['request-id'] From af9ba2750d5904c322adb29af64c682636779626 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 30 Oct 2018 09:27:52 +0100 Subject: [PATCH 025/108] Update examples/path_requests_run.py Co-Authored-By: EstherLerouzic --- examples/path_requests_run.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index ddd1751a3..aced5885e 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -83,7 +83,12 @@ def requests_from_json(json_data,equipment): else: temp = params['baud_rate'] - msg = f'Requested channel number is not consistant with frequency range : \ + msg = dedent(f''' + Requested channel number is not consistent with frequency range: + {fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp*1e-9} GHz + min recommanded spacing is {min_recommanded_spacing} + max recommanded nb of channels is {max_recommanded_nb_channels} + Computation stopped.''') \n{fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp*1e-9} GHz \ \nmin recommanded spacing is {min_recommanded_spacing}\ \nmax recommanded nb of channels is {max_recommanded_nb_channels}\ From efc846826848ca13f7d8c8f0d8c33ab661f16247 Mon Sep 17 00:00:00 2001 From: ojnas Date: Tue, 30 Oct 2018 09:48:59 +0100 Subject: [PATCH 026/108] Set type of all nodes to ROADM in CORONET Glabal. --- README.rst | 3 +- examples/CORONET_Global_Topology.json | 4106 ++++++++++++++++++------- examples/CORONET_Global_Topology.xls | Bin 48128 -> 49664 bytes 3 files changed, 3034 insertions(+), 1075 deletions(-) diff --git a/README.rst b/README.rst index 2f1eb33ff..1c8121e14 100644 --- a/README.rst +++ b/README.rst @@ -152,8 +152,7 @@ further instructions on how to prepare the Excel input file, see The main transmission example will calculate the average signal OSNR and SNR across network elements (transceiver, ROADMs, fibers, and amplifiers) between two transceivers selected by the user. (By default, for the CORONET Global -network, it will show the transmission of spectral information between Albuquerque, -New Mexico and Atlanta, Georgia.) +network, it will show the transmission of spectral information between Abilene, Texas and Albany, New York.) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. diff --git a/examples/CORONET_Global_Topology.json b/examples/CORONET_Global_Topology.json index 4dd8106b5..02657f9ad 100644 --- a/examples/CORONET_Global_Topology.json +++ b/examples/CORONET_Global_Topology.json @@ -1,5 +1,29 @@ { "elements": [ + { + "uid": "trx Abilene", + "metadata": { + "location": { + "city": "Abilene", + "region": "CONUS", + "latitude": 32.45, + "longitude": -99.739998 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Albany", + "metadata": { + "location": { + "city": "Albany", + "region": "CONUS", + "latitude": 42.6699982, + "longitude": -73.8000029 + } + }, + "type": "Transceiver" + }, { "uid": "trx Albuquerque", "metadata": { @@ -24,6 +48,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Austin", + "metadata": { + "location": { + "city": "Austin", + "region": "CONUS", + "latitude": 30.3099988, + "longitude": -97.7500018 + } + }, + "type": "Transceiver" + }, { "uid": "trx Baltimore", "metadata": { @@ -36,6 +72,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Baton_Rouge", + "metadata": { + "location": { + "city": "Baton_Rouge", + "region": "CONUS", + "latitude": 30.4499996, + "longitude": -91.1299968 + } + }, + "type": "Transceiver" + }, { "uid": "trx Billings", "metadata": { @@ -60,6 +108,66 @@ }, "type": "Transceiver" }, + { + "uid": "trx Bismarck", + "metadata": { + "location": { + "city": "Bismarck", + "region": "CONUS", + "latitude": 46.81000154, + "longitude": -100.7699965 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Boston", + "metadata": { + "location": { + "city": "Boston", + "region": "CONUS", + "latitude": 42.3400005, + "longitude": -71.0199959 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Buffalo", + "metadata": { + "location": { + "city": "Buffalo", + "region": "CONUS", + "latitude": 42.8899993, + "longitude": -78.860001 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Charleston", + "metadata": { + "location": { + "city": "Charleston", + "region": "CONUS", + "latitude": 32.7900008, + "longitude": -79.9899982 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Charlotte", + "metadata": { + "location": { + "city": "Charlotte", + "region": "CONUS", + "latitude": 35.2, + "longitude": -80.83 + } + }, + "type": "Transceiver" + }, { "uid": "trx Chicago", "metadata": { @@ -132,6 +240,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Detroit", + "metadata": { + "location": { + "city": "Detroit", + "region": "CONUS", + "latitude": 42.3800019, + "longitude": -83.0999998 + } + }, + "type": "Transceiver" + }, { "uid": "trx El_Paso", "metadata": { @@ -168,6 +288,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Hartford", + "metadata": { + "location": { + "city": "Hartford", + "region": "CONUS", + "latitude": 41.7700004, + "longitude": -72.6800003 + } + }, + "type": "Transceiver" + }, { "uid": "trx Houston", "metadata": { @@ -216,6 +348,30 @@ }, "type": "Transceiver" }, + { + "uid": "trx Little_Rock", + "metadata": { + "location": { + "city": "Little_Rock", + "region": "CONUS", + "latitude": 34.72, + "longitude": -92.35 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Long_Island", + "metadata": { + "location": { + "city": "Long_Island", + "region": "CONUS", + "latitude": 40.5899999, + "longitude": -73.6699993 + } + }, + "type": "Transceiver" + }, { "uid": "trx Los_Angeles", "metadata": { @@ -240,6 +396,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Memphis", + "metadata": { + "location": { + "city": "Memphis", + "region": "CONUS", + "latitude": 35.110001, + "longitude": -90.010004 + } + }, + "type": "Transceiver" + }, { "uid": "trx Miami", "metadata": { @@ -252,6 +420,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Milwaukee", + "metadata": { + "location": { + "city": "Milwaukee", + "region": "CONUS", + "latitude": 43.0600013, + "longitude": -87.9700005 + } + }, + "type": "Transceiver" + }, { "uid": "trx Minneapolis", "metadata": { @@ -300,6 +480,30 @@ }, "type": "Transceiver" }, + { + "uid": "trx Newark", + "metadata": { + "location": { + "city": "Newark", + "region": "CONUS", + "latitude": 40.7200012, + "longitude": -74.1699986 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Norfolk", + "metadata": { + "location": { + "city": "Norfolk", + "region": "CONUS", + "latitude": 36.9199982, + "longitude": -76.2399978 + } + }, + "type": "Transceiver" + }, { "uid": "trx Oakland", "metadata": { @@ -312,6 +516,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Oklahoma_City", + "metadata": { + "location": { + "city": "Oklahoma_City", + "region": "CONUS", + "latitude": 35.4700015, + "longitude": -97.5100028 + } + }, + "type": "Transceiver" + }, { "uid": "trx Omaha", "metadata": { @@ -324,6 +540,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Orlando", + "metadata": { + "location": { + "city": "Orlando", + "region": "CONUS", + "latitude": 28.4999994, + "longitude": -81.370003 + } + }, + "type": "Transceiver" + }, { "uid": "trx Philadelphia", "metadata": { @@ -372,6 +600,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Providence", + "metadata": { + "location": { + "city": "Providence", + "region": "CONUS", + "latitude": 41.82, + "longitude": -71.42 + } + }, + "type": "Transceiver" + }, { "uid": "trx Raleigh", "metadata": { @@ -385,181 +625,493 @@ "type": "Transceiver" }, { - "uid": "trx Salt_Lake_City", + "uid": "trx Richmond", "metadata": { "location": { - "city": "Salt_Lake_City", + "city": "Richmond", "region": "CONUS", - "latitude": 40.77999863, - "longitude": -111.9300007 + "latitude": 37.5299986, + "longitude": -77.4700015 } }, "type": "Transceiver" }, { - "uid": "trx Scranton", + "uid": "trx Rochester", "metadata": { "location": { - "city": "Scranton", + "city": "Rochester", "region": "CONUS", - "latitude": 41.4, - "longitude": -75.67 + "latitude": 43.1699985, + "longitude": -77.620003 } }, "type": "Transceiver" }, { - "uid": "trx St_Louis", + "uid": "trx Sacramento", "metadata": { "location": { - "city": "St_Louis", + "city": "Sacramento", "region": "CONUS", - "latitude": 38.64, - "longitude": -90.24 + "latitude": 38.56999946, + "longitude": -121.4700016 } }, "type": "Transceiver" }, { - "uid": "trx Syracuse", + "uid": "trx Salt_Lake_City", "metadata": { "location": { - "city": "Syracuse", + "city": "Salt_Lake_City", "region": "CONUS", - "latitude": 43.040001, - "longitude": -76.1399993 + "latitude": 40.77999863, + "longitude": -111.9300007 } }, "type": "Transceiver" }, { - "uid": "trx Washington_DC", + "uid": "trx San_Antonio", "metadata": { "location": { - "city": "Washington_DC", + "city": "San_Antonio", "region": "CONUS", - "latitude": 38.9100003, - "longitude": -77.0199965 + "latitude": 29.459997, + "longitude": -98.510002 } }, "type": "Transceiver" }, { - "uid": "trx Amsterdam", + "uid": "trx San_Diego", "metadata": { "location": { - "city": "Amsterdam", - "region": "Europe", - "latitude": 52.3699996, - "longitude": 4.88999915 + "city": "San_Diego", + "region": "CONUS", + "latitude": 32.8100017, + "longitude": -117.139999 } }, "type": "Transceiver" }, { - "uid": "trx Istanbul", + "uid": "trx San_Francisco", "metadata": { "location": { - "city": "Istanbul", - "region": "Europe", - "latitude": 41.1, - "longitude": 29.0 + "city": "San_Francisco", + "region": "CONUS", + "latitude": 37.65999942, + "longitude": -122.4199987 } }, "type": "Transceiver" }, { - "uid": "trx London", + "uid": "trx San_Jose", "metadata": { "location": { - "city": "London", - "region": "Europe", - "latitude": 51.5200005, - "longitude": -0.100000296 + "city": "San_Jose", + "region": "CONUS", + "latitude": 37.29999947, + "longitude": -121.8499985 } }, "type": "Transceiver" }, { - "uid": "trx Paris", + "uid": "trx Santa_Barbara", "metadata": { "location": { - "city": "Paris", - "region": "Europe", - "latitude": 48.86, - "longitude": 2.3399995 + "city": "Santa_Barbara", + "region": "CONUS", + "latitude": 34.43000021, + "longitude": -119.7200014 } }, "type": "Transceiver" }, { - "uid": "trx Rome", + "uid": "trx Scranton", "metadata": { "location": { - "city": "Rome", - "region": "Europe", - "latitude": 41.8899996, - "longitude": 12.5000004 + "city": "Scranton", + "region": "CONUS", + "latitude": 41.4, + "longitude": -75.67 } }, "type": "Transceiver" }, { - "uid": "trx Vienna", + "uid": "trx Seattle", "metadata": { "location": { - "city": "Vienna", - "region": "Europe", - "latitude": 48.2200024, - "longitude": 16.3700005 + "city": "Seattle", + "region": "CONUS", + "latitude": 47.61999916, + "longitude": -122.3499985 } }, "type": "Transceiver" }, { - "uid": "trx Warsaw", + "uid": "trx Spokane", "metadata": { "location": { - "city": "Warsaw", - "region": "Europe", - "latitude": 52.2599987, - "longitude": 21.0200005 + "city": "Spokane", + "region": "CONUS", + "latitude": 47.66999805, + "longitude": -117.4100038 } }, "type": "Transceiver" }, { - "uid": "trx Delhi", + "uid": "trx Springfield", "metadata": { "location": { - "city": "Delhi", - "region": "Asia", - "latitude": 28.6700003, - "longitude": 77.2099989 + "city": "Springfield", + "region": "CONUS", + "latitude": 39.5, + "longitude": -89.4 } }, "type": "Transceiver" }, { - "uid": "trx Hong_Kong", + "uid": "trx St_Louis", "metadata": { "location": { - "city": "Hong_Kong", - "region": "Asia", - "latitude": 22.267, - "longitude": 114.14 + "city": "St_Louis", + "region": "CONUS", + "latitude": 38.64, + "longitude": -90.24 } }, "type": "Transceiver" }, { - "uid": "trx Honolulu", + "uid": "trx Syracuse", "metadata": { "location": { - "city": "Honolulu", - "region": "Asia", - "latitude": 21.3199996, - "longitude": -157.800003 + "city": "Syracuse", + "region": "CONUS", + "latitude": 43.040001, + "longitude": -76.1399993 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tallahassee", + "metadata": { + "location": { + "city": "Tallahassee", + "region": "CONUS", + "latitude": 30.46, + "longitude": -84.28 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tampa", + "metadata": { + "location": { + "city": "Tampa", + "region": "CONUS", + "latitude": 27.9599988, + "longitude": -82.4800035 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Toledo", + "metadata": { + "location": { + "city": "Toledo", + "region": "CONUS", + "latitude": 41.659997, + "longitude": -83.58 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tucson", + "metadata": { + "location": { + "city": "Tucson", + "region": "CONUS", + "latitude": 32.2, + "longitude": -110.89 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tulsa", + "metadata": { + "location": { + "city": "Tulsa", + "region": "CONUS", + "latitude": 36.13, + "longitude": -95.92 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Washington_DC", + "metadata": { + "location": { + "city": "Washington_DC", + "region": "CONUS", + "latitude": 38.9100003, + "longitude": -77.0199965 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx West_Palm_Beach", + "metadata": { + "location": { + "city": "West_Palm_Beach", + "region": "CONUS", + "latitude": 26.7499997, + "longitude": -80.1299975 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Wilmington", + "metadata": { + "location": { + "city": "Wilmington", + "region": "CONUS", + "latitude": 39.7400018, + "longitude": -75.5299989 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Amsterdam", + "metadata": { + "location": { + "city": "Amsterdam", + "region": "Europe", + "latitude": 52.3699996, + "longitude": 4.88999915 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Berlin", + "metadata": { + "location": { + "city": "Berlin", + "region": "Europe", + "latitude": 52.520002, + "longitude": 13.379995 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Brussels", + "metadata": { + "location": { + "city": "Brussels", + "region": "Europe", + "latitude": 50.830002, + "longitude": 4.330002 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Bucharest", + "metadata": { + "location": { + "city": "Bucharest", + "region": "Europe", + "latitude": 44.44, + "longitude": 26.1 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Frankfurt", + "metadata": { + "location": { + "city": "Frankfurt", + "region": "Europe", + "latitude": 50.1199992, + "longitude": 8.68000104 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Istanbul", + "metadata": { + "location": { + "city": "Istanbul", + "region": "Europe", + "latitude": 41.1, + "longitude": 29.0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx London", + "metadata": { + "location": { + "city": "London", + "region": "Europe", + "latitude": 51.5200005, + "longitude": -0.100000296 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Madrid", + "metadata": { + "location": { + "city": "Madrid", + "region": "Europe", + "latitude": 40.419998, + "longitude": -3.7100002 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Paris", + "metadata": { + "location": { + "city": "Paris", + "region": "Europe", + "latitude": 48.86, + "longitude": 2.3399995 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Rome", + "metadata": { + "location": { + "city": "Rome", + "region": "Europe", + "latitude": 41.8899996, + "longitude": 12.5000004 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Vienna", + "metadata": { + "location": { + "city": "Vienna", + "region": "Europe", + "latitude": 48.2200024, + "longitude": 16.3700005 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Warsaw", + "metadata": { + "location": { + "city": "Warsaw", + "region": "Europe", + "latitude": 52.2599987, + "longitude": 21.0200005 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Zurich", + "metadata": { + "location": { + "city": "Zurich", + "region": "Europe", + "latitude": 47.3800015, + "longitude": 8.5399996 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Bangkok", + "metadata": { + "location": { + "city": "Bangkok", + "region": "Asia", + "latitude": 13.73, + "longitude": 100.5 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Beijing", + "metadata": { + "location": { + "city": "Beijing", + "region": "Asia", + "latitude": 39.92999979, + "longitude": 116.4000013 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Delhi", + "metadata": { + "location": { + "city": "Delhi", + "region": "Asia", + "latitude": 28.6700003, + "longitude": 77.2099989 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Hong_Kong", + "metadata": { + "location": { + "city": "Hong_Kong", + "region": "Asia", + "latitude": 22.267, + "longitude": 114.14 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Honolulu", + "metadata": { + "location": { + "city": "Honolulu", + "region": "Asia", + "latitude": 21.3199996, + "longitude": -157.800003 } }, "type": "Transceiver" @@ -568,502 +1120,1042 @@ "uid": "trx Mumbai", "metadata": { "location": { - "city": "Mumbai", - "region": "Asia", - "latitude": 18.9599987, - "longitude": 72.8199999 + "city": "Mumbai", + "region": "Asia", + "latitude": 18.9599987, + "longitude": 72.8199999 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Seoul", + "metadata": { + "location": { + "city": "Seoul", + "region": "Asia", + "latitude": 37.56000108, + "longitude": 126.9899988 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Shanghai", + "metadata": { + "location": { + "city": "Shanghai", + "region": "Asia", + "latitude": 31.23, + "longitude": 121.47 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Singapore", + "metadata": { + "location": { + "city": "Singapore", + "region": "Asia", + "latitude": 1.299999907, + "longitude": 103.8499992 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Sydney", + "metadata": { + "location": { + "city": "Sydney", + "region": "Asia", + "latitude": -33.86999896, + "longitude": 151.2100066 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Taipei", + "metadata": { + "location": { + "city": "Taipei", + "region": "Asia", + "latitude": 25.0200005, + "longitude": 121.449997 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tokyo", + "metadata": { + "location": { + "city": "Tokyo", + "region": "Asia", + "latitude": 35.6699986, + "longitude": 139.770004 + } + }, + "type": "Transceiver" + }, + { + "uid": "roadm Abilene", + "metadata": { + "location": { + "city": "Abilene", + "region": "CONUS", + "latitude": 32.45, + "longitude": -99.739998 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Albany", + "metadata": { + "location": { + "city": "Albany", + "region": "CONUS", + "latitude": 42.6699982, + "longitude": -73.8000029 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Albuquerque", + "metadata": { + "location": { + "city": "Albuquerque", + "region": "CONUS", + "latitude": 35.119977, + "longitude": -106.61997 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Atlanta", + "metadata": { + "location": { + "city": "Atlanta", + "region": "CONUS", + "latitude": 33.7599982, + "longitude": -84.4199987 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Austin", + "metadata": { + "location": { + "city": "Austin", + "region": "CONUS", + "latitude": 30.3099988, + "longitude": -97.7500018 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Baltimore", + "metadata": { + "location": { + "city": "Baltimore", + "region": "CONUS", + "latitude": 39.2999992, + "longitude": -76.6100008 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Baton_Rouge", + "metadata": { + "location": { + "city": "Baton_Rouge", + "region": "CONUS", + "latitude": 30.4499996, + "longitude": -91.1299968 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Billings", + "metadata": { + "location": { + "city": "Billings", + "region": "CONUS", + "latitude": 45.79000104, + "longitude": -108.5400006 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Birmingham", + "metadata": { + "location": { + "city": "Birmingham", + "region": "CONUS", + "latitude": 33.5299985, + "longitude": -86.8000029 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Bismarck", + "metadata": { + "location": { + "city": "Bismarck", + "region": "CONUS", + "latitude": 46.81000154, + "longitude": -100.7699965 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Boston", + "metadata": { + "location": { + "city": "Boston", + "region": "CONUS", + "latitude": 42.3400005, + "longitude": -71.0199959 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Buffalo", + "metadata": { + "location": { + "city": "Buffalo", + "region": "CONUS", + "latitude": 42.8899993, + "longitude": -78.860001 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Charleston", + "metadata": { + "location": { + "city": "Charleston", + "region": "CONUS", + "latitude": 32.7900008, + "longitude": -79.9899982 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Charlotte", + "metadata": { + "location": { + "city": "Charlotte", + "region": "CONUS", + "latitude": 35.2, + "longitude": -80.83 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Chicago", + "metadata": { + "location": { + "city": "Chicago", + "region": "CONUS", + "latitude": 41.839997, + "longitude": -87.680001 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Cincinnati", + "metadata": { + "location": { + "city": "Cincinnati", + "region": "CONUS", + "latitude": 39.1399991, + "longitude": -84.5100027 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Cleveland", + "metadata": { + "location": { + "city": "Cleveland", + "region": "CONUS", + "latitude": 41.4799992, + "longitude": -81.6800014 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Columbus", + "metadata": { + "location": { + "city": "Columbus", + "region": "CONUS", + "latitude": 39.990002, + "longitude": -82.989997 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Dallas", + "metadata": { + "location": { + "city": "Dallas", + "region": "CONUS", + "latitude": 32.79, + "longitude": -96.77 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Denver", + "metadata": { + "location": { + "city": "Denver", + "region": "CONUS", + "latitude": 39.77000271, + "longitude": -104.8700036 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Detroit", + "metadata": { + "location": { + "city": "Detroit", + "region": "CONUS", + "latitude": 42.3800019, + "longitude": -83.0999998 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm El_Paso", + "metadata": { + "location": { + "city": "El_Paso", + "region": "CONUS", + "latitude": 31.84981, + "longitude": -106.4396 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Fresno", + "metadata": { + "location": { + "city": "Fresno", + "region": "CONUS", + "latitude": 36.7800007, + "longitude": -119.790002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Greensboro", + "metadata": { + "location": { + "city": "Greensboro", + "region": "CONUS", + "latitude": 36.0800024, + "longitude": -79.8300018 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Hartford", + "metadata": { + "location": { + "city": "Hartford", + "region": "CONUS", + "latitude": 41.7700004, + "longitude": -72.6800003 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Houston", + "metadata": { + "location": { + "city": "Houston", + "region": "CONUS", + "latitude": 29.77, + "longitude": -95.39 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Jacksonville", + "metadata": { + "location": { + "city": "Jacksonville", + "region": "CONUS", + "latitude": 30.330003, + "longitude": -81.660002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Kansas_City", + "metadata": { + "location": { + "city": "Kansas_City", + "region": "CONUS", + "latitude": 39.1199992, + "longitude": -94.7300038 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Las_Vegas", + "metadata": { + "location": { + "city": "Las_Vegas", + "region": "CONUS", + "latitude": 36.20999, + "longitude": -115.2199 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Little_Rock", + "metadata": { + "location": { + "city": "Little_Rock", + "region": "CONUS", + "latitude": 34.72, + "longitude": -92.35 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Long_Island", + "metadata": { + "location": { + "city": "Long_Island", + "region": "CONUS", + "latitude": 40.5899999, + "longitude": -73.6699993 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Los_Angeles", + "metadata": { + "location": { + "city": "Los_Angeles", + "region": "CONUS", + "latitude": 34.110001, + "longitude": -118.410002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Louisville", + "metadata": { + "location": { + "city": "Louisville", + "region": "CONUS", + "latitude": 38.2200009, + "longitude": -85.7399979 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Memphis", + "metadata": { + "location": { + "city": "Memphis", + "region": "CONUS", + "latitude": 35.110001, + "longitude": -90.010004 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Sydney", + "uid": "roadm Miami", "metadata": { "location": { - "city": "Sydney", - "region": "Asia", - "latitude": -33.86999896, - "longitude": 151.2100066 + "city": "Miami", + "region": "CONUS", + "latitude": 25.7800006, + "longitude": -80.2099997 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Taipei", + "uid": "roadm Milwaukee", "metadata": { "location": { - "city": "Taipei", - "region": "Asia", - "latitude": 25.0200005, - "longitude": 121.449997 + "city": "Milwaukee", + "region": "CONUS", + "latitude": 43.0600013, + "longitude": -87.9700005 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Tokyo", + "uid": "roadm Minneapolis", "metadata": { "location": { - "city": "Tokyo", - "region": "Asia", - "latitude": 35.6699986, - "longitude": 139.770004 + "city": "Minneapolis", + "region": "CONUS", + "latitude": 44.9599988, + "longitude": -93.2699973 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "roadm Albuquerque", + "uid": "roadm Nashville", "metadata": { "location": { - "city": "Albuquerque", + "city": "Nashville", "region": "CONUS", - "latitude": 35.119977, - "longitude": -106.61997 + "latitude": 36.1699984, + "longitude": -86.7799989 } }, "type": "Roadm" }, { - "uid": "roadm Atlanta", + "uid": "roadm New_Orleans", "metadata": { "location": { - "city": "Atlanta", + "city": "New_Orleans", "region": "CONUS", - "latitude": 33.7599982, - "longitude": -84.4199987 + "latitude": 30.07, + "longitude": -89.93 } }, "type": "Roadm" }, { - "uid": "roadm Baltimore", + "uid": "roadm New_York", "metadata": { "location": { - "city": "Baltimore", + "city": "New_York", "region": "CONUS", - "latitude": 39.2999992, - "longitude": -76.6100008 + "latitude": 40.6699983, + "longitude": -73.9400035 } }, "type": "Roadm" }, { - "uid": "roadm Billings", + "uid": "roadm Newark", "metadata": { "location": { - "city": "Billings", + "city": "Newark", "region": "CONUS", - "latitude": 45.79000104, - "longitude": -108.5400006 + "latitude": 40.7200012, + "longitude": -74.1699986 } }, "type": "Roadm" }, { - "uid": "roadm Birmingham", + "uid": "roadm Norfolk", "metadata": { "location": { - "city": "Birmingham", + "city": "Norfolk", "region": "CONUS", - "latitude": 33.5299985, - "longitude": -86.8000029 + "latitude": 36.9199982, + "longitude": -76.2399978 } }, "type": "Roadm" }, { - "uid": "roadm Chicago", + "uid": "roadm Oakland", "metadata": { "location": { - "city": "Chicago", + "city": "Oakland", "region": "CONUS", - "latitude": 41.839997, - "longitude": -87.680001 + "latitude": 37.77000071, + "longitude": -122.2200016 } }, "type": "Roadm" }, { - "uid": "roadm Cincinnati", + "uid": "roadm Oklahoma_City", "metadata": { "location": { - "city": "Cincinnati", + "city": "Oklahoma_City", "region": "CONUS", - "latitude": 39.1399991, - "longitude": -84.5100027 + "latitude": 35.4700015, + "longitude": -97.5100028 } }, "type": "Roadm" }, { - "uid": "roadm Cleveland", + "uid": "roadm Omaha", "metadata": { "location": { - "city": "Cleveland", + "city": "Omaha", "region": "CONUS", - "latitude": 41.4799992, - "longitude": -81.6800014 + "latitude": 41.2599984, + "longitude": -96.0100022 } }, "type": "Roadm" }, { - "uid": "roadm Columbus", + "uid": "roadm Orlando", "metadata": { "location": { - "city": "Columbus", + "city": "Orlando", "region": "CONUS", - "latitude": 39.990002, - "longitude": -82.989997 + "latitude": 28.4999994, + "longitude": -81.370003 } }, "type": "Roadm" }, { - "uid": "roadm Dallas", + "uid": "roadm Philadelphia", "metadata": { "location": { - "city": "Dallas", + "city": "Philadelphia", "region": "CONUS", - "latitude": 32.79, - "longitude": -96.77 + "latitude": 40.0099985, + "longitude": -75.1299964 } }, "type": "Roadm" }, { - "uid": "roadm Denver", + "uid": "roadm Phoenix", "metadata": { "location": { - "city": "Denver", + "city": "Phoenix", "region": "CONUS", - "latitude": 39.77000271, - "longitude": -104.8700036 + "latitude": 33.54000058, + "longitude": -112.0699996 } }, "type": "Roadm" }, { - "uid": "roadm El_Paso", + "uid": "roadm Pittsburgh", "metadata": { "location": { - "city": "El_Paso", + "city": "Pittsburgh", "region": "CONUS", - "latitude": 31.84981, - "longitude": -106.4396 + "latitude": 40.3, + "longitude": -80.13 } }, "type": "Roadm" }, { - "uid": "roadm Fresno", + "uid": "roadm Portland", "metadata": { "location": { - "city": "Fresno", + "city": "Portland", "region": "CONUS", - "latitude": 36.7800007, - "longitude": -119.790002 + "latitude": 45.54000072, + "longitude": -122.6600035 } }, "type": "Roadm" }, { - "uid": "roadm Greensboro", + "uid": "roadm Providence", "metadata": { "location": { - "city": "Greensboro", + "city": "Providence", "region": "CONUS", - "latitude": 36.0800024, - "longitude": -79.8300018 + "latitude": 41.82, + "longitude": -71.42 } }, "type": "Roadm" }, { - "uid": "roadm Houston", + "uid": "roadm Raleigh", "metadata": { "location": { - "city": "Houston", + "city": "Raleigh", "region": "CONUS", - "latitude": 29.77, - "longitude": -95.39 + "latitude": 35.8199995, + "longitude": -78.6600034 } }, "type": "Roadm" }, { - "uid": "roadm Jacksonville", + "uid": "roadm Richmond", "metadata": { "location": { - "city": "Jacksonville", + "city": "Richmond", "region": "CONUS", - "latitude": 30.330003, - "longitude": -81.660002 + "latitude": 37.5299986, + "longitude": -77.4700015 } }, "type": "Roadm" }, { - "uid": "roadm Kansas_City", + "uid": "roadm Rochester", "metadata": { "location": { - "city": "Kansas_City", + "city": "Rochester", "region": "CONUS", - "latitude": 39.1199992, - "longitude": -94.7300038 + "latitude": 43.1699985, + "longitude": -77.620003 } }, "type": "Roadm" }, { - "uid": "roadm Las_Vegas", + "uid": "roadm Sacramento", "metadata": { "location": { - "city": "Las_Vegas", + "city": "Sacramento", "region": "CONUS", - "latitude": 36.20999, - "longitude": -115.2199 + "latitude": 38.56999946, + "longitude": -121.4700016 } }, "type": "Roadm" }, { - "uid": "roadm Los_Angeles", + "uid": "roadm Salt_Lake_City", "metadata": { "location": { - "city": "Los_Angeles", + "city": "Salt_Lake_City", "region": "CONUS", - "latitude": 34.110001, - "longitude": -118.410002 + "latitude": 40.77999863, + "longitude": -111.9300007 } }, "type": "Roadm" }, { - "uid": "roadm Louisville", + "uid": "roadm San_Antonio", "metadata": { "location": { - "city": "Louisville", + "city": "San_Antonio", "region": "CONUS", - "latitude": 38.2200009, - "longitude": -85.7399979 + "latitude": 29.459997, + "longitude": -98.510002 } }, "type": "Roadm" }, { - "uid": "roadm Miami", + "uid": "roadm San_Diego", "metadata": { "location": { - "city": "Miami", + "city": "San_Diego", "region": "CONUS", - "latitude": 25.7800006, - "longitude": -80.2099997 + "latitude": 32.8100017, + "longitude": -117.139999 } }, "type": "Roadm" }, { - "uid": "roadm Minneapolis", + "uid": "roadm San_Francisco", "metadata": { "location": { - "city": "Minneapolis", + "city": "San_Francisco", "region": "CONUS", - "latitude": 44.9599988, - "longitude": -93.2699973 + "latitude": 37.65999942, + "longitude": -122.4199987 } }, "type": "Roadm" }, { - "uid": "roadm Nashville", + "uid": "roadm San_Jose", "metadata": { "location": { - "city": "Nashville", + "city": "San_Jose", "region": "CONUS", - "latitude": 36.1699984, - "longitude": -86.7799989 + "latitude": 37.29999947, + "longitude": -121.8499985 } }, "type": "Roadm" }, { - "uid": "roadm New_Orleans", + "uid": "roadm Santa_Barbara", "metadata": { "location": { - "city": "New_Orleans", + "city": "Santa_Barbara", "region": "CONUS", - "latitude": 30.07, - "longitude": -89.93 + "latitude": 34.43000021, + "longitude": -119.7200014 } }, "type": "Roadm" }, { - "uid": "roadm New_York", + "uid": "roadm Scranton", "metadata": { "location": { - "city": "New_York", + "city": "Scranton", "region": "CONUS", - "latitude": 40.6699983, - "longitude": -73.9400035 + "latitude": 41.4, + "longitude": -75.67 } }, "type": "Roadm" }, { - "uid": "roadm Oakland", + "uid": "roadm Seattle", "metadata": { "location": { - "city": "Oakland", + "city": "Seattle", "region": "CONUS", - "latitude": 37.77000071, - "longitude": -122.2200016 + "latitude": 47.61999916, + "longitude": -122.3499985 } }, "type": "Roadm" }, { - "uid": "roadm Omaha", + "uid": "roadm Spokane", "metadata": { "location": { - "city": "Omaha", + "city": "Spokane", "region": "CONUS", - "latitude": 41.2599984, - "longitude": -96.0100022 + "latitude": 47.66999805, + "longitude": -117.4100038 } }, "type": "Roadm" }, { - "uid": "roadm Philadelphia", + "uid": "roadm Springfield", "metadata": { "location": { - "city": "Philadelphia", + "city": "Springfield", "region": "CONUS", - "latitude": 40.0099985, - "longitude": -75.1299964 + "latitude": 39.5, + "longitude": -89.4 } }, "type": "Roadm" }, { - "uid": "roadm Phoenix", + "uid": "roadm St_Louis", "metadata": { "location": { - "city": "Phoenix", + "city": "St_Louis", "region": "CONUS", - "latitude": 33.54000058, - "longitude": -112.0699996 + "latitude": 38.64, + "longitude": -90.24 } }, "type": "Roadm" }, { - "uid": "roadm Pittsburgh", + "uid": "roadm Syracuse", "metadata": { "location": { - "city": "Pittsburgh", + "city": "Syracuse", "region": "CONUS", - "latitude": 40.3, - "longitude": -80.13 + "latitude": 43.040001, + "longitude": -76.1399993 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tallahassee", + "metadata": { + "location": { + "city": "Tallahassee", + "region": "CONUS", + "latitude": 30.46, + "longitude": -84.28 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tampa", + "metadata": { + "location": { + "city": "Tampa", + "region": "CONUS", + "latitude": 27.9599988, + "longitude": -82.4800035 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Toledo", + "metadata": { + "location": { + "city": "Toledo", + "region": "CONUS", + "latitude": 41.659997, + "longitude": -83.58 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tucson", + "metadata": { + "location": { + "city": "Tucson", + "region": "CONUS", + "latitude": 32.2, + "longitude": -110.89 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tulsa", + "metadata": { + "location": { + "city": "Tulsa", + "region": "CONUS", + "latitude": 36.13, + "longitude": -95.92 } }, "type": "Roadm" }, { - "uid": "roadm Portland", + "uid": "roadm Washington_DC", "metadata": { "location": { - "city": "Portland", + "city": "Washington_DC", "region": "CONUS", - "latitude": 45.54000072, - "longitude": -122.6600035 + "latitude": 38.9100003, + "longitude": -77.0199965 } }, "type": "Roadm" }, { - "uid": "roadm Raleigh", + "uid": "roadm West_Palm_Beach", "metadata": { "location": { - "city": "Raleigh", + "city": "West_Palm_Beach", "region": "CONUS", - "latitude": 35.8199995, - "longitude": -78.6600034 + "latitude": 26.7499997, + "longitude": -80.1299975 } }, "type": "Roadm" }, { - "uid": "roadm Salt_Lake_City", + "uid": "roadm Wilmington", "metadata": { "location": { - "city": "Salt_Lake_City", + "city": "Wilmington", "region": "CONUS", - "latitude": 40.77999863, - "longitude": -111.9300007 + "latitude": 39.7400018, + "longitude": -75.5299989 } }, "type": "Roadm" }, { - "uid": "roadm Scranton", + "uid": "roadm Amsterdam", "metadata": { "location": { - "city": "Scranton", - "region": "CONUS", - "latitude": 41.4, - "longitude": -75.67 + "city": "Amsterdam", + "region": "Europe", + "latitude": 52.3699996, + "longitude": 4.88999915 } }, "type": "Roadm" }, { - "uid": "roadm St_Louis", + "uid": "roadm Berlin", "metadata": { "location": { - "city": "St_Louis", - "region": "CONUS", - "latitude": 38.64, - "longitude": -90.24 + "city": "Berlin", + "region": "Europe", + "latitude": 52.520002, + "longitude": 13.379995 } }, "type": "Roadm" }, { - "uid": "roadm Syracuse", + "uid": "roadm Brussels", "metadata": { "location": { - "city": "Syracuse", - "region": "CONUS", - "latitude": 43.040001, - "longitude": -76.1399993 + "city": "Brussels", + "region": "Europe", + "latitude": 50.830002, + "longitude": 4.330002 } }, "type": "Roadm" }, { - "uid": "roadm Washington_DC", + "uid": "roadm Bucharest", "metadata": { "location": { - "city": "Washington_DC", - "region": "CONUS", - "latitude": 38.9100003, - "longitude": -77.0199965 + "city": "Bucharest", + "region": "Europe", + "latitude": 44.44, + "longitude": 26.1 } }, "type": "Roadm" }, { - "uid": "roadm Amsterdam", + "uid": "roadm Frankfurt", "metadata": { "location": { - "city": "Amsterdam", + "city": "Frankfurt", "region": "Europe", - "latitude": 52.3699996, - "longitude": 4.88999915 + "latitude": 50.1199992, + "longitude": 8.68000104 } }, "type": "Roadm" @@ -1092,6 +2184,18 @@ }, "type": "Roadm" }, + { + "uid": "roadm Madrid", + "metadata": { + "location": { + "city": "Madrid", + "region": "Europe", + "latitude": 40.419998, + "longitude": -3.7100002 + } + }, + "type": "Roadm" + }, { "uid": "roadm Paris", "metadata": { @@ -1140,6 +2244,42 @@ }, "type": "Roadm" }, + { + "uid": "roadm Zurich", + "metadata": { + "location": { + "city": "Zurich", + "region": "Europe", + "latitude": 47.3800015, + "longitude": 8.5399996 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Bangkok", + "metadata": { + "location": { + "city": "Bangkok", + "region": "Asia", + "latitude": 13.73, + "longitude": 100.5 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Beijing", + "metadata": { + "location": { + "city": "Beijing", + "region": "Asia", + "latitude": 39.92999979, + "longitude": 116.4000013 + } + }, + "type": "Roadm" + }, { "uid": "roadm Delhi", "metadata": { @@ -1188,6 +2328,42 @@ }, "type": "Roadm" }, + { + "uid": "roadm Seoul", + "metadata": { + "location": { + "city": "Seoul", + "region": "Asia", + "latitude": 37.56000108, + "longitude": 126.9899988 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Shanghai", + "metadata": { + "location": { + "city": "Shanghai", + "region": "Asia", + "latitude": 31.23, + "longitude": 121.47 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Singapore", + "metadata": { + "location": { + "city": "Singapore", + "region": "Asia", + "latitude": 1.299999907, + "longitude": 103.8499992 + } + }, + "type": "Roadm" + }, { "uid": "roadm Sydney", "metadata": { @@ -1225,7 +2401,7 @@ "type": "Roadm" }, { - "uid": "fiber (Abilene \u2192 Dallas)-", + "uid": "fiber (Abilene → Dallas)-", "metadata": { "location": { "latitude": 32.620000000000005, @@ -1243,7 +2419,7 @@ } }, { - "uid": "fiber (Abilene \u2192 El_Paso)-", + "uid": "fiber (Abilene → El_Paso)-", "metadata": { "location": { "latitude": 32.149905000000004, @@ -1261,7 +2437,7 @@ } }, { - "uid": "fiber (Albany \u2192 Boston)-", + "uid": "fiber (Albany → Boston)-", "metadata": { "location": { "latitude": 42.504999350000006, @@ -1279,7 +2455,7 @@ } }, { - "uid": "fiber (Albany \u2192 Syracuse)-", + "uid": "fiber (Albany → Syracuse)-", "metadata": { "location": { "latitude": 42.8549996, @@ -1297,7 +2473,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Dallas)-", + "uid": "fiber (Albuquerque → Dallas)-", "metadata": { "location": { "latitude": 33.9549885, @@ -1315,7 +2491,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Denver)-", + "uid": "fiber (Albuquerque → Denver)-", "metadata": { "location": { "latitude": 37.444989855, @@ -1333,7 +2509,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 El_Paso)-", + "uid": "fiber (Albuquerque → El_Paso)-", "metadata": { "location": { "latitude": 33.4848935, @@ -1351,7 +2527,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Las_Vegas)-", + "uid": "fiber (Albuquerque → Las_Vegas)-", "metadata": { "location": { "latitude": 35.6649835, @@ -1369,7 +2545,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Berlin)-", + "uid": "fiber (Amsterdam → Berlin)-", "metadata": { "location": { "latitude": 52.4450008, @@ -1387,7 +2563,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Brussels)-", + "uid": "fiber (Amsterdam → Brussels)-", "metadata": { "location": { "latitude": 51.600000800000004, @@ -1405,7 +2581,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Frankfurt)-", + "uid": "fiber (Amsterdam → Frankfurt)-", "metadata": { "location": { "latitude": 51.2449994, @@ -1423,7 +2599,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 New_York)-", + "uid": "fiber (Amsterdam → New_York)-", "metadata": { "location": { "latitude": 46.51999895, @@ -1441,7 +2617,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Birmingham)-", + "uid": "fiber (Atlanta → Birmingham)-", "metadata": { "location": { "latitude": 33.644998349999995, @@ -1459,7 +2635,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Charlotte)-", + "uid": "fiber (Atlanta → Charlotte)-", "metadata": { "location": { "latitude": 34.4799991, @@ -1477,7 +2653,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Jacksonville)-", + "uid": "fiber (Atlanta → Jacksonville)-", "metadata": { "location": { "latitude": 32.0450006, @@ -1495,7 +2671,7 @@ } }, { - "uid": "fiber (Austin \u2192 Houston)-", + "uid": "fiber (Austin → Houston)-", "metadata": { "location": { "latitude": 30.0399994, @@ -1513,7 +2689,7 @@ } }, { - "uid": "fiber (Austin \u2192 San_Antonio)-", + "uid": "fiber (Austin → San_Antonio)-", "metadata": { "location": { "latitude": 29.884997900000002, @@ -1531,7 +2707,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Philadelphia)-", + "uid": "fiber (Baltimore → Philadelphia)-", "metadata": { "location": { "latitude": 39.65499885, @@ -1549,7 +2725,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Pittsburgh)-", + "uid": "fiber (Baltimore → Pittsburgh)-", "metadata": { "location": { "latitude": 39.7999996, @@ -1567,7 +2743,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Washington_DC)-", + "uid": "fiber (Baltimore → Washington_DC)-", "metadata": { "location": { "latitude": 39.104999750000005, @@ -1585,7 +2761,7 @@ } }, { - "uid": "fiber (Bangkok \u2192 Delhi)-", + "uid": "fiber (Bangkok → Delhi)-", "metadata": { "location": { "latitude": 21.20000015, @@ -1603,7 +2779,7 @@ } }, { - "uid": "fiber (Bangkok \u2192 Hong_Kong)-", + "uid": "fiber (Bangkok → Hong_Kong)-", "metadata": { "location": { "latitude": 17.9985, @@ -1621,7 +2797,7 @@ } }, { - "uid": "fiber (Baton_Rouge \u2192 Houston)-", + "uid": "fiber (Baton_Rouge → Houston)-", "metadata": { "location": { "latitude": 30.1099998, @@ -1639,7 +2815,7 @@ } }, { - "uid": "fiber (Baton_Rouge \u2192 New_Orleans)-", + "uid": "fiber (Baton_Rouge → New_Orleans)-", "metadata": { "location": { "latitude": 30.259999800000003, @@ -1657,7 +2833,7 @@ } }, { - "uid": "fiber (Beijing \u2192 Seoul)-", + "uid": "fiber (Beijing → Seoul)-", "metadata": { "location": { "latitude": 38.745000434999994, @@ -1675,7 +2851,7 @@ } }, { - "uid": "fiber (Beijing \u2192 Shanghai)-", + "uid": "fiber (Beijing → Shanghai)-", "metadata": { "location": { "latitude": 35.579999895, @@ -1693,7 +2869,7 @@ } }, { - "uid": "fiber (Berlin \u2192 Warsaw)-", + "uid": "fiber (Berlin → Warsaw)-", "metadata": { "location": { "latitude": 52.390000349999994, @@ -1711,7 +2887,7 @@ } }, { - "uid": "fiber (Billings \u2192 Bismarck)-", + "uid": "fiber (Billings → Bismarck)-", "metadata": { "location": { "latitude": 46.30000129, @@ -1729,7 +2905,7 @@ } }, { - "uid": "fiber (Billings \u2192 Denver)-", + "uid": "fiber (Billings → Denver)-", "metadata": { "location": { "latitude": 42.780001874999996, @@ -1747,7 +2923,7 @@ } }, { - "uid": "fiber (Billings \u2192 Spokane)-", + "uid": "fiber (Billings → Spokane)-", "metadata": { "location": { "latitude": 46.729999545, @@ -1765,7 +2941,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 Nashville)-", + "uid": "fiber (Birmingham → Nashville)-", "metadata": { "location": { "latitude": 34.84999845, @@ -1783,7 +2959,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 New_Orleans)-", + "uid": "fiber (Birmingham → New_Orleans)-", "metadata": { "location": { "latitude": 31.79999925, @@ -1801,7 +2977,7 @@ } }, { - "uid": "fiber (Bismarck \u2192 Minneapolis)-", + "uid": "fiber (Bismarck → Minneapolis)-", "metadata": { "location": { "latitude": 45.88500017, @@ -1819,7 +2995,7 @@ } }, { - "uid": "fiber (Boston \u2192 Providence)-", + "uid": "fiber (Boston → Providence)-", "metadata": { "location": { "latitude": 42.08000025, @@ -1837,7 +3013,7 @@ } }, { - "uid": "fiber (Brussels \u2192 London)-", + "uid": "fiber (Brussels → London)-", "metadata": { "location": { "latitude": 51.17500125, @@ -1855,7 +3031,7 @@ } }, { - "uid": "fiber (Bucharest \u2192 Istanbul)-", + "uid": "fiber (Bucharest → Istanbul)-", "metadata": { "location": { "latitude": 42.769999999999996, @@ -1873,7 +3049,7 @@ } }, { - "uid": "fiber (Bucharest \u2192 Warsaw)-", + "uid": "fiber (Bucharest → Warsaw)-", "metadata": { "location": { "latitude": 48.34999935, @@ -1891,7 +3067,7 @@ } }, { - "uid": "fiber (Buffalo \u2192 Cleveland)-", + "uid": "fiber (Buffalo → Cleveland)-", "metadata": { "location": { "latitude": 42.184999250000004, @@ -1909,7 +3085,7 @@ } }, { - "uid": "fiber (Buffalo \u2192 Rochester)-", + "uid": "fiber (Buffalo → Rochester)-", "metadata": { "location": { "latitude": 43.029998899999995, @@ -1927,7 +3103,7 @@ } }, { - "uid": "fiber (Charleston \u2192 Jacksonville)-", + "uid": "fiber (Charleston → Jacksonville)-", "metadata": { "location": { "latitude": 31.560001900000003, @@ -1945,7 +3121,7 @@ } }, { - "uid": "fiber (Charleston \u2192 Raleigh)-", + "uid": "fiber (Charleston → Raleigh)-", "metadata": { "location": { "latitude": 34.30500015, @@ -1963,7 +3139,7 @@ } }, { - "uid": "fiber (Charlotte \u2192 Greensboro)-", + "uid": "fiber (Charlotte → Greensboro)-", "metadata": { "location": { "latitude": 35.6400012, @@ -1981,7 +3157,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Detroit)-", + "uid": "fiber (Chicago → Detroit)-", "metadata": { "location": { "latitude": 42.109999450000004, @@ -1999,7 +3175,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Milwaukee)-", + "uid": "fiber (Chicago → Milwaukee)-", "metadata": { "location": { "latitude": 42.44999915, @@ -2017,7 +3193,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Springfield)-", + "uid": "fiber (Chicago → Springfield)-", "metadata": { "location": { "latitude": 40.6699985, @@ -2035,7 +3211,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Columbus)-", + "uid": "fiber (Cincinnati → Columbus)-", "metadata": { "location": { "latitude": 39.56500054999999, @@ -2053,7 +3229,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Louisville)-", + "uid": "fiber (Cincinnati → Louisville)-", "metadata": { "location": { "latitude": 38.68, @@ -2071,7 +3247,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Washington_DC)-", + "uid": "fiber (Cincinnati → Washington_DC)-", "metadata": { "location": { "latitude": 39.024999699999995, @@ -2089,7 +3265,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Columbus)-", + "uid": "fiber (Cleveland → Columbus)-", "metadata": { "location": { "latitude": 40.7350006, @@ -2107,7 +3283,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Toledo)-", + "uid": "fiber (Cleveland → Toledo)-", "metadata": { "location": { "latitude": 41.5699981, @@ -2125,7 +3301,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Pittsburgh)-", + "uid": "fiber (Columbus → Pittsburgh)-", "metadata": { "location": { "latitude": 40.14500099999999, @@ -2143,7 +3319,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Houston)-", + "uid": "fiber (Dallas → Houston)-", "metadata": { "location": { "latitude": 31.28, @@ -2161,7 +3337,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Little_Rock)-", + "uid": "fiber (Dallas → Little_Rock)-", "metadata": { "location": { "latitude": 33.754999999999995, @@ -2179,7 +3355,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Oklahoma_City)-", + "uid": "fiber (Dallas → Oklahoma_City)-", "metadata": { "location": { "latitude": 34.13000075, @@ -2197,7 +3373,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Istanbul)-", + "uid": "fiber (Delhi → Istanbul)-", "metadata": { "location": { "latitude": 34.88500015, @@ -2215,7 +3391,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Mumbai)-", + "uid": "fiber (Delhi → Mumbai)-", "metadata": { "location": { "latitude": 23.8149995, @@ -2233,7 +3409,7 @@ } }, { - "uid": "fiber (Denver \u2192 Omaha)-", + "uid": "fiber (Denver → Omaha)-", "metadata": { "location": { "latitude": 40.515000555, @@ -2251,7 +3427,7 @@ } }, { - "uid": "fiber (Denver \u2192 Salt_Lake_City)-", + "uid": "fiber (Denver → Salt_Lake_City)-", "metadata": { "location": { "latitude": 40.27500067, @@ -2269,7 +3445,7 @@ } }, { - "uid": "fiber (Detroit \u2192 Toledo)-", + "uid": "fiber (Detroit → Toledo)-", "metadata": { "location": { "latitude": 42.01999945, @@ -2287,7 +3463,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 San_Antonio)-", + "uid": "fiber (El_Paso → San_Antonio)-", "metadata": { "location": { "latitude": 30.654903500000003, @@ -2305,7 +3481,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Tucson)-", + "uid": "fiber (El_Paso → Tucson)-", "metadata": { "location": { "latitude": 32.024905000000004, @@ -2323,7 +3499,7 @@ } }, { - "uid": "fiber (Frankfurt \u2192 Vienna)-", + "uid": "fiber (Frankfurt → Vienna)-", "metadata": { "location": { "latitude": 49.1700008, @@ -2341,7 +3517,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Las_Vegas)-", + "uid": "fiber (Fresno → Las_Vegas)-", "metadata": { "location": { "latitude": 36.494995349999996, @@ -2359,7 +3535,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Los_Angeles)-", + "uid": "fiber (Fresno → Los_Angeles)-", "metadata": { "location": { "latitude": 35.44500085, @@ -2377,7 +3553,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Oakland)-", + "uid": "fiber (Fresno → Oakland)-", "metadata": { "location": { "latitude": 37.275000705, @@ -2395,7 +3571,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Louisville)-", + "uid": "fiber (Greensboro → Louisville)-", "metadata": { "location": { "latitude": 37.15000165, @@ -2413,7 +3589,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Raleigh)-", + "uid": "fiber (Greensboro → Raleigh)-", "metadata": { "location": { "latitude": 35.95000095, @@ -2431,7 +3607,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Richmond)-", + "uid": "fiber (Greensboro → Richmond)-", "metadata": { "location": { "latitude": 36.8050005, @@ -2449,7 +3625,7 @@ } }, { - "uid": "fiber (Hartford \u2192 Long_Island)-", + "uid": "fiber (Hartford → Long_Island)-", "metadata": { "location": { "latitude": 41.18000015, @@ -2467,7 +3643,7 @@ } }, { - "uid": "fiber (Hartford \u2192 Providence)-", + "uid": "fiber (Hartford → Providence)-", "metadata": { "location": { "latitude": 41.795000200000004, @@ -2485,7 +3661,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Shanghai)-", + "uid": "fiber (Hong_Kong → Shanghai)-", "metadata": { "location": { "latitude": 26.7485, @@ -2503,7 +3679,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Sydney)-", + "uid": "fiber (Hong_Kong → Sydney)-", "metadata": { "location": { "latitude": -5.801499479999999, @@ -2521,7 +3697,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Taipei)-", + "uid": "fiber (Hong_Kong → Taipei)-", "metadata": { "location": { "latitude": 23.64350025, @@ -2539,7 +3715,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Los_Angeles)-", + "uid": "fiber (Honolulu → Los_Angeles)-", "metadata": { "location": { "latitude": 27.7150003, @@ -2557,7 +3733,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Sydney)-", + "uid": "fiber (Honolulu → Sydney)-", "metadata": { "location": { "latitude": -6.274999679999999, @@ -2575,7 +3751,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Taipei)-", + "uid": "fiber (Honolulu → Taipei)-", "metadata": { "location": { "latitude": 23.17000005, @@ -2593,7 +3769,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Rome)-", + "uid": "fiber (Istanbul → Rome)-", "metadata": { "location": { "latitude": 41.4949998, @@ -2611,7 +3787,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Orlando)-", + "uid": "fiber (Jacksonville → Orlando)-", "metadata": { "location": { "latitude": 29.4150012, @@ -2629,7 +3805,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 Omaha)-", + "uid": "fiber (Kansas_City → Omaha)-", "metadata": { "location": { "latitude": 40.1899988, @@ -2647,7 +3823,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 St_Louis)-", + "uid": "fiber (Kansas_City → St_Louis)-", "metadata": { "location": { "latitude": 38.879999600000005, @@ -2665,7 +3841,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 Tulsa)-", + "uid": "fiber (Kansas_City → Tulsa)-", "metadata": { "location": { "latitude": 37.6249996, @@ -2683,7 +3859,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Phoenix)-", + "uid": "fiber (Las_Vegas → Phoenix)-", "metadata": { "location": { "latitude": 34.87499529, @@ -2701,7 +3877,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Salt_Lake_City)-", + "uid": "fiber (Las_Vegas → Salt_Lake_City)-", "metadata": { "location": { "latitude": 38.494994315, @@ -2719,7 +3895,7 @@ } }, { - "uid": "fiber (Little_Rock \u2192 Memphis)-", + "uid": "fiber (Little_Rock → Memphis)-", "metadata": { "location": { "latitude": 34.9150005, @@ -2737,7 +3913,7 @@ } }, { - "uid": "fiber (London \u2192 Paris)-", + "uid": "fiber (London → Paris)-", "metadata": { "location": { "latitude": 50.19000025, @@ -2755,7 +3931,7 @@ } }, { - "uid": "fiber (London \u2192 Washington_DC)-", + "uid": "fiber (London → Washington_DC)-", "metadata": { "location": { "latitude": 45.2150004, @@ -2773,7 +3949,7 @@ } }, { - "uid": "fiber (Long_Island \u2192 New_York)-", + "uid": "fiber (Long_Island → New_York)-", "metadata": { "location": { "latitude": 40.629999100000006, @@ -2791,7 +3967,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 San_Diego)-", + "uid": "fiber (Los_Angeles → San_Diego)-", "metadata": { "location": { "latitude": 33.46000135, @@ -2809,7 +3985,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Santa_Barbara)-", + "uid": "fiber (Los_Angeles → Santa_Barbara)-", "metadata": { "location": { "latitude": 34.270000605, @@ -2827,7 +4003,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Nashville)-", + "uid": "fiber (Louisville → Nashville)-", "metadata": { "location": { "latitude": 37.19499965, @@ -2845,7 +4021,7 @@ } }, { - "uid": "fiber (Louisville \u2192 St_Louis)-", + "uid": "fiber (Louisville → St_Louis)-", "metadata": { "location": { "latitude": 38.43000045, @@ -2863,7 +4039,7 @@ } }, { - "uid": "fiber (Madrid \u2192 Paris)-", + "uid": "fiber (Madrid → Paris)-", "metadata": { "location": { "latitude": 44.639999, @@ -2881,7 +4057,7 @@ } }, { - "uid": "fiber (Madrid \u2192 Zurich)-", + "uid": "fiber (Madrid → Zurich)-", "metadata": { "location": { "latitude": 43.89999975, @@ -2899,7 +4075,7 @@ } }, { - "uid": "fiber (Memphis \u2192 Nashville)-", + "uid": "fiber (Memphis → Nashville)-", "metadata": { "location": { "latitude": 35.6399997, @@ -2917,7 +4093,7 @@ } }, { - "uid": "fiber (Miami \u2192 Paris)-", + "uid": "fiber (Miami → Paris)-", "metadata": { "location": { "latitude": 37.320000300000004, @@ -2935,7 +4111,7 @@ } }, { - "uid": "fiber (Miami \u2192 Tampa)-", + "uid": "fiber (Miami → Tampa)-", "metadata": { "location": { "latitude": 26.8699997, @@ -2953,7 +4129,7 @@ } }, { - "uid": "fiber (Miami \u2192 West_Palm_Beach)-", + "uid": "fiber (Miami → West_Palm_Beach)-", "metadata": { "location": { "latitude": 26.26500015, @@ -2971,7 +4147,7 @@ } }, { - "uid": "fiber (Milwaukee \u2192 Minneapolis)-", + "uid": "fiber (Milwaukee → Minneapolis)-", "metadata": { "location": { "latitude": 44.01000005, @@ -2989,7 +4165,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Omaha)-", + "uid": "fiber (Minneapolis → Omaha)-", "metadata": { "location": { "latitude": 43.1099986, @@ -3007,7 +4183,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Rome)-", + "uid": "fiber (Mumbai → Rome)-", "metadata": { "location": { "latitude": 30.42499915, @@ -3025,7 +4201,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Singapore)-", + "uid": "fiber (Mumbai → Singapore)-", "metadata": { "location": { "latitude": 10.1299993035, @@ -3043,7 +4219,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Tallahassee)-", + "uid": "fiber (New_Orleans → Tallahassee)-", "metadata": { "location": { "latitude": 30.265, @@ -3061,7 +4237,7 @@ } }, { - "uid": "fiber (New_York \u2192 Newark)-", + "uid": "fiber (New_York → Newark)-", "metadata": { "location": { "latitude": 40.69499975, @@ -3079,7 +4255,7 @@ } }, { - "uid": "fiber (New_York \u2192 Scranton)-", + "uid": "fiber (New_York → Scranton)-", "metadata": { "location": { "latitude": 41.034999150000004, @@ -3097,7 +4273,7 @@ } }, { - "uid": "fiber (New_York \u2192 Wilmington)-", + "uid": "fiber (New_York → Wilmington)-", "metadata": { "location": { "latitude": 40.20500005, @@ -3115,7 +4291,7 @@ } }, { - "uid": "fiber (Newark \u2192 Philadelphia)-", + "uid": "fiber (Newark → Philadelphia)-", "metadata": { "location": { "latitude": 40.364999850000004, @@ -3133,7 +4309,7 @@ } }, { - "uid": "fiber (Norfolk \u2192 Raleigh)-", + "uid": "fiber (Norfolk → Raleigh)-", "metadata": { "location": { "latitude": 36.36999885, @@ -3151,7 +4327,7 @@ } }, { - "uid": "fiber (Norfolk \u2192 Wilmington)-", + "uid": "fiber (Norfolk → Wilmington)-", "metadata": { "location": { "latitude": 38.33, @@ -3169,7 +4345,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Sacramento)-", + "uid": "fiber (Oakland → Sacramento)-", "metadata": { "location": { "latitude": 38.170000085, @@ -3187,7 +4363,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Salt_Lake_City)-", + "uid": "fiber (Oakland → Salt_Lake_City)-", "metadata": { "location": { "latitude": 39.27499967, @@ -3205,7 +4381,7 @@ } }, { - "uid": "fiber (Oakland \u2192 San_Francisco)-", + "uid": "fiber (Oakland → San_Francisco)-", "metadata": { "location": { "latitude": 37.715000065, @@ -3223,7 +4399,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Taipei)-", + "uid": "fiber (Oakland → Taipei)-", "metadata": { "location": { "latitude": 31.395000605, @@ -3241,7 +4417,7 @@ } }, { - "uid": "fiber (Oklahoma_City \u2192 Tulsa)-", + "uid": "fiber (Oklahoma_City → Tulsa)-", "metadata": { "location": { "latitude": 35.80000075, @@ -3259,7 +4435,7 @@ } }, { - "uid": "fiber (Orlando \u2192 West_Palm_Beach)-", + "uid": "fiber (Orlando → West_Palm_Beach)-", "metadata": { "location": { "latitude": 27.62499955, @@ -3277,7 +4453,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Scranton)-", + "uid": "fiber (Philadelphia → Scranton)-", "metadata": { "location": { "latitude": 40.70499925, @@ -3295,7 +4471,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 San_Diego)-", + "uid": "fiber (Phoenix → San_Diego)-", "metadata": { "location": { "latitude": 33.17500114, @@ -3313,7 +4489,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 Tucson)-", + "uid": "fiber (Phoenix → Tucson)-", "metadata": { "location": { "latitude": 32.87000029, @@ -3331,7 +4507,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Scranton)-", + "uid": "fiber (Pittsburgh → Scranton)-", "metadata": { "location": { "latitude": 40.849999999999994, @@ -3349,7 +4525,7 @@ } }, { - "uid": "fiber (Portland \u2192 Sacramento)-", + "uid": "fiber (Portland → Sacramento)-", "metadata": { "location": { "latitude": 42.05500009, @@ -3367,7 +4543,7 @@ } }, { - "uid": "fiber (Portland \u2192 Salt_Lake_City)-", + "uid": "fiber (Portland → Salt_Lake_City)-", "metadata": { "location": { "latitude": 43.159999675, @@ -3385,7 +4561,7 @@ } }, { - "uid": "fiber (Portland \u2192 Seattle)-", + "uid": "fiber (Portland → Seattle)-", "metadata": { "location": { "latitude": 46.57999994, @@ -3403,7 +4579,7 @@ } }, { - "uid": "fiber (Portland \u2192 Tokyo)-", + "uid": "fiber (Portland → Tokyo)-", "metadata": { "location": { "latitude": 40.604999660000004, @@ -3421,7 +4597,7 @@ } }, { - "uid": "fiber (Richmond \u2192 Washington_DC)-", + "uid": "fiber (Richmond → Washington_DC)-", "metadata": { "location": { "latitude": 38.21999945, @@ -3439,7 +4615,7 @@ } }, { - "uid": "fiber (Rochester \u2192 Syracuse)-", + "uid": "fiber (Rochester → Syracuse)-", "metadata": { "location": { "latitude": 43.10499975, @@ -3457,7 +4633,7 @@ } }, { - "uid": "fiber (Rome \u2192 Vienna)-", + "uid": "fiber (Rome → Vienna)-", "metadata": { "location": { "latitude": 45.055001000000004, @@ -3475,7 +4651,7 @@ } }, { - "uid": "fiber (Rome \u2192 Zurich)-", + "uid": "fiber (Rome → Zurich)-", "metadata": { "location": { "latitude": 44.63500055, @@ -3493,7 +4669,7 @@ } }, { - "uid": "fiber (San_Francisco \u2192 San_Jose)-", + "uid": "fiber (San_Francisco → San_Jose)-", "metadata": { "location": { "latitude": 37.479999445000004, @@ -3511,7 +4687,7 @@ } }, { - "uid": "fiber (San_Jose \u2192 Santa_Barbara)-", + "uid": "fiber (San_Jose → Santa_Barbara)-", "metadata": { "location": { "latitude": 35.86499984, @@ -3529,7 +4705,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Syracuse)-", + "uid": "fiber (Scranton → Syracuse)-", "metadata": { "location": { "latitude": 42.2200005, @@ -3547,7 +4723,7 @@ } }, { - "uid": "fiber (Seattle \u2192 Spokane)-", + "uid": "fiber (Seattle → Spokane)-", "metadata": { "location": { "latitude": 47.644998605, @@ -3565,7 +4741,7 @@ } }, { - "uid": "fiber (Seoul \u2192 Tokyo)-", + "uid": "fiber (Seoul → Tokyo)-", "metadata": { "location": { "latitude": 36.614999839999996, @@ -3583,7 +4759,7 @@ } }, { - "uid": "fiber (Singapore \u2192 Sydney)-", + "uid": "fiber (Singapore → Sydney)-", "metadata": { "location": { "latitude": -16.2849995265, @@ -3601,7 +4777,7 @@ } }, { - "uid": "fiber (Springfield \u2192 St_Louis)-", + "uid": "fiber (Springfield → St_Louis)-", "metadata": { "location": { "latitude": 39.07, @@ -3619,7 +4795,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Tokyo)-", + "uid": "fiber (Taipei → Tokyo)-", "metadata": { "location": { "latitude": 30.344999549999997, @@ -3637,7 +4813,7 @@ } }, { - "uid": "fiber (Tallahassee \u2192 Tampa)-", + "uid": "fiber (Tallahassee → Tampa)-", "metadata": { "location": { "latitude": 29.2099994, @@ -3655,7 +4831,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Warsaw)-", + "uid": "fiber (Vienna → Warsaw)-", "metadata": { "location": { "latitude": 50.24000055, @@ -3673,7 +4849,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Abilene)-", + "uid": "fiber (Dallas → Abilene)-", "metadata": { "location": { "latitude": 32.620000000000005, @@ -3691,7 +4867,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Abilene)-", + "uid": "fiber (El_Paso → Abilene)-", "metadata": { "location": { "latitude": 32.149905000000004, @@ -3709,7 +4885,7 @@ } }, { - "uid": "fiber (Boston \u2192 Albany)-", + "uid": "fiber (Boston → Albany)-", "metadata": { "location": { "latitude": 42.504999350000006, @@ -3727,7 +4903,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Albany)-", + "uid": "fiber (Syracuse → Albany)-", "metadata": { "location": { "latitude": 42.8549996, @@ -3745,7 +4921,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Albuquerque)-", + "uid": "fiber (Dallas → Albuquerque)-", "metadata": { "location": { "latitude": 33.9549885, @@ -3763,7 +4939,7 @@ } }, { - "uid": "fiber (Denver \u2192 Albuquerque)-", + "uid": "fiber (Denver → Albuquerque)-", "metadata": { "location": { "latitude": 37.444989855, @@ -3781,7 +4957,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Albuquerque)-", + "uid": "fiber (El_Paso → Albuquerque)-", "metadata": { "location": { "latitude": 33.4848935, @@ -3799,7 +4975,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Albuquerque)-", + "uid": "fiber (Las_Vegas → Albuquerque)-", "metadata": { "location": { "latitude": 35.6649835, @@ -3817,7 +4993,7 @@ } }, { - "uid": "fiber (Berlin \u2192 Amsterdam)-", + "uid": "fiber (Berlin → Amsterdam)-", "metadata": { "location": { "latitude": 52.4450008, @@ -3835,7 +5011,7 @@ } }, { - "uid": "fiber (Brussels \u2192 Amsterdam)-", + "uid": "fiber (Brussels → Amsterdam)-", "metadata": { "location": { "latitude": 51.600000800000004, @@ -3853,7 +5029,7 @@ } }, { - "uid": "fiber (Frankfurt \u2192 Amsterdam)-", + "uid": "fiber (Frankfurt → Amsterdam)-", "metadata": { "location": { "latitude": 51.2449994, @@ -3871,7 +5047,7 @@ } }, { - "uid": "fiber (New_York \u2192 Amsterdam)-", + "uid": "fiber (New_York → Amsterdam)-", "metadata": { "location": { "latitude": 46.51999895, @@ -3889,7 +5065,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 Atlanta)-", + "uid": "fiber (Birmingham → Atlanta)-", "metadata": { "location": { "latitude": 33.644998349999995, @@ -3907,7 +5083,7 @@ } }, { - "uid": "fiber (Charlotte \u2192 Atlanta)-", + "uid": "fiber (Charlotte → Atlanta)-", "metadata": { "location": { "latitude": 34.4799991, @@ -3925,7 +5101,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Atlanta)-", + "uid": "fiber (Jacksonville → Atlanta)-", "metadata": { "location": { "latitude": 32.0450006, @@ -3943,7 +5119,7 @@ } }, { - "uid": "fiber (Houston \u2192 Austin)-", + "uid": "fiber (Houston → Austin)-", "metadata": { "location": { "latitude": 30.0399994, @@ -3961,7 +5137,7 @@ } }, { - "uid": "fiber (San_Antonio \u2192 Austin)-", + "uid": "fiber (San_Antonio → Austin)-", "metadata": { "location": { "latitude": 29.884997900000002, @@ -3979,7 +5155,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Baltimore)-", + "uid": "fiber (Philadelphia → Baltimore)-", "metadata": { "location": { "latitude": 39.65499885, @@ -3997,7 +5173,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Baltimore)-", + "uid": "fiber (Pittsburgh → Baltimore)-", "metadata": { "location": { "latitude": 39.7999996, @@ -4015,7 +5191,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Baltimore)-", + "uid": "fiber (Washington_DC → Baltimore)-", "metadata": { "location": { "latitude": 39.104999750000005, @@ -4033,7 +5209,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Bangkok)-", + "uid": "fiber (Delhi → Bangkok)-", "metadata": { "location": { "latitude": 21.20000015, @@ -4051,7 +5227,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Bangkok)-", + "uid": "fiber (Hong_Kong → Bangkok)-", "metadata": { "location": { "latitude": 17.9985, @@ -4069,7 +5245,7 @@ } }, { - "uid": "fiber (Houston \u2192 Baton_Rouge)-", + "uid": "fiber (Houston → Baton_Rouge)-", "metadata": { "location": { "latitude": 30.1099998, @@ -4087,7 +5263,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Baton_Rouge)-", + "uid": "fiber (New_Orleans → Baton_Rouge)-", "metadata": { "location": { "latitude": 30.259999800000003, @@ -4105,7 +5281,7 @@ } }, { - "uid": "fiber (Seoul \u2192 Beijing)-", + "uid": "fiber (Seoul → Beijing)-", "metadata": { "location": { "latitude": 38.745000434999994, @@ -4123,7 +5299,7 @@ } }, { - "uid": "fiber (Shanghai \u2192 Beijing)-", + "uid": "fiber (Shanghai → Beijing)-", "metadata": { "location": { "latitude": 35.579999895, @@ -4141,7 +5317,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Berlin)-", + "uid": "fiber (Warsaw → Berlin)-", "metadata": { "location": { "latitude": 52.390000349999994, @@ -4159,7 +5335,7 @@ } }, { - "uid": "fiber (Bismarck \u2192 Billings)-", + "uid": "fiber (Bismarck → Billings)-", "metadata": { "location": { "latitude": 46.30000129, @@ -4177,7 +5353,7 @@ } }, { - "uid": "fiber (Denver \u2192 Billings)-", + "uid": "fiber (Denver → Billings)-", "metadata": { "location": { "latitude": 42.780001874999996, @@ -4195,7 +5371,7 @@ } }, { - "uid": "fiber (Spokane \u2192 Billings)-", + "uid": "fiber (Spokane → Billings)-", "metadata": { "location": { "latitude": 46.729999545, @@ -4213,7 +5389,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Birmingham)-", + "uid": "fiber (Nashville → Birmingham)-", "metadata": { "location": { "latitude": 34.84999845, @@ -4231,7 +5407,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Birmingham)-", + "uid": "fiber (New_Orleans → Birmingham)-", "metadata": { "location": { "latitude": 31.79999925, @@ -4249,7 +5425,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Bismarck)-", + "uid": "fiber (Minneapolis → Bismarck)-", "metadata": { "location": { "latitude": 45.88500017, @@ -4267,7 +5443,7 @@ } }, { - "uid": "fiber (Providence \u2192 Boston)-", + "uid": "fiber (Providence → Boston)-", "metadata": { "location": { "latitude": 42.08000025, @@ -4285,7 +5461,7 @@ } }, { - "uid": "fiber (London \u2192 Brussels)-", + "uid": "fiber (London → Brussels)-", "metadata": { "location": { "latitude": 51.17500125, @@ -4303,7 +5479,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Bucharest)-", + "uid": "fiber (Istanbul → Bucharest)-", "metadata": { "location": { "latitude": 42.769999999999996, @@ -4321,7 +5497,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Bucharest)-", + "uid": "fiber (Warsaw → Bucharest)-", "metadata": { "location": { "latitude": 48.34999935, @@ -4339,7 +5515,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Buffalo)-", + "uid": "fiber (Cleveland → Buffalo)-", "metadata": { "location": { "latitude": 42.184999250000004, @@ -4357,7 +5533,7 @@ } }, { - "uid": "fiber (Rochester \u2192 Buffalo)-", + "uid": "fiber (Rochester → Buffalo)-", "metadata": { "location": { "latitude": 43.029998899999995, @@ -4375,7 +5551,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Charleston)-", + "uid": "fiber (Jacksonville → Charleston)-", "metadata": { "location": { "latitude": 31.560001900000003, @@ -4393,7 +5569,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Charleston)-", + "uid": "fiber (Raleigh → Charleston)-", "metadata": { "location": { "latitude": 34.30500015, @@ -4411,7 +5587,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Charlotte)-", + "uid": "fiber (Greensboro → Charlotte)-", "metadata": { "location": { "latitude": 35.6400012, @@ -4429,7 +5605,7 @@ } }, { - "uid": "fiber (Detroit \u2192 Chicago)-", + "uid": "fiber (Detroit → Chicago)-", "metadata": { "location": { "latitude": 42.109999450000004, @@ -4447,7 +5623,7 @@ } }, { - "uid": "fiber (Milwaukee \u2192 Chicago)-", + "uid": "fiber (Milwaukee → Chicago)-", "metadata": { "location": { "latitude": 42.44999915, @@ -4465,7 +5641,7 @@ } }, { - "uid": "fiber (Springfield \u2192 Chicago)-", + "uid": "fiber (Springfield → Chicago)-", "metadata": { "location": { "latitude": 40.6699985, @@ -4483,7 +5659,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Cincinnati)-", + "uid": "fiber (Columbus → Cincinnati)-", "metadata": { "location": { "latitude": 39.56500054999999, @@ -4501,7 +5677,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Cincinnati)-", + "uid": "fiber (Louisville → Cincinnati)-", "metadata": { "location": { "latitude": 38.68, @@ -4519,7 +5695,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Cincinnati)-", + "uid": "fiber (Washington_DC → Cincinnati)-", "metadata": { "location": { "latitude": 39.024999699999995, @@ -4537,7 +5713,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Cleveland)-", + "uid": "fiber (Columbus → Cleveland)-", "metadata": { "location": { "latitude": 40.7350006, @@ -4555,7 +5731,7 @@ } }, { - "uid": "fiber (Toledo \u2192 Cleveland)-", + "uid": "fiber (Toledo → Cleveland)-", "metadata": { "location": { "latitude": 41.5699981, @@ -4573,7 +5749,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Columbus)-", + "uid": "fiber (Pittsburgh → Columbus)-", "metadata": { "location": { "latitude": 40.14500099999999, @@ -4591,7 +5767,7 @@ } }, { - "uid": "fiber (Houston \u2192 Dallas)-", + "uid": "fiber (Houston → Dallas)-", "metadata": { "location": { "latitude": 31.28, @@ -4609,7 +5785,7 @@ } }, { - "uid": "fiber (Little_Rock \u2192 Dallas)-", + "uid": "fiber (Little_Rock → Dallas)-", "metadata": { "location": { "latitude": 33.754999999999995, @@ -4627,7 +5803,7 @@ } }, { - "uid": "fiber (Oklahoma_City \u2192 Dallas)-", + "uid": "fiber (Oklahoma_City → Dallas)-", "metadata": { "location": { "latitude": 34.13000075, @@ -4645,7 +5821,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Delhi)-", + "uid": "fiber (Istanbul → Delhi)-", "metadata": { "location": { "latitude": 34.88500015, @@ -4663,7 +5839,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Delhi)-", + "uid": "fiber (Mumbai → Delhi)-", "metadata": { "location": { "latitude": 23.8149995, @@ -4681,7 +5857,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Denver)-", + "uid": "fiber (Omaha → Denver)-", "metadata": { "location": { "latitude": 40.515000555, @@ -4699,7 +5875,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Denver)-", + "uid": "fiber (Salt_Lake_City → Denver)-", "metadata": { "location": { "latitude": 40.27500067, @@ -4717,7 +5893,7 @@ } }, { - "uid": "fiber (Toledo \u2192 Detroit)-", + "uid": "fiber (Toledo → Detroit)-", "metadata": { "location": { "latitude": 42.01999945, @@ -4735,7 +5911,7 @@ } }, { - "uid": "fiber (San_Antonio \u2192 El_Paso)-", + "uid": "fiber (San_Antonio → El_Paso)-", "metadata": { "location": { "latitude": 30.654903500000003, @@ -4753,7 +5929,7 @@ } }, { - "uid": "fiber (Tucson \u2192 El_Paso)-", + "uid": "fiber (Tucson → El_Paso)-", "metadata": { "location": { "latitude": 32.024905000000004, @@ -4771,7 +5947,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Frankfurt)-", + "uid": "fiber (Vienna → Frankfurt)-", "metadata": { "location": { "latitude": 49.1700008, @@ -4789,7 +5965,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Fresno)-", + "uid": "fiber (Las_Vegas → Fresno)-", "metadata": { "location": { "latitude": 36.494995349999996, @@ -4807,7 +5983,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Fresno)-", + "uid": "fiber (Los_Angeles → Fresno)-", "metadata": { "location": { "latitude": 35.44500085, @@ -4825,7 +6001,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Fresno)-", + "uid": "fiber (Oakland → Fresno)-", "metadata": { "location": { "latitude": 37.275000705, @@ -4843,7 +6019,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Greensboro)-", + "uid": "fiber (Louisville → Greensboro)-", "metadata": { "location": { "latitude": 37.15000165, @@ -4861,7 +6037,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Greensboro)-", + "uid": "fiber (Raleigh → Greensboro)-", "metadata": { "location": { "latitude": 35.95000095, @@ -4879,7 +6055,7 @@ } }, { - "uid": "fiber (Richmond \u2192 Greensboro)-", + "uid": "fiber (Richmond → Greensboro)-", "metadata": { "location": { "latitude": 36.8050005, @@ -4897,7 +6073,7 @@ } }, { - "uid": "fiber (Long_Island \u2192 Hartford)-", + "uid": "fiber (Long_Island → Hartford)-", "metadata": { "location": { "latitude": 41.18000015, @@ -4915,7 +6091,7 @@ } }, { - "uid": "fiber (Providence \u2192 Hartford)-", + "uid": "fiber (Providence → Hartford)-", "metadata": { "location": { "latitude": 41.795000200000004, @@ -4933,7 +6109,7 @@ } }, { - "uid": "fiber (Shanghai \u2192 Hong_Kong)-", + "uid": "fiber (Shanghai → Hong_Kong)-", "metadata": { "location": { "latitude": 26.7485, @@ -4951,7 +6127,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Hong_Kong)-", + "uid": "fiber (Sydney → Hong_Kong)-", "metadata": { "location": { "latitude": -5.801499479999999, @@ -4969,7 +6145,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Hong_Kong)-", + "uid": "fiber (Taipei → Hong_Kong)-", "metadata": { "location": { "latitude": 23.64350025, @@ -4987,7 +6163,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Honolulu)-", + "uid": "fiber (Los_Angeles → Honolulu)-", "metadata": { "location": { "latitude": 27.7150003, @@ -5005,7 +6181,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Honolulu)-", + "uid": "fiber (Sydney → Honolulu)-", "metadata": { "location": { "latitude": -6.274999679999999, @@ -5023,7 +6199,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Honolulu)-", + "uid": "fiber (Taipei → Honolulu)-", "metadata": { "location": { "latitude": 23.17000005, @@ -5041,7 +6217,7 @@ } }, { - "uid": "fiber (Rome \u2192 Istanbul)-", + "uid": "fiber (Rome → Istanbul)-", "metadata": { "location": { "latitude": 41.4949998, @@ -5059,7 +6235,7 @@ } }, { - "uid": "fiber (Orlando \u2192 Jacksonville)-", + "uid": "fiber (Orlando → Jacksonville)-", "metadata": { "location": { "latitude": 29.4150012, @@ -5077,7 +6253,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Kansas_City)-", + "uid": "fiber (Omaha → Kansas_City)-", "metadata": { "location": { "latitude": 40.1899988, @@ -5095,7 +6271,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Kansas_City)-", + "uid": "fiber (St_Louis → Kansas_City)-", "metadata": { "location": { "latitude": 38.879999600000005, @@ -5113,7 +6289,7 @@ } }, { - "uid": "fiber (Tulsa \u2192 Kansas_City)-", + "uid": "fiber (Tulsa → Kansas_City)-", "metadata": { "location": { "latitude": 37.6249996, @@ -5131,7 +6307,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 Las_Vegas)-", + "uid": "fiber (Phoenix → Las_Vegas)-", "metadata": { "location": { "latitude": 34.87499529, @@ -5149,7 +6325,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Las_Vegas)-", + "uid": "fiber (Salt_Lake_City → Las_Vegas)-", "metadata": { "location": { "latitude": 38.494994315, @@ -5167,7 +6343,7 @@ } }, { - "uid": "fiber (Memphis \u2192 Little_Rock)-", + "uid": "fiber (Memphis → Little_Rock)-", "metadata": { "location": { "latitude": 34.9150005, @@ -5185,7 +6361,7 @@ } }, { - "uid": "fiber (Paris \u2192 London)-", + "uid": "fiber (Paris → London)-", "metadata": { "location": { "latitude": 50.19000025, @@ -5203,7 +6379,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 London)-", + "uid": "fiber (Washington_DC → London)-", "metadata": { "location": { "latitude": 45.2150004, @@ -5221,7 +6397,7 @@ } }, { - "uid": "fiber (New_York \u2192 Long_Island)-", + "uid": "fiber (New_York → Long_Island)-", "metadata": { "location": { "latitude": 40.629999100000006, @@ -5239,7 +6415,7 @@ } }, { - "uid": "fiber (San_Diego \u2192 Los_Angeles)-", + "uid": "fiber (San_Diego → Los_Angeles)-", "metadata": { "location": { "latitude": 33.46000135, @@ -5257,7 +6433,7 @@ } }, { - "uid": "fiber (Santa_Barbara \u2192 Los_Angeles)-", + "uid": "fiber (Santa_Barbara → Los_Angeles)-", "metadata": { "location": { "latitude": 34.270000605, @@ -5275,7 +6451,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Louisville)-", + "uid": "fiber (Nashville → Louisville)-", "metadata": { "location": { "latitude": 37.19499965, @@ -5293,7 +6469,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Louisville)-", + "uid": "fiber (St_Louis → Louisville)-", "metadata": { "location": { "latitude": 38.43000045, @@ -5311,7 +6487,7 @@ } }, { - "uid": "fiber (Paris \u2192 Madrid)-", + "uid": "fiber (Paris → Madrid)-", "metadata": { "location": { "latitude": 44.639999, @@ -5329,7 +6505,7 @@ } }, { - "uid": "fiber (Zurich \u2192 Madrid)-", + "uid": "fiber (Zurich → Madrid)-", "metadata": { "location": { "latitude": 43.89999975, @@ -5347,7 +6523,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Memphis)-", + "uid": "fiber (Nashville → Memphis)-", "metadata": { "location": { "latitude": 35.6399997, @@ -5365,7 +6541,7 @@ } }, { - "uid": "fiber (Paris \u2192 Miami)-", + "uid": "fiber (Paris → Miami)-", "metadata": { "location": { "latitude": 37.320000300000004, @@ -5383,7 +6559,7 @@ } }, { - "uid": "fiber (Tampa \u2192 Miami)-", + "uid": "fiber (Tampa → Miami)-", "metadata": { "location": { "latitude": 26.8699997, @@ -5401,7 +6577,7 @@ } }, { - "uid": "fiber (West_Palm_Beach \u2192 Miami)-", + "uid": "fiber (West_Palm_Beach → Miami)-", "metadata": { "location": { "latitude": 26.26500015, @@ -5419,7 +6595,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Milwaukee)-", + "uid": "fiber (Minneapolis → Milwaukee)-", "metadata": { "location": { "latitude": 44.01000005, @@ -5437,7 +6613,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Minneapolis)-", + "uid": "fiber (Omaha → Minneapolis)-", "metadata": { "location": { "latitude": 43.1099986, @@ -5455,7 +6631,7 @@ } }, { - "uid": "fiber (Rome \u2192 Mumbai)-", + "uid": "fiber (Rome → Mumbai)-", "metadata": { "location": { "latitude": 30.42499915, @@ -5473,7 +6649,7 @@ } }, { - "uid": "fiber (Singapore \u2192 Mumbai)-", + "uid": "fiber (Singapore → Mumbai)-", "metadata": { "location": { "latitude": 10.1299993035, @@ -5491,7 +6667,7 @@ } }, { - "uid": "fiber (Tallahassee \u2192 New_Orleans)-", + "uid": "fiber (Tallahassee → New_Orleans)-", "metadata": { "location": { "latitude": 30.265, @@ -5509,7 +6685,7 @@ } }, { - "uid": "fiber (Newark \u2192 New_York)-", + "uid": "fiber (Newark → New_York)-", "metadata": { "location": { "latitude": 40.69499975, @@ -5527,7 +6703,7 @@ } }, { - "uid": "fiber (Scranton \u2192 New_York)-", + "uid": "fiber (Scranton → New_York)-", "metadata": { "location": { "latitude": 41.034999150000004, @@ -5545,7 +6721,7 @@ } }, { - "uid": "fiber (Wilmington \u2192 New_York)-", + "uid": "fiber (Wilmington → New_York)-", "metadata": { "location": { "latitude": 40.20500005, @@ -5563,7 +6739,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Newark)-", + "uid": "fiber (Philadelphia → Newark)-", "metadata": { "location": { "latitude": 40.364999850000004, @@ -5581,7 +6757,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Norfolk)-", + "uid": "fiber (Raleigh → Norfolk)-", "metadata": { "location": { "latitude": 36.36999885, @@ -5599,7 +6775,7 @@ } }, { - "uid": "fiber (Wilmington \u2192 Norfolk)-", + "uid": "fiber (Wilmington → Norfolk)-", "metadata": { "location": { "latitude": 38.33, @@ -5617,7 +6793,7 @@ } }, { - "uid": "fiber (Sacramento \u2192 Oakland)-", + "uid": "fiber (Sacramento → Oakland)-", "metadata": { "location": { "latitude": 38.170000085, @@ -5635,7 +6811,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Oakland)-", + "uid": "fiber (Salt_Lake_City → Oakland)-", "metadata": { "location": { "latitude": 39.27499967, @@ -5653,7 +6829,7 @@ } }, { - "uid": "fiber (San_Francisco \u2192 Oakland)-", + "uid": "fiber (San_Francisco → Oakland)-", "metadata": { "location": { "latitude": 37.715000065, @@ -5671,7 +6847,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Oakland)-", + "uid": "fiber (Taipei → Oakland)-", "metadata": { "location": { "latitude": 31.395000605, @@ -5689,7 +6865,7 @@ } }, { - "uid": "fiber (Tulsa \u2192 Oklahoma_City)-", + "uid": "fiber (Tulsa → Oklahoma_City)-", "metadata": { "location": { "latitude": 35.80000075, @@ -5707,7 +6883,7 @@ } }, { - "uid": "fiber (West_Palm_Beach \u2192 Orlando)-", + "uid": "fiber (West_Palm_Beach → Orlando)-", "metadata": { "location": { "latitude": 27.62499955, @@ -5725,7 +6901,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Philadelphia)-", + "uid": "fiber (Scranton → Philadelphia)-", "metadata": { "location": { "latitude": 40.70499925, @@ -5743,7 +6919,7 @@ } }, { - "uid": "fiber (San_Diego \u2192 Phoenix)-", + "uid": "fiber (San_Diego → Phoenix)-", "metadata": { "location": { "latitude": 33.17500114, @@ -5761,7 +6937,7 @@ } }, { - "uid": "fiber (Tucson \u2192 Phoenix)-", + "uid": "fiber (Tucson → Phoenix)-", "metadata": { "location": { "latitude": 32.87000029, @@ -5779,7 +6955,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Pittsburgh)-", + "uid": "fiber (Scranton → Pittsburgh)-", "metadata": { "location": { "latitude": 40.849999999999994, @@ -5797,7 +6973,7 @@ } }, { - "uid": "fiber (Sacramento \u2192 Portland)-", + "uid": "fiber (Sacramento → Portland)-", "metadata": { "location": { "latitude": 42.05500009, @@ -5815,7 +6991,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Portland)-", + "uid": "fiber (Salt_Lake_City → Portland)-", "metadata": { "location": { "latitude": 43.159999675, @@ -5833,7 +7009,7 @@ } }, { - "uid": "fiber (Seattle \u2192 Portland)-", + "uid": "fiber (Seattle → Portland)-", "metadata": { "location": { "latitude": 46.57999994, @@ -5851,7 +7027,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Portland)-", + "uid": "fiber (Tokyo → Portland)-", "metadata": { "location": { "latitude": 40.604999660000004, @@ -5869,7 +7045,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Richmond)-", + "uid": "fiber (Washington_DC → Richmond)-", "metadata": { "location": { "latitude": 38.21999945, @@ -5887,7 +7063,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Rochester)-", + "uid": "fiber (Syracuse → Rochester)-", "metadata": { "location": { "latitude": 43.10499975, @@ -5905,7 +7081,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Rome)-", + "uid": "fiber (Vienna → Rome)-", "metadata": { "location": { "latitude": 45.055001000000004, @@ -5923,7 +7099,7 @@ } }, { - "uid": "fiber (Zurich \u2192 Rome)-", + "uid": "fiber (Zurich → Rome)-", "metadata": { "location": { "latitude": 44.63500055, @@ -5941,7 +7117,7 @@ } }, { - "uid": "fiber (San_Jose \u2192 San_Francisco)-", + "uid": "fiber (San_Jose → San_Francisco)-", "metadata": { "location": { "latitude": 37.479999445000004, @@ -5959,7 +7135,7 @@ } }, { - "uid": "fiber (Santa_Barbara \u2192 San_Jose)-", + "uid": "fiber (Santa_Barbara → San_Jose)-", "metadata": { "location": { "latitude": 35.86499984, @@ -5977,7 +7153,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Scranton)-", + "uid": "fiber (Syracuse → Scranton)-", "metadata": { "location": { "latitude": 42.2200005, @@ -5995,7 +7171,7 @@ } }, { - "uid": "fiber (Spokane \u2192 Seattle)-", + "uid": "fiber (Spokane → Seattle)-", "metadata": { "location": { "latitude": 47.644998605, @@ -6013,7 +7189,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Seoul)-", + "uid": "fiber (Tokyo → Seoul)-", "metadata": { "location": { "latitude": 36.614999839999996, @@ -6031,7 +7207,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Singapore)-", + "uid": "fiber (Sydney → Singapore)-", "metadata": { "location": { "latitude": -16.2849995265, @@ -6049,7 +7225,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Springfield)-", + "uid": "fiber (St_Louis → Springfield)-", "metadata": { "location": { "latitude": 39.07, @@ -6067,7 +7243,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Taipei)-", + "uid": "fiber (Tokyo → Taipei)-", "metadata": { "location": { "latitude": 30.344999549999997, @@ -6085,7 +7261,7 @@ } }, { - "uid": "fiber (Tampa \u2192 Tallahassee)-", + "uid": "fiber (Tampa → Tallahassee)-", "metadata": { "location": { "latitude": 29.2099994, @@ -6103,7 +7279,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Vienna)-", + "uid": "fiber (Warsaw → Vienna)-", "metadata": { "location": { "latitude": 50.24000055, @@ -6123,1789 +7299,2197 @@ ], "connections": [ { - "from_node": "fiber (Dallas \u2192 Abilene)-", - "to_node": "fiber (Abilene \u2192 El_Paso)-" + "from_node": "roadm Abilene", + "to_node": "fiber (Abilene → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Abilene)-", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Abilene", + "to_node": "fiber (Abilene → El_Paso)-" + }, + { + "from_node": "fiber (El_Paso → Abilene)-", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Albany", + "to_node": "fiber (Albany → Boston)-" }, { - "from_node": "fiber (El_Paso \u2192 Abilene)-", - "to_node": "fiber (Abilene \u2192 Dallas)-" + "from_node": "fiber (Boston → Albany)-", + "to_node": "roadm Albany" }, { - "from_node": "fiber (Boston \u2192 Albany)-", - "to_node": "fiber (Albany \u2192 Syracuse)-" + "from_node": "roadm Albany", + "to_node": "fiber (Albany → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Albany)-", - "to_node": "fiber (Albany \u2192 Boston)-" + "from_node": "fiber (Syracuse → Albany)-", + "to_node": "roadm Albany" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Dallas)-" + "to_node": "fiber (Albuquerque → Dallas)-" }, { - "from_node": "fiber (Dallas \u2192 Albuquerque)-", + "from_node": "fiber (Dallas → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Denver)-" + "to_node": "fiber (Albuquerque → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Albuquerque)-", + "from_node": "fiber (Denver → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 El_Paso)-" + "to_node": "fiber (Albuquerque → El_Paso)-" }, { - "from_node": "fiber (El_Paso \u2192 Albuquerque)-", + "from_node": "fiber (El_Paso → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Las_Vegas)-" + "to_node": "fiber (Albuquerque → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Albuquerque)-", + "from_node": "fiber (Las_Vegas → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Birmingham)-" + "to_node": "fiber (Atlanta → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 Atlanta)-", + "from_node": "fiber (Birmingham → Atlanta)-", "to_node": "roadm Atlanta" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Charlotte)-" + "to_node": "fiber (Atlanta → Charlotte)-" }, { - "from_node": "fiber (Charlotte \u2192 Atlanta)-", + "from_node": "fiber (Charlotte → Atlanta)-", "to_node": "roadm Atlanta" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Jacksonville)-" + "to_node": "fiber (Atlanta → Jacksonville)-" }, { - "from_node": "fiber (Jacksonville \u2192 Atlanta)-", + "from_node": "fiber (Jacksonville → Atlanta)-", "to_node": "roadm Atlanta" }, { - "from_node": "fiber (Houston \u2192 Austin)-", - "to_node": "fiber (Austin \u2192 San_Antonio)-" + "from_node": "roadm Austin", + "to_node": "fiber (Austin → Houston)-" }, { - "from_node": "fiber (San_Antonio \u2192 Austin)-", - "to_node": "fiber (Austin \u2192 Houston)-" + "from_node": "fiber (Houston → Austin)-", + "to_node": "roadm Austin" + }, + { + "from_node": "roadm Austin", + "to_node": "fiber (Austin → San_Antonio)-" + }, + { + "from_node": "fiber (San_Antonio → Austin)-", + "to_node": "roadm Austin" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Philadelphia)-" + "to_node": "fiber (Baltimore → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Baltimore)-", + "from_node": "fiber (Philadelphia → Baltimore)-", "to_node": "roadm Baltimore" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Pittsburgh)-" + "to_node": "fiber (Baltimore → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Baltimore)-", + "from_node": "fiber (Pittsburgh → Baltimore)-", "to_node": "roadm Baltimore" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Washington_DC)-" + "to_node": "fiber (Baltimore → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 Baltimore)-", + "from_node": "fiber (Washington_DC → Baltimore)-", "to_node": "roadm Baltimore" }, { - "from_node": "fiber (Houston \u2192 Baton_Rouge)-", - "to_node": "fiber (Baton_Rouge \u2192 New_Orleans)-" + "from_node": "roadm Baton_Rouge", + "to_node": "fiber (Baton_Rouge → Houston)-" + }, + { + "from_node": "fiber (Houston → Baton_Rouge)-", + "to_node": "roadm Baton_Rouge" + }, + { + "from_node": "roadm Baton_Rouge", + "to_node": "fiber (Baton_Rouge → New_Orleans)-" }, { - "from_node": "fiber (New_Orleans \u2192 Baton_Rouge)-", - "to_node": "fiber (Baton_Rouge \u2192 Houston)-" + "from_node": "fiber (New_Orleans → Baton_Rouge)-", + "to_node": "roadm Baton_Rouge" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Bismarck)-" + "to_node": "fiber (Billings → Bismarck)-" }, { - "from_node": "fiber (Bismarck \u2192 Billings)-", + "from_node": "fiber (Bismarck → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Denver)-" + "to_node": "fiber (Billings → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Billings)-", + "from_node": "fiber (Denver → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Spokane)-" + "to_node": "fiber (Billings → Spokane)-" }, { - "from_node": "fiber (Spokane \u2192 Billings)-", + "from_node": "fiber (Spokane → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 Atlanta)-" + "to_node": "fiber (Birmingham → Atlanta)-" }, { - "from_node": "fiber (Atlanta \u2192 Birmingham)-", + "from_node": "fiber (Atlanta → Birmingham)-", "to_node": "roadm Birmingham" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 Nashville)-" + "to_node": "fiber (Birmingham → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Birmingham)-", + "from_node": "fiber (Nashville → Birmingham)-", "to_node": "roadm Birmingham" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 New_Orleans)-" + "to_node": "fiber (Birmingham → New_Orleans)-" }, { - "from_node": "fiber (New_Orleans \u2192 Birmingham)-", + "from_node": "fiber (New_Orleans → Birmingham)-", "to_node": "roadm Birmingham" }, { - "from_node": "fiber (Billings \u2192 Bismarck)-", - "to_node": "fiber (Bismarck \u2192 Minneapolis)-" + "from_node": "roadm Bismarck", + "to_node": "fiber (Bismarck → Billings)-" + }, + { + "from_node": "fiber (Billings → Bismarck)-", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Bismarck", + "to_node": "fiber (Bismarck → Minneapolis)-" + }, + { + "from_node": "fiber (Minneapolis → Bismarck)-", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Boston", + "to_node": "fiber (Boston → Albany)-" }, { - "from_node": "fiber (Minneapolis \u2192 Bismarck)-", - "to_node": "fiber (Bismarck \u2192 Billings)-" + "from_node": "fiber (Albany → Boston)-", + "to_node": "roadm Boston" }, { - "from_node": "fiber (Albany \u2192 Boston)-", - "to_node": "fiber (Boston \u2192 Providence)-" + "from_node": "roadm Boston", + "to_node": "fiber (Boston → Providence)-" }, { - "from_node": "fiber (Providence \u2192 Boston)-", - "to_node": "fiber (Boston \u2192 Albany)-" + "from_node": "fiber (Providence → Boston)-", + "to_node": "roadm Boston" }, { - "from_node": "fiber (Cleveland \u2192 Buffalo)-", - "to_node": "fiber (Buffalo \u2192 Rochester)-" + "from_node": "roadm Buffalo", + "to_node": "fiber (Buffalo → Cleveland)-" }, { - "from_node": "fiber (Rochester \u2192 Buffalo)-", - "to_node": "fiber (Buffalo \u2192 Cleveland)-" + "from_node": "fiber (Cleveland → Buffalo)-", + "to_node": "roadm Buffalo" }, { - "from_node": "fiber (Jacksonville \u2192 Charleston)-", - "to_node": "fiber (Charleston \u2192 Raleigh)-" + "from_node": "roadm Buffalo", + "to_node": "fiber (Buffalo → Rochester)-" }, { - "from_node": "fiber (Raleigh \u2192 Charleston)-", - "to_node": "fiber (Charleston \u2192 Jacksonville)-" + "from_node": "fiber (Rochester → Buffalo)-", + "to_node": "roadm Buffalo" }, { - "from_node": "fiber (Atlanta \u2192 Charlotte)-", - "to_node": "fiber (Charlotte \u2192 Greensboro)-" + "from_node": "roadm Charleston", + "to_node": "fiber (Charleston → Jacksonville)-" }, { - "from_node": "fiber (Greensboro \u2192 Charlotte)-", - "to_node": "fiber (Charlotte \u2192 Atlanta)-" + "from_node": "fiber (Jacksonville → Charleston)-", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charleston", + "to_node": "fiber (Charleston → Raleigh)-" + }, + { + "from_node": "fiber (Raleigh → Charleston)-", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charlotte", + "to_node": "fiber (Charlotte → Atlanta)-" + }, + { + "from_node": "fiber (Atlanta → Charlotte)-", + "to_node": "roadm Charlotte" + }, + { + "from_node": "roadm Charlotte", + "to_node": "fiber (Charlotte → Greensboro)-" + }, + { + "from_node": "fiber (Greensboro → Charlotte)-", + "to_node": "roadm Charlotte" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Detroit)-" + "to_node": "fiber (Chicago → Detroit)-" }, { - "from_node": "fiber (Detroit \u2192 Chicago)-", + "from_node": "fiber (Detroit → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Milwaukee)-" + "to_node": "fiber (Chicago → Milwaukee)-" }, { - "from_node": "fiber (Milwaukee \u2192 Chicago)-", + "from_node": "fiber (Milwaukee → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Springfield)-" + "to_node": "fiber (Chicago → Springfield)-" }, { - "from_node": "fiber (Springfield \u2192 Chicago)-", + "from_node": "fiber (Springfield → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Columbus)-" + "to_node": "fiber (Cincinnati → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Cincinnati)-", + "from_node": "fiber (Columbus → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Louisville)-" + "to_node": "fiber (Cincinnati → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Cincinnati)-", + "from_node": "fiber (Louisville → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Washington_DC)-" + "to_node": "fiber (Cincinnati → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 Cincinnati)-", + "from_node": "fiber (Washington_DC → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Buffalo)-" + "to_node": "fiber (Cleveland → Buffalo)-" }, { - "from_node": "fiber (Buffalo \u2192 Cleveland)-", + "from_node": "fiber (Buffalo → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Columbus)-" + "to_node": "fiber (Cleveland → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Cleveland)-", + "from_node": "fiber (Columbus → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Toledo)-" + "to_node": "fiber (Cleveland → Toledo)-" }, { - "from_node": "fiber (Toledo \u2192 Cleveland)-", + "from_node": "fiber (Toledo → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Cincinnati)-" + "to_node": "fiber (Columbus → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Columbus)-", + "from_node": "fiber (Cincinnati → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Cleveland)-" + "to_node": "fiber (Columbus → Cleveland)-" }, { - "from_node": "fiber (Cleveland \u2192 Columbus)-", + "from_node": "fiber (Cleveland → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Pittsburgh)-" + "to_node": "fiber (Columbus → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Columbus)-", + "from_node": "fiber (Pittsburgh → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Abilene)-" + "to_node": "fiber (Dallas → Abilene)-" }, { - "from_node": "fiber (Abilene \u2192 Dallas)-", + "from_node": "fiber (Abilene → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Albuquerque)-" + "to_node": "fiber (Dallas → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Dallas)-", + "from_node": "fiber (Albuquerque → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Houston)-" + "to_node": "fiber (Dallas → Houston)-" }, { - "from_node": "fiber (Houston \u2192 Dallas)-", + "from_node": "fiber (Houston → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Little_Rock)-" + "to_node": "fiber (Dallas → Little_Rock)-" }, { - "from_node": "fiber (Little_Rock \u2192 Dallas)-", + "from_node": "fiber (Little_Rock → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Oklahoma_City)-" + "to_node": "fiber (Dallas → Oklahoma_City)-" }, { - "from_node": "fiber (Oklahoma_City \u2192 Dallas)-", + "from_node": "fiber (Oklahoma_City → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Albuquerque)-" + "to_node": "fiber (Denver → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Denver)-", + "from_node": "fiber (Albuquerque → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Billings)-" + "to_node": "fiber (Denver → Billings)-" }, { - "from_node": "fiber (Billings \u2192 Denver)-", + "from_node": "fiber (Billings → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Omaha)-" + "to_node": "fiber (Denver → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Denver)-", + "from_node": "fiber (Omaha → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Salt_Lake_City)-" + "to_node": "fiber (Denver → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Denver)-", + "from_node": "fiber (Salt_Lake_City → Denver)-", "to_node": "roadm Denver" }, { - "from_node": "fiber (Chicago \u2192 Detroit)-", - "to_node": "fiber (Detroit \u2192 Toledo)-" + "from_node": "roadm Detroit", + "to_node": "fiber (Detroit → Chicago)-" + }, + { + "from_node": "fiber (Chicago → Detroit)-", + "to_node": "roadm Detroit" + }, + { + "from_node": "roadm Detroit", + "to_node": "fiber (Detroit → Toledo)-" }, { - "from_node": "fiber (Toledo \u2192 Detroit)-", - "to_node": "fiber (Detroit \u2192 Chicago)-" + "from_node": "fiber (Toledo → Detroit)-", + "to_node": "roadm Detroit" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Abilene)-" + "to_node": "fiber (El_Paso → Abilene)-" }, { - "from_node": "fiber (Abilene \u2192 El_Paso)-", + "from_node": "fiber (Abilene → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Albuquerque)-" + "to_node": "fiber (El_Paso → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 El_Paso)-", + "from_node": "fiber (Albuquerque → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 San_Antonio)-" + "to_node": "fiber (El_Paso → San_Antonio)-" }, { - "from_node": "fiber (San_Antonio \u2192 El_Paso)-", + "from_node": "fiber (San_Antonio → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Tucson)-" + "to_node": "fiber (El_Paso → Tucson)-" }, { - "from_node": "fiber (Tucson \u2192 El_Paso)-", + "from_node": "fiber (Tucson → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Las_Vegas)-" + "to_node": "fiber (Fresno → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Fresno)-", + "from_node": "fiber (Las_Vegas → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Los_Angeles)-" + "to_node": "fiber (Fresno → Los_Angeles)-" }, { - "from_node": "fiber (Los_Angeles \u2192 Fresno)-", + "from_node": "fiber (Los_Angeles → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Oakland)-" + "to_node": "fiber (Fresno → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Fresno)-", + "from_node": "fiber (Oakland → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Charlotte)-" + "to_node": "fiber (Greensboro → Charlotte)-" }, { - "from_node": "fiber (Charlotte \u2192 Greensboro)-", + "from_node": "fiber (Charlotte → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Louisville)-" + "to_node": "fiber (Greensboro → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Greensboro)-", + "from_node": "fiber (Louisville → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Raleigh)-" + "to_node": "fiber (Greensboro → Raleigh)-" }, { - "from_node": "fiber (Raleigh \u2192 Greensboro)-", + "from_node": "fiber (Raleigh → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Richmond)-" + "to_node": "fiber (Greensboro → Richmond)-" }, { - "from_node": "fiber (Richmond \u2192 Greensboro)-", + "from_node": "fiber (Richmond → Greensboro)-", "to_node": "roadm Greensboro" }, { - "from_node": "fiber (Long_Island \u2192 Hartford)-", - "to_node": "fiber (Hartford \u2192 Providence)-" + "from_node": "roadm Hartford", + "to_node": "fiber (Hartford → Long_Island)-" }, { - "from_node": "fiber (Providence \u2192 Hartford)-", - "to_node": "fiber (Hartford \u2192 Long_Island)-" + "from_node": "fiber (Long_Island → Hartford)-", + "to_node": "roadm Hartford" + }, + { + "from_node": "roadm Hartford", + "to_node": "fiber (Hartford → Providence)-" + }, + { + "from_node": "fiber (Providence → Hartford)-", + "to_node": "roadm Hartford" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Austin)-" + "to_node": "fiber (Houston → Austin)-" }, { - "from_node": "fiber (Austin \u2192 Houston)-", + "from_node": "fiber (Austin → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Baton_Rouge)-" + "to_node": "fiber (Houston → Baton_Rouge)-" }, { - "from_node": "fiber (Baton_Rouge \u2192 Houston)-", + "from_node": "fiber (Baton_Rouge → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Dallas)-" + "to_node": "fiber (Houston → Dallas)-" }, { - "from_node": "fiber (Dallas \u2192 Houston)-", + "from_node": "fiber (Dallas → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Atlanta)-" + "to_node": "fiber (Jacksonville → Atlanta)-" }, { - "from_node": "fiber (Atlanta \u2192 Jacksonville)-", + "from_node": "fiber (Atlanta → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Charleston)-" + "to_node": "fiber (Jacksonville → Charleston)-" }, { - "from_node": "fiber (Charleston \u2192 Jacksonville)-", + "from_node": "fiber (Charleston → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Orlando)-" + "to_node": "fiber (Jacksonville → Orlando)-" }, { - "from_node": "fiber (Orlando \u2192 Jacksonville)-", + "from_node": "fiber (Orlando → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 Omaha)-" + "to_node": "fiber (Kansas_City → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Kansas_City)-", + "from_node": "fiber (Omaha → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 St_Louis)-" + "to_node": "fiber (Kansas_City → St_Louis)-" }, { - "from_node": "fiber (St_Louis \u2192 Kansas_City)-", + "from_node": "fiber (St_Louis → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 Tulsa)-" + "to_node": "fiber (Kansas_City → Tulsa)-" }, { - "from_node": "fiber (Tulsa \u2192 Kansas_City)-", + "from_node": "fiber (Tulsa → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Albuquerque)-" + "to_node": "fiber (Las_Vegas → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Las_Vegas)-", + "from_node": "fiber (Albuquerque → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Fresno)-" + "to_node": "fiber (Las_Vegas → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Las_Vegas)-", + "from_node": "fiber (Fresno → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Phoenix)-" + "to_node": "fiber (Las_Vegas → Phoenix)-" }, { - "from_node": "fiber (Phoenix \u2192 Las_Vegas)-", + "from_node": "fiber (Phoenix → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Salt_Lake_City)-" + "to_node": "fiber (Las_Vegas → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Las_Vegas)-", + "from_node": "fiber (Salt_Lake_City → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { - "from_node": "fiber (Dallas \u2192 Little_Rock)-", - "to_node": "fiber (Little_Rock \u2192 Memphis)-" + "from_node": "roadm Little_Rock", + "to_node": "fiber (Little_Rock → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Little_Rock)-", + "to_node": "roadm Little_Rock" + }, + { + "from_node": "roadm Little_Rock", + "to_node": "fiber (Little_Rock → Memphis)-" }, { - "from_node": "fiber (Memphis \u2192 Little_Rock)-", - "to_node": "fiber (Little_Rock \u2192 Dallas)-" + "from_node": "fiber (Memphis → Little_Rock)-", + "to_node": "roadm Little_Rock" }, { - "from_node": "fiber (Hartford \u2192 Long_Island)-", - "to_node": "fiber (Long_Island \u2192 New_York)-" + "from_node": "roadm Long_Island", + "to_node": "fiber (Long_Island → Hartford)-" }, { - "from_node": "fiber (New_York \u2192 Long_Island)-", - "to_node": "fiber (Long_Island \u2192 Hartford)-" + "from_node": "fiber (Hartford → Long_Island)-", + "to_node": "roadm Long_Island" + }, + { + "from_node": "roadm Long_Island", + "to_node": "fiber (Long_Island → New_York)-" + }, + { + "from_node": "fiber (New_York → Long_Island)-", + "to_node": "roadm Long_Island" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Fresno)-" + "to_node": "fiber (Los_Angeles → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Los_Angeles)-", + "from_node": "fiber (Fresno → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Honolulu)-" + "to_node": "fiber (Los_Angeles → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Los_Angeles)-", + "from_node": "fiber (Honolulu → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 San_Diego)-" + "to_node": "fiber (Los_Angeles → San_Diego)-" }, { - "from_node": "fiber (San_Diego \u2192 Los_Angeles)-", + "from_node": "fiber (San_Diego → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Santa_Barbara)-" + "to_node": "fiber (Los_Angeles → Santa_Barbara)-" }, { - "from_node": "fiber (Santa_Barbara \u2192 Los_Angeles)-", + "from_node": "fiber (Santa_Barbara → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Cincinnati)-" + "to_node": "fiber (Louisville → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Louisville)-", + "from_node": "fiber (Cincinnati → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Greensboro)-" + "to_node": "fiber (Louisville → Greensboro)-" }, { - "from_node": "fiber (Greensboro \u2192 Louisville)-", + "from_node": "fiber (Greensboro → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Nashville)-" + "to_node": "fiber (Louisville → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Louisville)-", + "from_node": "fiber (Nashville → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 St_Louis)-" + "to_node": "fiber (Louisville → St_Louis)-" }, { - "from_node": "fiber (St_Louis \u2192 Louisville)-", + "from_node": "fiber (St_Louis → Louisville)-", "to_node": "roadm Louisville" }, { - "from_node": "fiber (Little_Rock \u2192 Memphis)-", - "to_node": "fiber (Memphis \u2192 Nashville)-" + "from_node": "roadm Memphis", + "to_node": "fiber (Memphis → Little_Rock)-" + }, + { + "from_node": "fiber (Little_Rock → Memphis)-", + "to_node": "roadm Memphis" + }, + { + "from_node": "roadm Memphis", + "to_node": "fiber (Memphis → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Memphis)-", - "to_node": "fiber (Memphis \u2192 Little_Rock)-" + "from_node": "fiber (Nashville → Memphis)-", + "to_node": "roadm Memphis" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 Paris)-" + "to_node": "fiber (Miami → Paris)-" }, { - "from_node": "fiber (Paris \u2192 Miami)-", + "from_node": "fiber (Paris → Miami)-", "to_node": "roadm Miami" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 Tampa)-" + "to_node": "fiber (Miami → Tampa)-" }, { - "from_node": "fiber (Tampa \u2192 Miami)-", + "from_node": "fiber (Tampa → Miami)-", "to_node": "roadm Miami" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 West_Palm_Beach)-" + "to_node": "fiber (Miami → West_Palm_Beach)-" }, { - "from_node": "fiber (West_Palm_Beach \u2192 Miami)-", + "from_node": "fiber (West_Palm_Beach → Miami)-", "to_node": "roadm Miami" }, { - "from_node": "fiber (Chicago \u2192 Milwaukee)-", - "to_node": "fiber (Milwaukee \u2192 Minneapolis)-" + "from_node": "roadm Milwaukee", + "to_node": "fiber (Milwaukee → Chicago)-" }, { - "from_node": "fiber (Minneapolis \u2192 Milwaukee)-", - "to_node": "fiber (Milwaukee \u2192 Chicago)-" + "from_node": "fiber (Chicago → Milwaukee)-", + "to_node": "roadm Milwaukee" + }, + { + "from_node": "roadm Milwaukee", + "to_node": "fiber (Milwaukee → Minneapolis)-" + }, + { + "from_node": "fiber (Minneapolis → Milwaukee)-", + "to_node": "roadm Milwaukee" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Bismarck)-" + "to_node": "fiber (Minneapolis → Bismarck)-" }, { - "from_node": "fiber (Bismarck \u2192 Minneapolis)-", + "from_node": "fiber (Bismarck → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Milwaukee)-" + "to_node": "fiber (Minneapolis → Milwaukee)-" }, { - "from_node": "fiber (Milwaukee \u2192 Minneapolis)-", + "from_node": "fiber (Milwaukee → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Omaha)-" + "to_node": "fiber (Minneapolis → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Minneapolis)-", + "from_node": "fiber (Omaha → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Birmingham)-" + "to_node": "fiber (Nashville → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 Nashville)-", + "from_node": "fiber (Birmingham → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Louisville)-" + "to_node": "fiber (Nashville → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Nashville)-", + "from_node": "fiber (Louisville → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Memphis)-" + "to_node": "fiber (Nashville → Memphis)-" }, { - "from_node": "fiber (Memphis \u2192 Nashville)-", + "from_node": "fiber (Memphis → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Baton_Rouge)-" + "to_node": "fiber (New_Orleans → Baton_Rouge)-" }, { - "from_node": "fiber (Baton_Rouge \u2192 New_Orleans)-", + "from_node": "fiber (Baton_Rouge → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Birmingham)-" + "to_node": "fiber (New_Orleans → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 New_Orleans)-", + "from_node": "fiber (Birmingham → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Tallahassee)-" + "to_node": "fiber (New_Orleans → Tallahassee)-" }, { - "from_node": "fiber (Tallahassee \u2192 New_Orleans)-", + "from_node": "fiber (Tallahassee → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Amsterdam)-" + "to_node": "fiber (New_York → Amsterdam)-" }, { - "from_node": "fiber (Amsterdam \u2192 New_York)-", + "from_node": "fiber (Amsterdam → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Long_Island)-" + "to_node": "fiber (New_York → Long_Island)-" }, { - "from_node": "fiber (Long_Island \u2192 New_York)-", + "from_node": "fiber (Long_Island → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Newark)-" + "to_node": "fiber (New_York → Newark)-" }, { - "from_node": "fiber (Newark \u2192 New_York)-", + "from_node": "fiber (Newark → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Scranton)-" + "to_node": "fiber (New_York → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 New_York)-", + "from_node": "fiber (Scranton → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Wilmington)-" + "to_node": "fiber (New_York → Wilmington)-" }, { - "from_node": "fiber (Wilmington \u2192 New_York)-", + "from_node": "fiber (Wilmington → New_York)-", "to_node": "roadm New_York" }, { - "from_node": "fiber (New_York \u2192 Newark)-", - "to_node": "fiber (Newark \u2192 Philadelphia)-" + "from_node": "roadm Newark", + "to_node": "fiber (Newark → New_York)-" + }, + { + "from_node": "fiber (New_York → Newark)-", + "to_node": "roadm Newark" + }, + { + "from_node": "roadm Newark", + "to_node": "fiber (Newark → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Newark)-", - "to_node": "fiber (Newark \u2192 New_York)-" + "from_node": "fiber (Philadelphia → Newark)-", + "to_node": "roadm Newark" }, { - "from_node": "fiber (Raleigh \u2192 Norfolk)-", - "to_node": "fiber (Norfolk \u2192 Wilmington)-" + "from_node": "roadm Norfolk", + "to_node": "fiber (Norfolk → Raleigh)-" }, { - "from_node": "fiber (Wilmington \u2192 Norfolk)-", - "to_node": "fiber (Norfolk \u2192 Raleigh)-" + "from_node": "fiber (Raleigh → Norfolk)-", + "to_node": "roadm Norfolk" + }, + { + "from_node": "roadm Norfolk", + "to_node": "fiber (Norfolk → Wilmington)-" + }, + { + "from_node": "fiber (Wilmington → Norfolk)-", + "to_node": "roadm Norfolk" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Fresno)-" + "to_node": "fiber (Oakland → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Oakland)-", + "from_node": "fiber (Fresno → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Sacramento)-" + "to_node": "fiber (Oakland → Sacramento)-" }, { - "from_node": "fiber (Sacramento \u2192 Oakland)-", + "from_node": "fiber (Sacramento → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Salt_Lake_City)-" + "to_node": "fiber (Oakland → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Oakland)-", + "from_node": "fiber (Salt_Lake_City → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 San_Francisco)-" + "to_node": "fiber (Oakland → San_Francisco)-" }, { - "from_node": "fiber (San_Francisco \u2192 Oakland)-", + "from_node": "fiber (San_Francisco → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Taipei)-" + "to_node": "fiber (Oakland → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Oakland)-", + "from_node": "fiber (Taipei → Oakland)-", "to_node": "roadm Oakland" }, { - "from_node": "fiber (Dallas \u2192 Oklahoma_City)-", - "to_node": "fiber (Oklahoma_City \u2192 Tulsa)-" + "from_node": "roadm Oklahoma_City", + "to_node": "fiber (Oklahoma_City → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Oklahoma_City)-", + "to_node": "roadm Oklahoma_City" + }, + { + "from_node": "roadm Oklahoma_City", + "to_node": "fiber (Oklahoma_City → Tulsa)-" }, { - "from_node": "fiber (Tulsa \u2192 Oklahoma_City)-", - "to_node": "fiber (Oklahoma_City \u2192 Dallas)-" + "from_node": "fiber (Tulsa → Oklahoma_City)-", + "to_node": "roadm Oklahoma_City" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Denver)-" + "to_node": "fiber (Omaha → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Omaha)-", + "from_node": "fiber (Denver → Omaha)-", "to_node": "roadm Omaha" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Kansas_City)-" + "to_node": "fiber (Omaha → Kansas_City)-" }, { - "from_node": "fiber (Kansas_City \u2192 Omaha)-", + "from_node": "fiber (Kansas_City → Omaha)-", "to_node": "roadm Omaha" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Minneapolis)-" + "to_node": "fiber (Omaha → Minneapolis)-" }, { - "from_node": "fiber (Minneapolis \u2192 Omaha)-", + "from_node": "fiber (Minneapolis → Omaha)-", "to_node": "roadm Omaha" }, { - "from_node": "fiber (Jacksonville \u2192 Orlando)-", - "to_node": "fiber (Orlando \u2192 West_Palm_Beach)-" + "from_node": "roadm Orlando", + "to_node": "fiber (Orlando → Jacksonville)-" }, { - "from_node": "fiber (West_Palm_Beach \u2192 Orlando)-", - "to_node": "fiber (Orlando \u2192 Jacksonville)-" + "from_node": "fiber (Jacksonville → Orlando)-", + "to_node": "roadm Orlando" + }, + { + "from_node": "roadm Orlando", + "to_node": "fiber (Orlando → West_Palm_Beach)-" + }, + { + "from_node": "fiber (West_Palm_Beach → Orlando)-", + "to_node": "roadm Orlando" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Baltimore)-" + "to_node": "fiber (Philadelphia → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Philadelphia)-", + "from_node": "fiber (Baltimore → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Newark)-" + "to_node": "fiber (Philadelphia → Newark)-" }, { - "from_node": "fiber (Newark \u2192 Philadelphia)-", + "from_node": "fiber (Newark → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Scranton)-" + "to_node": "fiber (Philadelphia → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 Philadelphia)-", + "from_node": "fiber (Scranton → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 Las_Vegas)-" + "to_node": "fiber (Phoenix → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Phoenix)-", + "from_node": "fiber (Las_Vegas → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 San_Diego)-" + "to_node": "fiber (Phoenix → San_Diego)-" }, { - "from_node": "fiber (San_Diego \u2192 Phoenix)-", + "from_node": "fiber (San_Diego → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 Tucson)-" + "to_node": "fiber (Phoenix → Tucson)-" }, { - "from_node": "fiber (Tucson \u2192 Phoenix)-", + "from_node": "fiber (Tucson → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Baltimore)-" + "to_node": "fiber (Pittsburgh → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Pittsburgh)-", + "from_node": "fiber (Baltimore → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Columbus)-" + "to_node": "fiber (Pittsburgh → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Pittsburgh)-", + "from_node": "fiber (Columbus → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Scranton)-" + "to_node": "fiber (Pittsburgh → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 Pittsburgh)-", + "from_node": "fiber (Scranton → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Sacramento)-" + "to_node": "fiber (Portland → Sacramento)-" }, { - "from_node": "fiber (Sacramento \u2192 Portland)-", + "from_node": "fiber (Sacramento → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Salt_Lake_City)-" + "to_node": "fiber (Portland → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Portland)-", + "from_node": "fiber (Salt_Lake_City → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Seattle)-" + "to_node": "fiber (Portland → Seattle)-" }, { - "from_node": "fiber (Seattle \u2192 Portland)-", + "from_node": "fiber (Seattle → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Tokyo)-" + "to_node": "fiber (Portland → Tokyo)-" }, { - "from_node": "fiber (Tokyo \u2192 Portland)-", + "from_node": "fiber (Tokyo → Portland)-", "to_node": "roadm Portland" }, { - "from_node": "fiber (Boston \u2192 Providence)-", - "to_node": "fiber (Providence \u2192 Hartford)-" + "from_node": "roadm Providence", + "to_node": "fiber (Providence → Boston)-" + }, + { + "from_node": "fiber (Boston → Providence)-", + "to_node": "roadm Providence" + }, + { + "from_node": "roadm Providence", + "to_node": "fiber (Providence → Hartford)-" }, { - "from_node": "fiber (Hartford \u2192 Providence)-", - "to_node": "fiber (Providence \u2192 Boston)-" + "from_node": "fiber (Hartford → Providence)-", + "to_node": "roadm Providence" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Charleston)-" + "to_node": "fiber (Raleigh → Charleston)-" }, { - "from_node": "fiber (Charleston \u2192 Raleigh)-", + "from_node": "fiber (Charleston → Raleigh)-", "to_node": "roadm Raleigh" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Greensboro)-" + "to_node": "fiber (Raleigh → Greensboro)-" }, { - "from_node": "fiber (Greensboro \u2192 Raleigh)-", + "from_node": "fiber (Greensboro → Raleigh)-", "to_node": "roadm Raleigh" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Norfolk)-" + "to_node": "fiber (Raleigh → Norfolk)-" }, { - "from_node": "fiber (Norfolk \u2192 Raleigh)-", + "from_node": "fiber (Norfolk → Raleigh)-", "to_node": "roadm Raleigh" }, { - "from_node": "fiber (Greensboro \u2192 Richmond)-", - "to_node": "fiber (Richmond \u2192 Washington_DC)-" + "from_node": "roadm Richmond", + "to_node": "fiber (Richmond → Greensboro)-" + }, + { + "from_node": "fiber (Greensboro → Richmond)-", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Richmond", + "to_node": "fiber (Richmond → Washington_DC)-" + }, + { + "from_node": "fiber (Washington_DC → Richmond)-", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Rochester", + "to_node": "fiber (Rochester → Buffalo)-" }, { - "from_node": "fiber (Washington_DC \u2192 Richmond)-", - "to_node": "fiber (Richmond \u2192 Greensboro)-" + "from_node": "fiber (Buffalo → Rochester)-", + "to_node": "roadm Rochester" }, { - "from_node": "fiber (Buffalo \u2192 Rochester)-", - "to_node": "fiber (Rochester \u2192 Syracuse)-" + "from_node": "roadm Rochester", + "to_node": "fiber (Rochester → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Rochester)-", - "to_node": "fiber (Rochester \u2192 Buffalo)-" + "from_node": "fiber (Syracuse → Rochester)-", + "to_node": "roadm Rochester" }, { - "from_node": "fiber (Oakland \u2192 Sacramento)-", - "to_node": "fiber (Sacramento \u2192 Portland)-" + "from_node": "roadm Sacramento", + "to_node": "fiber (Sacramento → Oakland)-" }, { - "from_node": "fiber (Portland \u2192 Sacramento)-", - "to_node": "fiber (Sacramento \u2192 Oakland)-" + "from_node": "fiber (Oakland → Sacramento)-", + "to_node": "roadm Sacramento" + }, + { + "from_node": "roadm Sacramento", + "to_node": "fiber (Sacramento → Portland)-" + }, + { + "from_node": "fiber (Portland → Sacramento)-", + "to_node": "roadm Sacramento" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Denver)-" + "to_node": "fiber (Salt_Lake_City → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Salt_Lake_City)-", + "from_node": "fiber (Denver → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Las_Vegas)-" + "to_node": "fiber (Salt_Lake_City → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Salt_Lake_City)-", + "from_node": "fiber (Las_Vegas → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Oakland)-" + "to_node": "fiber (Salt_Lake_City → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Salt_Lake_City)-", + "from_node": "fiber (Oakland → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Portland)-" + "to_node": "fiber (Salt_Lake_City → Portland)-" }, { - "from_node": "fiber (Portland \u2192 Salt_Lake_City)-", + "from_node": "fiber (Portland → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { - "from_node": "fiber (Austin \u2192 San_Antonio)-", - "to_node": "fiber (San_Antonio \u2192 El_Paso)-" + "from_node": "roadm San_Antonio", + "to_node": "fiber (San_Antonio → Austin)-" + }, + { + "from_node": "fiber (Austin → San_Antonio)-", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Antonio", + "to_node": "fiber (San_Antonio → El_Paso)-" + }, + { + "from_node": "fiber (El_Paso → San_Antonio)-", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Diego", + "to_node": "fiber (San_Diego → Los_Angeles)-" + }, + { + "from_node": "fiber (Los_Angeles → San_Diego)-", + "to_node": "roadm San_Diego" + }, + { + "from_node": "roadm San_Diego", + "to_node": "fiber (San_Diego → Phoenix)-" }, { - "from_node": "fiber (El_Paso \u2192 San_Antonio)-", - "to_node": "fiber (San_Antonio \u2192 Austin)-" + "from_node": "fiber (Phoenix → San_Diego)-", + "to_node": "roadm San_Diego" }, { - "from_node": "fiber (Los_Angeles \u2192 San_Diego)-", - "to_node": "fiber (San_Diego \u2192 Phoenix)-" + "from_node": "roadm San_Francisco", + "to_node": "fiber (San_Francisco → Oakland)-" }, { - "from_node": "fiber (Phoenix \u2192 San_Diego)-", - "to_node": "fiber (San_Diego \u2192 Los_Angeles)-" + "from_node": "fiber (Oakland → San_Francisco)-", + "to_node": "roadm San_Francisco" }, { - "from_node": "fiber (Oakland \u2192 San_Francisco)-", - "to_node": "fiber (San_Francisco \u2192 San_Jose)-" + "from_node": "roadm San_Francisco", + "to_node": "fiber (San_Francisco → San_Jose)-" }, { - "from_node": "fiber (San_Jose \u2192 San_Francisco)-", - "to_node": "fiber (San_Francisco \u2192 Oakland)-" + "from_node": "fiber (San_Jose → San_Francisco)-", + "to_node": "roadm San_Francisco" }, { - "from_node": "fiber (San_Francisco \u2192 San_Jose)-", - "to_node": "fiber (San_Jose \u2192 Santa_Barbara)-" + "from_node": "roadm San_Jose", + "to_node": "fiber (San_Jose → San_Francisco)-" }, { - "from_node": "fiber (Santa_Barbara \u2192 San_Jose)-", - "to_node": "fiber (San_Jose \u2192 San_Francisco)-" + "from_node": "fiber (San_Francisco → San_Jose)-", + "to_node": "roadm San_Jose" }, { - "from_node": "fiber (Los_Angeles \u2192 Santa_Barbara)-", - "to_node": "fiber (Santa_Barbara \u2192 San_Jose)-" + "from_node": "roadm San_Jose", + "to_node": "fiber (San_Jose → Santa_Barbara)-" }, { - "from_node": "fiber (San_Jose \u2192 Santa_Barbara)-", - "to_node": "fiber (Santa_Barbara \u2192 Los_Angeles)-" + "from_node": "fiber (Santa_Barbara → San_Jose)-", + "to_node": "roadm San_Jose" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "fiber (Santa_Barbara → Los_Angeles)-" + }, + { + "from_node": "fiber (Los_Angeles → Santa_Barbara)-", + "to_node": "roadm Santa_Barbara" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "fiber (Santa_Barbara → San_Jose)-" + }, + { + "from_node": "fiber (San_Jose → Santa_Barbara)-", + "to_node": "roadm Santa_Barbara" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 New_York)-" + "to_node": "fiber (Scranton → New_York)-" }, { - "from_node": "fiber (New_York \u2192 Scranton)-", + "from_node": "fiber (New_York → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Philadelphia)-" + "to_node": "fiber (Scranton → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Scranton)-", + "from_node": "fiber (Philadelphia → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Pittsburgh)-" + "to_node": "fiber (Scranton → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Scranton)-", + "from_node": "fiber (Pittsburgh → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Syracuse)-" + "to_node": "fiber (Scranton → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Scranton)-", + "from_node": "fiber (Syracuse → Scranton)-", "to_node": "roadm Scranton" }, { - "from_node": "fiber (Portland \u2192 Seattle)-", - "to_node": "fiber (Seattle \u2192 Spokane)-" + "from_node": "roadm Seattle", + "to_node": "fiber (Seattle → Portland)-" + }, + { + "from_node": "fiber (Portland → Seattle)-", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Seattle", + "to_node": "fiber (Seattle → Spokane)-" + }, + { + "from_node": "fiber (Spokane → Seattle)-", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Spokane", + "to_node": "fiber (Spokane → Billings)-" }, { - "from_node": "fiber (Spokane \u2192 Seattle)-", - "to_node": "fiber (Seattle \u2192 Portland)-" + "from_node": "fiber (Billings → Spokane)-", + "to_node": "roadm Spokane" }, { - "from_node": "fiber (Billings \u2192 Spokane)-", - "to_node": "fiber (Spokane \u2192 Seattle)-" + "from_node": "roadm Spokane", + "to_node": "fiber (Spokane → Seattle)-" }, { - "from_node": "fiber (Seattle \u2192 Spokane)-", - "to_node": "fiber (Spokane \u2192 Billings)-" + "from_node": "fiber (Seattle → Spokane)-", + "to_node": "roadm Spokane" }, { - "from_node": "fiber (Chicago \u2192 Springfield)-", - "to_node": "fiber (Springfield \u2192 St_Louis)-" + "from_node": "roadm Springfield", + "to_node": "fiber (Springfield → Chicago)-" }, { - "from_node": "fiber (St_Louis \u2192 Springfield)-", - "to_node": "fiber (Springfield \u2192 Chicago)-" + "from_node": "fiber (Chicago → Springfield)-", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm Springfield", + "to_node": "fiber (Springfield → St_Louis)-" + }, + { + "from_node": "fiber (St_Louis → Springfield)-", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm St_Louis", + "to_node": "fiber (St_Louis → Kansas_City)-" + }, + { + "from_node": "fiber (Kansas_City → St_Louis)-", + "to_node": "roadm St_Louis" }, { "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Kansas_City)-" + "to_node": "fiber (St_Louis → Louisville)-" }, { - "from_node": "fiber (Kansas_City \u2192 St_Louis)-", + "from_node": "fiber (Louisville → St_Louis)-", "to_node": "roadm St_Louis" }, { "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Louisville)-" + "to_node": "fiber (St_Louis → Springfield)-" }, { - "from_node": "fiber (Louisville \u2192 St_Louis)-", + "from_node": "fiber (Springfield → St_Louis)-", "to_node": "roadm St_Louis" }, { - "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Springfield)-" + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Albany)-" + }, + { + "from_node": "fiber (Albany → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Rochester)-" + }, + { + "from_node": "fiber (Rochester → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Scranton)-" + }, + { + "from_node": "fiber (Scranton → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "fiber (Tallahassee → New_Orleans)-" + }, + { + "from_node": "fiber (New_Orleans → Tallahassee)-", + "to_node": "roadm Tallahassee" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "fiber (Tallahassee → Tampa)-" }, { - "from_node": "fiber (Springfield \u2192 St_Louis)-", - "to_node": "roadm St_Louis" + "from_node": "fiber (Tampa → Tallahassee)-", + "to_node": "roadm Tallahassee" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Albany)-" + "from_node": "roadm Tampa", + "to_node": "fiber (Tampa → Miami)-" }, { - "from_node": "fiber (Albany \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Miami → Tampa)-", + "to_node": "roadm Tampa" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Rochester)-" + "from_node": "roadm Tampa", + "to_node": "fiber (Tampa → Tallahassee)-" }, { - "from_node": "fiber (Rochester \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Tallahassee → Tampa)-", + "to_node": "roadm Tampa" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Scranton)-" + "from_node": "roadm Toledo", + "to_node": "fiber (Toledo → Cleveland)-" }, { - "from_node": "fiber (Scranton \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Cleveland → Toledo)-", + "to_node": "roadm Toledo" }, { - "from_node": "fiber (New_Orleans \u2192 Tallahassee)-", - "to_node": "fiber (Tallahassee \u2192 Tampa)-" + "from_node": "roadm Toledo", + "to_node": "fiber (Toledo → Detroit)-" }, { - "from_node": "fiber (Tampa \u2192 Tallahassee)-", - "to_node": "fiber (Tallahassee \u2192 New_Orleans)-" + "from_node": "fiber (Detroit → Toledo)-", + "to_node": "roadm Toledo" }, { - "from_node": "fiber (Miami \u2192 Tampa)-", - "to_node": "fiber (Tampa \u2192 Tallahassee)-" + "from_node": "roadm Tucson", + "to_node": "fiber (Tucson → El_Paso)-" }, { - "from_node": "fiber (Tallahassee \u2192 Tampa)-", - "to_node": "fiber (Tampa \u2192 Miami)-" + "from_node": "fiber (El_Paso → Tucson)-", + "to_node": "roadm Tucson" }, { - "from_node": "fiber (Cleveland \u2192 Toledo)-", - "to_node": "fiber (Toledo \u2192 Detroit)-" + "from_node": "roadm Tucson", + "to_node": "fiber (Tucson → Phoenix)-" }, { - "from_node": "fiber (Detroit \u2192 Toledo)-", - "to_node": "fiber (Toledo \u2192 Cleveland)-" + "from_node": "fiber (Phoenix → Tucson)-", + "to_node": "roadm Tucson" }, { - "from_node": "fiber (El_Paso \u2192 Tucson)-", - "to_node": "fiber (Tucson \u2192 Phoenix)-" + "from_node": "roadm Tulsa", + "to_node": "fiber (Tulsa → Kansas_City)-" }, { - "from_node": "fiber (Phoenix \u2192 Tucson)-", - "to_node": "fiber (Tucson \u2192 El_Paso)-" + "from_node": "fiber (Kansas_City → Tulsa)-", + "to_node": "roadm Tulsa" }, { - "from_node": "fiber (Kansas_City \u2192 Tulsa)-", - "to_node": "fiber (Tulsa \u2192 Oklahoma_City)-" + "from_node": "roadm Tulsa", + "to_node": "fiber (Tulsa → Oklahoma_City)-" }, { - "from_node": "fiber (Oklahoma_City \u2192 Tulsa)-", - "to_node": "fiber (Tulsa \u2192 Kansas_City)-" + "from_node": "fiber (Oklahoma_City → Tulsa)-", + "to_node": "roadm Tulsa" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Baltimore)-" + "to_node": "fiber (Washington_DC → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Washington_DC)-", + "from_node": "fiber (Baltimore → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Cincinnati)-" + "to_node": "fiber (Washington_DC → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Washington_DC)-", + "from_node": "fiber (Cincinnati → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 London)-" + "to_node": "fiber (Washington_DC → London)-" }, { - "from_node": "fiber (London \u2192 Washington_DC)-", + "from_node": "fiber (London → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Richmond)-" + "to_node": "fiber (Washington_DC → Richmond)-" }, { - "from_node": "fiber (Richmond \u2192 Washington_DC)-", + "from_node": "fiber (Richmond → Washington_DC)-", "to_node": "roadm Washington_DC" }, { - "from_node": "fiber (Miami \u2192 West_Palm_Beach)-", - "to_node": "fiber (West_Palm_Beach \u2192 Orlando)-" + "from_node": "roadm West_Palm_Beach", + "to_node": "fiber (West_Palm_Beach → Miami)-" + }, + { + "from_node": "fiber (Miami → West_Palm_Beach)-", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm West_Palm_Beach", + "to_node": "fiber (West_Palm_Beach → Orlando)-" + }, + { + "from_node": "fiber (Orlando → West_Palm_Beach)-", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm Wilmington", + "to_node": "fiber (Wilmington → New_York)-" }, { - "from_node": "fiber (Orlando \u2192 West_Palm_Beach)-", - "to_node": "fiber (West_Palm_Beach \u2192 Miami)-" + "from_node": "fiber (New_York → Wilmington)-", + "to_node": "roadm Wilmington" }, { - "from_node": "fiber (New_York \u2192 Wilmington)-", - "to_node": "fiber (Wilmington \u2192 Norfolk)-" + "from_node": "roadm Wilmington", + "to_node": "fiber (Wilmington → Norfolk)-" }, { - "from_node": "fiber (Norfolk \u2192 Wilmington)-", - "to_node": "fiber (Wilmington \u2192 New_York)-" + "from_node": "fiber (Norfolk → Wilmington)-", + "to_node": "roadm Wilmington" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Berlin)-" + "to_node": "fiber (Amsterdam → Berlin)-" }, { - "from_node": "fiber (Berlin \u2192 Amsterdam)-", + "from_node": "fiber (Berlin → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Brussels)-" + "to_node": "fiber (Amsterdam → Brussels)-" }, { - "from_node": "fiber (Brussels \u2192 Amsterdam)-", + "from_node": "fiber (Brussels → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Frankfurt)-" + "to_node": "fiber (Amsterdam → Frankfurt)-" }, { - "from_node": "fiber (Frankfurt \u2192 Amsterdam)-", + "from_node": "fiber (Frankfurt → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 New_York)-" + "to_node": "fiber (Amsterdam → New_York)-" }, { - "from_node": "fiber (New_York \u2192 Amsterdam)-", + "from_node": "fiber (New_York → Amsterdam)-", "to_node": "roadm Amsterdam" }, { - "from_node": "fiber (Amsterdam \u2192 Berlin)-", - "to_node": "fiber (Berlin \u2192 Warsaw)-" + "from_node": "roadm Berlin", + "to_node": "fiber (Berlin → Amsterdam)-" + }, + { + "from_node": "fiber (Amsterdam → Berlin)-", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Berlin", + "to_node": "fiber (Berlin → Warsaw)-" + }, + { + "from_node": "fiber (Warsaw → Berlin)-", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Brussels", + "to_node": "fiber (Brussels → Amsterdam)-" + }, + { + "from_node": "fiber (Amsterdam → Brussels)-", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Brussels", + "to_node": "fiber (Brussels → London)-" + }, + { + "from_node": "fiber (London → Brussels)-", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Bucharest", + "to_node": "fiber (Bucharest → Istanbul)-" }, { - "from_node": "fiber (Warsaw \u2192 Berlin)-", - "to_node": "fiber (Berlin \u2192 Amsterdam)-" + "from_node": "fiber (Istanbul → Bucharest)-", + "to_node": "roadm Bucharest" }, { - "from_node": "fiber (Amsterdam \u2192 Brussels)-", - "to_node": "fiber (Brussels \u2192 London)-" + "from_node": "roadm Bucharest", + "to_node": "fiber (Bucharest → Warsaw)-" }, { - "from_node": "fiber (London \u2192 Brussels)-", - "to_node": "fiber (Brussels \u2192 Amsterdam)-" + "from_node": "fiber (Warsaw → Bucharest)-", + "to_node": "roadm Bucharest" }, { - "from_node": "fiber (Istanbul \u2192 Bucharest)-", - "to_node": "fiber (Bucharest \u2192 Warsaw)-" + "from_node": "roadm Frankfurt", + "to_node": "fiber (Frankfurt → Amsterdam)-" }, { - "from_node": "fiber (Warsaw \u2192 Bucharest)-", - "to_node": "fiber (Bucharest \u2192 Istanbul)-" + "from_node": "fiber (Amsterdam → Frankfurt)-", + "to_node": "roadm Frankfurt" }, { - "from_node": "fiber (Amsterdam \u2192 Frankfurt)-", - "to_node": "fiber (Frankfurt \u2192 Vienna)-" + "from_node": "roadm Frankfurt", + "to_node": "fiber (Frankfurt → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Frankfurt)-", - "to_node": "fiber (Frankfurt \u2192 Amsterdam)-" + "from_node": "fiber (Vienna → Frankfurt)-", + "to_node": "roadm Frankfurt" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Bucharest)-" + "to_node": "fiber (Istanbul → Bucharest)-" }, { - "from_node": "fiber (Bucharest \u2192 Istanbul)-", + "from_node": "fiber (Bucharest → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Delhi)-" + "to_node": "fiber (Istanbul → Delhi)-" }, { - "from_node": "fiber (Delhi \u2192 Istanbul)-", + "from_node": "fiber (Delhi → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Rome)-" + "to_node": "fiber (Istanbul → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Istanbul)-", + "from_node": "fiber (Rome → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Brussels)-" + "to_node": "fiber (London → Brussels)-" }, { - "from_node": "fiber (Brussels \u2192 London)-", + "from_node": "fiber (Brussels → London)-", "to_node": "roadm London" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Paris)-" + "to_node": "fiber (London → Paris)-" }, { - "from_node": "fiber (Paris \u2192 London)-", + "from_node": "fiber (Paris → London)-", "to_node": "roadm London" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Washington_DC)-" + "to_node": "fiber (London → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 London)-", + "from_node": "fiber (Washington_DC → London)-", "to_node": "roadm London" }, { - "from_node": "fiber (Paris \u2192 Madrid)-", - "to_node": "fiber (Madrid \u2192 Zurich)-" + "from_node": "roadm Madrid", + "to_node": "fiber (Madrid → Paris)-" }, { - "from_node": "fiber (Zurich \u2192 Madrid)-", - "to_node": "fiber (Madrid \u2192 Paris)-" + "from_node": "fiber (Paris → Madrid)-", + "to_node": "roadm Madrid" + }, + { + "from_node": "roadm Madrid", + "to_node": "fiber (Madrid → Zurich)-" + }, + { + "from_node": "fiber (Zurich → Madrid)-", + "to_node": "roadm Madrid" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 London)-" + "to_node": "fiber (Paris → London)-" }, { - "from_node": "fiber (London \u2192 Paris)-", + "from_node": "fiber (London → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 Madrid)-" + "to_node": "fiber (Paris → Madrid)-" }, { - "from_node": "fiber (Madrid \u2192 Paris)-", + "from_node": "fiber (Madrid → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 Miami)-" + "to_node": "fiber (Paris → Miami)-" }, { - "from_node": "fiber (Miami \u2192 Paris)-", + "from_node": "fiber (Miami → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Istanbul)-" + "to_node": "fiber (Rome → Istanbul)-" }, { - "from_node": "fiber (Istanbul \u2192 Rome)-", + "from_node": "fiber (Istanbul → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Mumbai)-" + "to_node": "fiber (Rome → Mumbai)-" }, { - "from_node": "fiber (Mumbai \u2192 Rome)-", + "from_node": "fiber (Mumbai → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Vienna)-" + "to_node": "fiber (Rome → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Rome)-", + "from_node": "fiber (Vienna → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Zurich)-" + "to_node": "fiber (Rome → Zurich)-" }, { - "from_node": "fiber (Zurich \u2192 Rome)-", + "from_node": "fiber (Zurich → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Frankfurt)-" + "to_node": "fiber (Vienna → Frankfurt)-" }, { - "from_node": "fiber (Frankfurt \u2192 Vienna)-", + "from_node": "fiber (Frankfurt → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Rome)-" + "to_node": "fiber (Vienna → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Vienna)-", + "from_node": "fiber (Rome → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Warsaw)-" + "to_node": "fiber (Vienna → Warsaw)-" }, { - "from_node": "fiber (Warsaw \u2192 Vienna)-", + "from_node": "fiber (Warsaw → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Berlin)-" + "to_node": "fiber (Warsaw → Berlin)-" }, { - "from_node": "fiber (Berlin \u2192 Warsaw)-", + "from_node": "fiber (Berlin → Warsaw)-", "to_node": "roadm Warsaw" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Bucharest)-" + "to_node": "fiber (Warsaw → Bucharest)-" }, { - "from_node": "fiber (Bucharest \u2192 Warsaw)-", + "from_node": "fiber (Bucharest → Warsaw)-", "to_node": "roadm Warsaw" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Vienna)-" + "to_node": "fiber (Warsaw → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Warsaw)-", + "from_node": "fiber (Vienna → Warsaw)-", "to_node": "roadm Warsaw" }, { - "from_node": "fiber (Madrid \u2192 Zurich)-", - "to_node": "fiber (Zurich \u2192 Rome)-" + "from_node": "roadm Zurich", + "to_node": "fiber (Zurich → Madrid)-" + }, + { + "from_node": "fiber (Madrid → Zurich)-", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Zurich", + "to_node": "fiber (Zurich → Rome)-" + }, + { + "from_node": "fiber (Rome → Zurich)-", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Bangkok", + "to_node": "fiber (Bangkok → Delhi)-" + }, + { + "from_node": "fiber (Delhi → Bangkok)-", + "to_node": "roadm Bangkok" + }, + { + "from_node": "roadm Bangkok", + "to_node": "fiber (Bangkok → Hong_Kong)-" }, { - "from_node": "fiber (Rome \u2192 Zurich)-", - "to_node": "fiber (Zurich \u2192 Madrid)-" + "from_node": "fiber (Hong_Kong → Bangkok)-", + "to_node": "roadm Bangkok" }, { - "from_node": "fiber (Delhi \u2192 Bangkok)-", - "to_node": "fiber (Bangkok \u2192 Hong_Kong)-" + "from_node": "roadm Beijing", + "to_node": "fiber (Beijing → Seoul)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Bangkok)-", - "to_node": "fiber (Bangkok \u2192 Delhi)-" + "from_node": "fiber (Seoul → Beijing)-", + "to_node": "roadm Beijing" }, { - "from_node": "fiber (Seoul \u2192 Beijing)-", - "to_node": "fiber (Beijing \u2192 Shanghai)-" + "from_node": "roadm Beijing", + "to_node": "fiber (Beijing → Shanghai)-" }, { - "from_node": "fiber (Shanghai \u2192 Beijing)-", - "to_node": "fiber (Beijing \u2192 Seoul)-" + "from_node": "fiber (Shanghai → Beijing)-", + "to_node": "roadm Beijing" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Bangkok)-" + "to_node": "fiber (Delhi → Bangkok)-" }, { - "from_node": "fiber (Bangkok \u2192 Delhi)-", + "from_node": "fiber (Bangkok → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Istanbul)-" + "to_node": "fiber (Delhi → Istanbul)-" }, { - "from_node": "fiber (Istanbul \u2192 Delhi)-", + "from_node": "fiber (Istanbul → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Mumbai)-" + "to_node": "fiber (Delhi → Mumbai)-" }, { - "from_node": "fiber (Mumbai \u2192 Delhi)-", + "from_node": "fiber (Mumbai → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Bangkok)-" + "to_node": "fiber (Hong_Kong → Bangkok)-" }, { - "from_node": "fiber (Bangkok \u2192 Hong_Kong)-", + "from_node": "fiber (Bangkok → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Shanghai)-" + "to_node": "fiber (Hong_Kong → Shanghai)-" }, { - "from_node": "fiber (Shanghai \u2192 Hong_Kong)-", + "from_node": "fiber (Shanghai → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Sydney)-" + "to_node": "fiber (Hong_Kong → Sydney)-" }, { - "from_node": "fiber (Sydney \u2192 Hong_Kong)-", + "from_node": "fiber (Sydney → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Taipei)-" + "to_node": "fiber (Hong_Kong → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Hong_Kong)-", + "from_node": "fiber (Taipei → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Los_Angeles)-" + "to_node": "fiber (Honolulu → Los_Angeles)-" }, { - "from_node": "fiber (Los_Angeles \u2192 Honolulu)-", + "from_node": "fiber (Los_Angeles → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Sydney)-" + "to_node": "fiber (Honolulu → Sydney)-" }, { - "from_node": "fiber (Sydney \u2192 Honolulu)-", + "from_node": "fiber (Sydney → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Taipei)-" + "to_node": "fiber (Honolulu → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Honolulu)-", + "from_node": "fiber (Taipei → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Delhi)-" + "to_node": "fiber (Mumbai → Delhi)-" }, { - "from_node": "fiber (Delhi \u2192 Mumbai)-", + "from_node": "fiber (Delhi → Mumbai)-", "to_node": "roadm Mumbai" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Rome)-" + "to_node": "fiber (Mumbai → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Mumbai)-", + "from_node": "fiber (Rome → Mumbai)-", "to_node": "roadm Mumbai" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Singapore)-" + "to_node": "fiber (Mumbai → Singapore)-" }, { - "from_node": "fiber (Singapore \u2192 Mumbai)-", + "from_node": "fiber (Singapore → Mumbai)-", "to_node": "roadm Mumbai" }, { - "from_node": "fiber (Beijing \u2192 Seoul)-", - "to_node": "fiber (Seoul \u2192 Tokyo)-" + "from_node": "roadm Seoul", + "to_node": "fiber (Seoul → Beijing)-" }, { - "from_node": "fiber (Tokyo \u2192 Seoul)-", - "to_node": "fiber (Seoul \u2192 Beijing)-" + "from_node": "fiber (Beijing → Seoul)-", + "to_node": "roadm Seoul" }, { - "from_node": "fiber (Beijing \u2192 Shanghai)-", - "to_node": "fiber (Shanghai \u2192 Hong_Kong)-" + "from_node": "roadm Seoul", + "to_node": "fiber (Seoul → Tokyo)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Shanghai)-", - "to_node": "fiber (Shanghai \u2192 Beijing)-" + "from_node": "fiber (Tokyo → Seoul)-", + "to_node": "roadm Seoul" }, { - "from_node": "fiber (Mumbai \u2192 Singapore)-", - "to_node": "fiber (Singapore \u2192 Sydney)-" + "from_node": "roadm Shanghai", + "to_node": "fiber (Shanghai → Beijing)-" }, { - "from_node": "fiber (Sydney \u2192 Singapore)-", - "to_node": "fiber (Singapore \u2192 Mumbai)-" + "from_node": "fiber (Beijing → Shanghai)-", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Shanghai", + "to_node": "fiber (Shanghai → Hong_Kong)-" + }, + { + "from_node": "fiber (Hong_Kong → Shanghai)-", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Singapore", + "to_node": "fiber (Singapore → Mumbai)-" + }, + { + "from_node": "fiber (Mumbai → Singapore)-", + "to_node": "roadm Singapore" + }, + { + "from_node": "roadm Singapore", + "to_node": "fiber (Singapore → Sydney)-" + }, + { + "from_node": "fiber (Sydney → Singapore)-", + "to_node": "roadm Singapore" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Hong_Kong)-" + "to_node": "fiber (Sydney → Hong_Kong)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Sydney)-", + "from_node": "fiber (Hong_Kong → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Honolulu)-" + "to_node": "fiber (Sydney → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Sydney)-", + "from_node": "fiber (Honolulu → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Singapore)-" + "to_node": "fiber (Sydney → Singapore)-" }, { - "from_node": "fiber (Singapore \u2192 Sydney)-", + "from_node": "fiber (Singapore → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Hong_Kong)-" + "to_node": "fiber (Taipei → Hong_Kong)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Taipei)-", + "from_node": "fiber (Hong_Kong → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Honolulu)-" + "to_node": "fiber (Taipei → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Taipei)-", + "from_node": "fiber (Honolulu → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Oakland)-" + "to_node": "fiber (Taipei → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Taipei)-", + "from_node": "fiber (Oakland → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Tokyo)-" + "to_node": "fiber (Taipei → Tokyo)-" }, { - "from_node": "fiber (Tokyo \u2192 Taipei)-", + "from_node": "fiber (Tokyo → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Portland)-" + "to_node": "fiber (Tokyo → Portland)-" }, { - "from_node": "fiber (Portland \u2192 Tokyo)-", + "from_node": "fiber (Portland → Tokyo)-", "to_node": "roadm Tokyo" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Seoul)-" + "to_node": "fiber (Tokyo → Seoul)-" }, { - "from_node": "fiber (Seoul \u2192 Tokyo)-", + "from_node": "fiber (Seoul → Tokyo)-", "to_node": "roadm Tokyo" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Taipei)-" + "to_node": "fiber (Tokyo → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Tokyo)-", + "from_node": "fiber (Taipei → Tokyo)-", "to_node": "roadm Tokyo" }, + { + "from_node": "trx Abilene", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Abilene", + "to_node": "trx Abilene" + }, + { + "from_node": "trx Albany", + "to_node": "roadm Albany" + }, + { + "from_node": "roadm Albany", + "to_node": "trx Albany" + }, { "from_node": "trx Albuquerque", "to_node": "roadm Albuquerque" @@ -7922,6 +9506,14 @@ "from_node": "roadm Atlanta", "to_node": "trx Atlanta" }, + { + "from_node": "trx Austin", + "to_node": "roadm Austin" + }, + { + "from_node": "roadm Austin", + "to_node": "trx Austin" + }, { "from_node": "trx Baltimore", "to_node": "roadm Baltimore" @@ -7930,6 +9522,14 @@ "from_node": "roadm Baltimore", "to_node": "trx Baltimore" }, + { + "from_node": "trx Baton_Rouge", + "to_node": "roadm Baton_Rouge" + }, + { + "from_node": "roadm Baton_Rouge", + "to_node": "trx Baton_Rouge" + }, { "from_node": "trx Billings", "to_node": "roadm Billings" @@ -7946,6 +9546,46 @@ "from_node": "roadm Birmingham", "to_node": "trx Birmingham" }, + { + "from_node": "trx Bismarck", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Bismarck", + "to_node": "trx Bismarck" + }, + { + "from_node": "trx Boston", + "to_node": "roadm Boston" + }, + { + "from_node": "roadm Boston", + "to_node": "trx Boston" + }, + { + "from_node": "trx Buffalo", + "to_node": "roadm Buffalo" + }, + { + "from_node": "roadm Buffalo", + "to_node": "trx Buffalo" + }, + { + "from_node": "trx Charleston", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charleston", + "to_node": "trx Charleston" + }, + { + "from_node": "trx Charlotte", + "to_node": "roadm Charlotte" + }, + { + "from_node": "roadm Charlotte", + "to_node": "trx Charlotte" + }, { "from_node": "trx Chicago", "to_node": "roadm Chicago" @@ -7994,6 +9634,14 @@ "from_node": "roadm Denver", "to_node": "trx Denver" }, + { + "from_node": "trx Detroit", + "to_node": "roadm Detroit" + }, + { + "from_node": "roadm Detroit", + "to_node": "trx Detroit" + }, { "from_node": "trx El_Paso", "to_node": "roadm El_Paso" @@ -8018,6 +9666,14 @@ "from_node": "roadm Greensboro", "to_node": "trx Greensboro" }, + { + "from_node": "trx Hartford", + "to_node": "roadm Hartford" + }, + { + "from_node": "roadm Hartford", + "to_node": "trx Hartford" + }, { "from_node": "trx Houston", "to_node": "roadm Houston" @@ -8050,6 +9706,22 @@ "from_node": "roadm Las_Vegas", "to_node": "trx Las_Vegas" }, + { + "from_node": "trx Little_Rock", + "to_node": "roadm Little_Rock" + }, + { + "from_node": "roadm Little_Rock", + "to_node": "trx Little_Rock" + }, + { + "from_node": "trx Long_Island", + "to_node": "roadm Long_Island" + }, + { + "from_node": "roadm Long_Island", + "to_node": "trx Long_Island" + }, { "from_node": "trx Los_Angeles", "to_node": "roadm Los_Angeles" @@ -8066,6 +9738,14 @@ "from_node": "roadm Louisville", "to_node": "trx Louisville" }, + { + "from_node": "trx Memphis", + "to_node": "roadm Memphis" + }, + { + "from_node": "roadm Memphis", + "to_node": "trx Memphis" + }, { "from_node": "trx Miami", "to_node": "roadm Miami" @@ -8074,6 +9754,14 @@ "from_node": "roadm Miami", "to_node": "trx Miami" }, + { + "from_node": "trx Milwaukee", + "to_node": "roadm Milwaukee" + }, + { + "from_node": "roadm Milwaukee", + "to_node": "trx Milwaukee" + }, { "from_node": "trx Minneapolis", "to_node": "roadm Minneapolis" @@ -8106,6 +9794,22 @@ "from_node": "roadm New_York", "to_node": "trx New_York" }, + { + "from_node": "trx Newark", + "to_node": "roadm Newark" + }, + { + "from_node": "roadm Newark", + "to_node": "trx Newark" + }, + { + "from_node": "trx Norfolk", + "to_node": "roadm Norfolk" + }, + { + "from_node": "roadm Norfolk", + "to_node": "trx Norfolk" + }, { "from_node": "trx Oakland", "to_node": "roadm Oakland" @@ -8114,6 +9818,14 @@ "from_node": "roadm Oakland", "to_node": "trx Oakland" }, + { + "from_node": "trx Oklahoma_City", + "to_node": "roadm Oklahoma_City" + }, + { + "from_node": "roadm Oklahoma_City", + "to_node": "trx Oklahoma_City" + }, { "from_node": "trx Omaha", "to_node": "roadm Omaha" @@ -8122,6 +9834,14 @@ "from_node": "roadm Omaha", "to_node": "trx Omaha" }, + { + "from_node": "trx Orlando", + "to_node": "roadm Orlando" + }, + { + "from_node": "roadm Orlando", + "to_node": "trx Orlando" + }, { "from_node": "trx Philadelphia", "to_node": "roadm Philadelphia" @@ -8154,6 +9874,14 @@ "from_node": "roadm Portland", "to_node": "trx Portland" }, + { + "from_node": "trx Providence", + "to_node": "roadm Providence" + }, + { + "from_node": "roadm Providence", + "to_node": "trx Providence" + }, { "from_node": "trx Raleigh", "to_node": "roadm Raleigh" @@ -8162,6 +9890,30 @@ "from_node": "roadm Raleigh", "to_node": "trx Raleigh" }, + { + "from_node": "trx Richmond", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Richmond", + "to_node": "trx Richmond" + }, + { + "from_node": "trx Rochester", + "to_node": "roadm Rochester" + }, + { + "from_node": "roadm Rochester", + "to_node": "trx Rochester" + }, + { + "from_node": "trx Sacramento", + "to_node": "roadm Sacramento" + }, + { + "from_node": "roadm Sacramento", + "to_node": "trx Sacramento" + }, { "from_node": "trx Salt_Lake_City", "to_node": "roadm Salt_Lake_City" @@ -8170,6 +9922,46 @@ "from_node": "roadm Salt_Lake_City", "to_node": "trx Salt_Lake_City" }, + { + "from_node": "trx San_Antonio", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Antonio", + "to_node": "trx San_Antonio" + }, + { + "from_node": "trx San_Diego", + "to_node": "roadm San_Diego" + }, + { + "from_node": "roadm San_Diego", + "to_node": "trx San_Diego" + }, + { + "from_node": "trx San_Francisco", + "to_node": "roadm San_Francisco" + }, + { + "from_node": "roadm San_Francisco", + "to_node": "trx San_Francisco" + }, + { + "from_node": "trx San_Jose", + "to_node": "roadm San_Jose" + }, + { + "from_node": "roadm San_Jose", + "to_node": "trx San_Jose" + }, + { + "from_node": "trx Santa_Barbara", + "to_node": "roadm Santa_Barbara" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "trx Santa_Barbara" + }, { "from_node": "trx Scranton", "to_node": "roadm Scranton" @@ -8178,6 +9970,30 @@ "from_node": "roadm Scranton", "to_node": "trx Scranton" }, + { + "from_node": "trx Seattle", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Seattle", + "to_node": "trx Seattle" + }, + { + "from_node": "trx Spokane", + "to_node": "roadm Spokane" + }, + { + "from_node": "roadm Spokane", + "to_node": "trx Spokane" + }, + { + "from_node": "trx Springfield", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm Springfield", + "to_node": "trx Springfield" + }, { "from_node": "trx St_Louis", "to_node": "roadm St_Louis" @@ -8194,6 +10010,46 @@ "from_node": "roadm Syracuse", "to_node": "trx Syracuse" }, + { + "from_node": "trx Tallahassee", + "to_node": "roadm Tallahassee" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "trx Tallahassee" + }, + { + "from_node": "trx Tampa", + "to_node": "roadm Tampa" + }, + { + "from_node": "roadm Tampa", + "to_node": "trx Tampa" + }, + { + "from_node": "trx Toledo", + "to_node": "roadm Toledo" + }, + { + "from_node": "roadm Toledo", + "to_node": "trx Toledo" + }, + { + "from_node": "trx Tucson", + "to_node": "roadm Tucson" + }, + { + "from_node": "roadm Tucson", + "to_node": "trx Tucson" + }, + { + "from_node": "trx Tulsa", + "to_node": "roadm Tulsa" + }, + { + "from_node": "roadm Tulsa", + "to_node": "trx Tulsa" + }, { "from_node": "trx Washington_DC", "to_node": "roadm Washington_DC" @@ -8202,6 +10058,22 @@ "from_node": "roadm Washington_DC", "to_node": "trx Washington_DC" }, + { + "from_node": "trx West_Palm_Beach", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm West_Palm_Beach", + "to_node": "trx West_Palm_Beach" + }, + { + "from_node": "trx Wilmington", + "to_node": "roadm Wilmington" + }, + { + "from_node": "roadm Wilmington", + "to_node": "trx Wilmington" + }, { "from_node": "trx Amsterdam", "to_node": "roadm Amsterdam" @@ -8210,6 +10082,38 @@ "from_node": "roadm Amsterdam", "to_node": "trx Amsterdam" }, + { + "from_node": "trx Berlin", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Berlin", + "to_node": "trx Berlin" + }, + { + "from_node": "trx Brussels", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Brussels", + "to_node": "trx Brussels" + }, + { + "from_node": "trx Bucharest", + "to_node": "roadm Bucharest" + }, + { + "from_node": "roadm Bucharest", + "to_node": "trx Bucharest" + }, + { + "from_node": "trx Frankfurt", + "to_node": "roadm Frankfurt" + }, + { + "from_node": "roadm Frankfurt", + "to_node": "trx Frankfurt" + }, { "from_node": "trx Istanbul", "to_node": "roadm Istanbul" @@ -8226,6 +10130,14 @@ "from_node": "roadm London", "to_node": "trx London" }, + { + "from_node": "trx Madrid", + "to_node": "roadm Madrid" + }, + { + "from_node": "roadm Madrid", + "to_node": "trx Madrid" + }, { "from_node": "trx Paris", "to_node": "roadm Paris" @@ -8258,6 +10170,30 @@ "from_node": "roadm Warsaw", "to_node": "trx Warsaw" }, + { + "from_node": "trx Zurich", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Zurich", + "to_node": "trx Zurich" + }, + { + "from_node": "trx Bangkok", + "to_node": "roadm Bangkok" + }, + { + "from_node": "roadm Bangkok", + "to_node": "trx Bangkok" + }, + { + "from_node": "trx Beijing", + "to_node": "roadm Beijing" + }, + { + "from_node": "roadm Beijing", + "to_node": "trx Beijing" + }, { "from_node": "trx Delhi", "to_node": "roadm Delhi" @@ -8290,6 +10226,30 @@ "from_node": "roadm Mumbai", "to_node": "trx Mumbai" }, + { + "from_node": "trx Seoul", + "to_node": "roadm Seoul" + }, + { + "from_node": "roadm Seoul", + "to_node": "trx Seoul" + }, + { + "from_node": "trx Shanghai", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Shanghai", + "to_node": "trx Shanghai" + }, + { + "from_node": "trx Singapore", + "to_node": "roadm Singapore" + }, + { + "from_node": "roadm Singapore", + "to_node": "trx Singapore" + }, { "from_node": "trx Sydney", "to_node": "roadm Sydney" diff --git a/examples/CORONET_Global_Topology.xls b/examples/CORONET_Global_Topology.xls index 952d008b8a367d546300226d3a60024fb1dbd838..90b7e83acd35e9930b05ab2c67a63881e1c7b051 100644 GIT binary patch delta 7966 zcmaJ`32+WB&l#L(mpA3#2I50kj9@AFbY)Z!CreY}ze`VSy4WVM5A9t;{6yn`x zTa;z^-6Vk(b0PjVf(!9x3)7H|+yqat3YcIBJCd}Z`FLkiDi2gBXTdE}M6H-}Gn|qH zi-iZHEVHS4ix#91U*Tm?xl~3>kV1SoT6p`cPbwU6xg*~tzhUv}?CGZYN zV2!mc49U@$n!_j=Yc3_05=ep~fHG%US-oE=9ob*n>UYJU5=$8*Q`lJ+JAJ`{U2$d$ zAO#!|v)cODSRo1>WlAhnRJ{cWhOr@IjF&?-ECJ|qRVy)->T^*g;~8dC>MGS5K^A?k zjy_k3WjTbZ;zIO|^zEM=BEm#NL@^Js3Y_4HsYJAXu|Zh;PWy_pylf5ku{b5$zuhwf{fzueh(w zy0Iu#uk&qLS+}R&o(h&@T$_gDsYTL*crSJJX?}fxUr*6BPfEwWGzAygGK(^G|2JSW z-O>y6H21e-U0Oj=me4A6OU9Zui~I|UvUP2=Y;Bx!@Mv0rl#3_R>ZCk8PuF~AFBPC8 zy+A5t_vlT}YK#O+p)e9qd^Ya>~Gw&6Znk744R$Nc{m0h#> zHHWSWu5;M&WTo5WYQ@u4olM-gfQ!4!JM&zvS6f|h^{k+UQMWf2a<{iDx&5b__sg31 z`{kWQM&6W^z=Z7;bMWcn>?G>C2wLOFMt7Cl&}aTEtLz25)ZB7jYGnl%Z40^BtW{+| ztI81vCq45!&92rIyiCc#3B`-9QXb%Q#fOK=+|&Uc*6KAS-1vwlUeiQ@qY;Y~PmKF= zd~-gxf6dVuVhubL*FB4=vPoc}7n4mp;qVJ#%BxkaeU;TI5$?>HGhqn-^{z0H)>tkS z3WXI#u^Di3JV&vw#)p)47muKaZf|doM$kh?I359u=2z!c#?`o$VoBInl7}bDItsWO zyO@c#(5cl3J#%vL*-}RlnzCsYPX}nIFIQZOr@ucqHtC+WdhmF4U5<&`o(3swN~Ok^ z6Efi~>G@`+|ERjoX{4us+3^7znZweW?{vtCcW7b`H`UZteMbqo@ea-SR!v*Y+n;_B zPOLH2C@kNJ&yQze2BKzMTaQfL5X*0#-G)-m{VtZ1@8&6GKe)r|`{a84zM(c9?cz%^wF;}wn79Mn#wgz-dU1HGAfP{&r- zD-TVK2F3!DgT`AzNFXv(0)Cr#07f`NhIu_48c zYSVg7H^kBn_+Zn@!jtDN&@eEc##m(|{yXB+6iYXur+HZhnq+9E8~|&_CJs*GHO-gv zo@ik`v0->@Bsie8_{-)^d~g=Aj`Ri(PVNtk;;NRFynQeQVZ5nj5pTg_)&lSNWDuWi z8O%geDyH1$u`kM}yN=C%&}EQ7mamipIyW z_UbURY2!BIlY!Cc#oc(XJrBDV|Flw-SPDN(kEMAL-H?rUd?0v$5q~?N5v&yl@UzAB z^KPb^Nl**VNLD%l)L{w!3)$9hD8|Dx-`FpvgsAGhmrn$#I=)Swu8muSw3MCT(&9>?e7G zD+Y6WGk&pcuV{4H99p;`YhpIZgHq-vWhnO|~R~FE~Um7jzC!UJgBz zfyp4PFfvF7w0~j;Jz+B+b~Q_(6mU}cbV!+CrT3Gq+u}L4T8rm$47gkaF3*6=7aVm$ zq~HQUMmg4+_=0K2m%z<2;N}`|#R*)U9p)vFcD89uD1@!jQgHR^8`8g}RAr%VU(Oy; zd~5ZREHia3Tkd4Sop;c&qyrsm3gjfR$%Kc<4;$8~G8?G~{2{><0Y4^a6>!yBRkjIu zA3<5biv&{z+_6rT(*!(AFkQgx^{Sj9;21&nXho{=JVCpF6&qCg5)jp1yJ4}5H7r{+ z><&-IHIyi_sj-=~S~~YM>xyjM1z+9}kaKhd=-jBvxdL8KFi*e_2<8je;q}=Hbcjd1 z0l82|fFFBRxk$iOn^bv@fcFucE8svXO47h~`TqWm1 zvlBp-fnl`)S7X3A4LC~d39k|*b-{%q34k)XAR`=Q^8}7EdICquJb|M`p1@HOPvAn$ z24sr?$7a9yY{d3C@f>~hN#K?maBRmxzTR2+x6QzCnE|)lfNN(Q&g~tMmFVM)*Y>LN zd;u@?`fQ~-#F{MunSCUSMTB zm+n;Qqh+-9$98J@u9@e%L3UJh@qEWtZbpTTBlD^1ICSv=1c1RVwsnkSwMk>QGXlDJL-uFHU1X~3;A;8q)OYlur&#;i3k zTxYw)ozu-|^Madff$E2bb{m)CTTx*)sY3{q_~yV}q7oo_dyhY>0Z? zFc^?G>Im@9gR1NmaK(@+ZxZnCA)mZiz)uKv3%GQzPxc9T{oXEHj}Gy9Xz8 zAmkn7+CZ@q=RkRd>=(`WLw-kiw1pc)eYhxO>9cyoGkcR=zW4ai(K- z&TOYWoh5eGF+1&<*DuQ}FH=7@YQ~Aa0jwpYG~P7|FUSb*;?N$`X1G;`^U|38>0q;JXA z507=;GyaBTo_Y0nW2Piy(y1S3>b^8OblCBcQzs=SZawXqdHwiR8E1Z7DQ#i%8k@)1 zymgVjSF&k|O+#$jVbctoR@ex#S%c}>tiWagmi1YdXIY&kah9~1{#!aP-r9ZL^pm40 l>NWe0Iw#)115X|{y>=&l{p1yr7Z3g>l*XnT_T`No{{tRSPBj1k delta 6621 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 Date: Tue, 30 Oct 2018 12:00:47 -0400 Subject: [PATCH 027/108] small fix in case spacing_list is not sorted --- gnpy/core/equipment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 315b5ec70..4ee6a91b9 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -182,7 +182,7 @@ def automatic_spacing(baud_rate): """return the min possible channel spacing for a given baud rate""" spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples #[(max_baud_rate, spacing_for_this_baud_rate)] - return next((s[1] for s in spacing_list if s[0] > baud_rate), baud_rate*1.2) + return min((s[1] for s in spacing_list if s[0] > baud_rate), default=baud_rate*1.2) def automatic_nch(f_min, f_max, spacing): return int((f_max - f_min)//spacing) From ac8a96398a5704096dc15b78ea6d46facb84db86 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 23 Oct 2018 17:09:56 -0400 Subject: [PATCH 028/108] Update issue templates --- .github/ISSUE_TEMPLATE/feature_request.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..066b2d920 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,17 @@ +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 7d8224890378f8dfb5dbbb8bc4eb7c1d1aa25cd0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 30 Oct 2018 16:36:44 +0000 Subject: [PATCH 029/108] Small fix obsolete text not removed in the message made an error: I removed them Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 4e132c7b2..cd0d42f99 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -89,10 +89,6 @@ def requests_from_json(json_data,equipment): min recommanded spacing is {min_recommanded_spacing} max recommanded nb of channels is {max_recommanded_nb_channels} Computation stopped.''') - \n{fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp*1e-9} GHz \ - \nmin recommanded spacing is {min_recommanded_spacing}\ - \nmax recommanded nb of channels is {max_recommanded_nb_channels}\ - \nComputation stopped.' logger.critical(msg) raise ValueError(msg) requests_list.append(Path_request(**params)) From c168af46bcd7a46dab93ddd8f9ea4131b96967fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 30 Oct 2018 17:37:50 +0100 Subject: [PATCH 030/108] Update transmission_main_example.py --- examples/transmission_main_example.py | 66 ++++++++++++++++----------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 71edc5c6b..b1f4aea2f 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -148,38 +148,40 @@ def main(network, equipment, source, destination, req = None): for uid in transceivers: print(uid) exit() - + + #First try to find exact match if source/destination provided if args.source: - try: - source = next(transceivers[uid] for uid in transceivers if uid == args.source) - except StopIteration as e: - #TODO code a more advanced regex to find nodes match - nodes_suggestion = [uid for uid in transceivers \ - if args.source.lower() in uid.lower()] - source = transceivers[nodes_suggestion[0]] \ - if len(nodes_suggestion)>0 else list(transceivers.values())[0] - print(f'invalid souce node specified, did you mean:\ - \n{nodes_suggestion}?\ - \n{args.source!r}, replaced with {source.uid}') - del transceivers[source.uid] + 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') - source = list(transceivers.values())[0] - + if args.destination: - try: - destination = next(transceivers[uid] for uid in transceivers if uid == args.destination) - except StopIteration as e: - nodes_suggestion = [uid for uid in transceivers \ - if args.destination.lower() in uid.lower()] - destination = transceivers[nodes_suggestion[0]] \ - if len(nodes_suggestion)>0 else list(transceivers.values())[0] - print(f'invalid destination node specified, did you mean:\ - \n{nodes_suggestion}?\ - \n{args.destination!r}, replaced with {destination.uid}') + destination = transceivers.pop(args.destination, None) + valid_destination = True if destination else False else: - logger.info('No source node specified: picking random transceiver') - destination = list(transceivers.values())[1] + 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}') @@ -201,5 +203,15 @@ def main(network, equipment, source, destination, req = None): path = main(network, equipment, source, destination, req) save_network(args.filename, network) + 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) From f103bebe0524bc3f62bbe7be0ffdcd2f0da08b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Mon, 5 Nov 2018 17:13:03 +0100 Subject: [PATCH 031/108] Some modifications in split_fiber function. --- gnpy/core/network.py | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/gnpy/core/network.py b/gnpy/core/network.py index db2367093..aa7094711 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -337,23 +337,17 @@ def split_fiber(network, fiber, bounds, target_length, equipment): print(f'{repr(fiber)} is not properly connected, please check network topology') exit() - network.remove_edge(fiber, next_node) - network.remove_edge(prev_node, fiber) network.remove_node(fiber) - # update connector loss parameter with default values + 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 - new_spans = [ - Fiber( - uid = f'{fiber.uid}_({span}/{n_spans})', - metadata = fiber.metadata, - params = fiber_params - ) for span in range(n_spans) - ] - for new_span in new_spans: - new_span.length = new_length - network.add_node(new_span) + + for span in range(n_spans): + new_span = Fiber(uid = f'{fiber.uid}_({span}/{n_spans})', + metadata = fiber.metadata, + params = fiber_params) network.add_edge(prev_node, new_span) prev_node = new_span network.add_edge(prev_node, next_node) From f4f9868381d363e8287769876c5d9fd4d2d0133e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Mon, 5 Nov 2018 17:43:14 +0100 Subject: [PATCH 032/108] Change span numbering in split_fiber. --- gnpy/core/network.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnpy/core/network.py b/gnpy/core/network.py index aa7094711..e14947459 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -345,7 +345,7 @@ def split_fiber(network, fiber, bounds, target_length, equipment): fiber_params['con_out'] = fiber.con_out for span in range(n_spans): - new_span = Fiber(uid = f'{fiber.uid}_({span}/{n_spans})', + new_span = Fiber(uid = f'{fiber.uid}_({span+1}/{n_spans})', metadata = fiber.metadata, params = fiber_params) network.add_edge(prev_node, new_span) From 7937392dfc6db83320e6f14b8b90ae78254ea149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 13 Nov 2018 13:33:21 +0100 Subject: [PATCH 033/108] fix bug in gain mode --- gnpy/core/elements.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 9372a79c1..7e23cff9d 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -491,6 +491,8 @@ def interpol_params(self, frequencies, pin, baud_rates, pref): if self.dp_db is not None: self.target_pch_db = round(self.dp_db + pref.p0, 2) self.effective_gain = self.target_pch_db - pref.pi + else: + self.effective_gain = self.operational.gain_target self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db) self.effective_pch_db = round(pref.pi + self.effective_gain, 2) From 3df270e4acfddf91076a0123aa7f3bda6b1bb842 Mon Sep 17 00:00:00 2001 From: James Powell Date: Tue, 13 Nov 2018 10:24:15 -0500 Subject: [PATCH 034/108] fix changed spelling & misspellings in meshTopolgyExampleV2_Services.json for "synchronize/synchronization" --- examples/meshTopologyExampleV2_services.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 64cda69cf..11ccfc6f7 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -136,9 +136,9 @@ } } ], - "synchronisation": [ + "synchronization": [ { - "synchonization-id": 0, + "synchronization-id": 0, "svec": { "relaxable": "False", "link-diverse": "True", @@ -150,7 +150,7 @@ } }, { - "synchonization-id": 3, + "synchronization-id": 3, "svec": { "relaxable": "False", "link-diverse": "True", From dc867fa0516b868a078a32c8224e4cbcc401b7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 13 Nov 2018 22:29:11 +0100 Subject: [PATCH 035/108] improve power range handling --- examples/transmission_main_example.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index f225e884c..d700890c5 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -18,7 +18,7 @@ from json import loads from collections import Counter from logging import getLogger, basicConfig, INFO, ERROR, DEBUG -from numpy import arange, mean +from numpy import linspace, mean from matplotlib.pyplot import show, axis, figure, title from networkx import (draw_networkx_nodes, draw_networkx_edges, draw_networkx_labels, dijkstra_path) @@ -85,12 +85,9 @@ def main(network, equipment, source, destination, req = None): print(f'\nNow propagating between {source.uid} and {destination.uid}:') try: - power_range = list(arange(*equipment['SI']['default'].power_range_db)) - last = equipment['SI']['default'].power_range_db[-2] - if len(power_range) == 0 : #bad input that will lead to no simulation - power_range = [0] #better than an error message - else: - power_range.append(last) + 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] From 72d3525da158d14912adaa34a3d3670be55c6bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Wed, 14 Nov 2018 15:24:25 +0100 Subject: [PATCH 036/108] add a couple of missing imports --- examples/path_requests_run.py | 1 + gnpy/core/request.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index cd0d42f99..7891e7379 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -30,6 +30,7 @@ from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, propagate, jsontocsv, Disjunction, compute_path_dsjctn) from copy import copy, deepcopy +from textwrap import dedent #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 6bf756386..76a564605 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -15,6 +15,7 @@ See: draft-ietf-teas-yang-path-computation-01.txt """ +from sys import exit from collections import namedtuple from logging import getLogger, basicConfig, CRITICAL, DEBUG, INFO from networkx import (dijkstra_path, NetworkXNoPath, all_simple_paths) @@ -651,4 +652,4 @@ def remove_candidate(candidates, allpaths, rq, pth) : temp.remove(sol) break candidates[key] = temp - return candidates \ No newline at end of file + return candidates From 5381e0300f27bcadb49534be95b2a159981696ae Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 16 Nov 2018 11:04:52 +0000 Subject: [PATCH 037/108] correcting example file Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 24 -- examples/meshTopologyExampleV2.json | 288 +++++++++++++++---- examples/meshTopologyExampleV2.xls | Bin 14336 -> 14848 bytes examples/meshTopologyExampleV2_services.json | 92 ++++-- 4 files changed, 314 insertions(+), 90 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 0e64a6e12..c110ad0b6 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -144,30 +144,6 @@ }, { "type_variety": "Voyager", - "frequency":{ - "min": 191.35e12, - "max": 196.1e12 - }, - "mode":[ - { - "format": "16QAM", - "baud_rate": 32e9, - "OSNR": 19, - "bit_rate": 200e9, - "roll_off": 0.15 - }, - { - "format": "QPSK", - "baud_rate": 32e9, - "OSNR": 12, - "bit_rate": 100e9, - "roll_off": 0.15 - } - - ] - }, - { - "type_variety": "Voyager_16QAM", "frequency":{ "min": 191.35e12, "max": 196.1e12 diff --git a/examples/meshTopologyExampleV2.json b/examples/meshTopologyExampleV2.json index 7fd4df153..107b54ff1 100644 --- a/examples/meshTopologyExampleV2.json +++ b/examples/meshTopologyExampleV2.json @@ -193,7 +193,7 @@ "type": "Fused" }, { - "uid": "fiber (Lannion_CAS \u2192 Corlay)-F061", + "uid": "fiber (Lannion_CAS → Corlay)-F061", "metadata": { "location": { "latitude": 2.0, @@ -211,7 +211,7 @@ } }, { - "uid": "fiber (Corlay \u2192 Loudeac)-F010", + "uid": "fiber (Corlay → Loudeac)-F010", "metadata": { "location": { "latitude": 2.0, @@ -229,7 +229,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "uid": "fiber (Loudeac → Lorient_KMA)-F054", "metadata": { "location": { "latitude": 2.0, @@ -247,7 +247,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055", "metadata": { "location": { "latitude": 2.0, @@ -265,7 +265,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", + "uid": "fiber (Lannion_CAS → Stbrieuc)-F056", "metadata": { "location": { "latitude": 1.5, @@ -283,7 +283,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "uid": "fiber (Stbrieuc → Rennes_STA)-F057", "metadata": { "location": { "latitude": 0.5, @@ -301,7 +301,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059", + "uid": "fiber (Lannion_CAS → Morlaix)-F059", "metadata": { "location": { "latitude": 2.5, @@ -319,7 +319,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "uid": "fiber (Morlaix → Brest_KLA)-F060", "metadata": { "location": { "latitude": 3.5, @@ -337,7 +337,79 @@ } }, { - "uid": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "uid": "fiber (Brest_KLA → Quimper)-", + "metadata": { + "location": { + "latitude": 2.5, + "longitude": 0.5 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 75.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Quimper → Lorient_KMA)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 2.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 70.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Ploermel → Vannes_KBE)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 3.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 50.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Ploermel → Rennes_STA)-", + "metadata": { + "location": { + "latitude": 0.5, + "longitude": 1.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 55.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Corlay → Lannion_CAS)-F061", "metadata": { "location": { "latitude": 2.0, @@ -355,7 +427,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Corlay)-F010", + "uid": "fiber (Loudeac → Corlay)-F010", "metadata": { "location": { "latitude": 2.0, @@ -373,7 +445,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054", + "uid": "fiber (Lorient_KMA → Loudeac)-F054", "metadata": { "location": { "latitude": 2.0, @@ -391,7 +463,7 @@ } }, { - "uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055", "metadata": { "location": { "latitude": 2.0, @@ -409,7 +481,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "uid": "fiber (Stbrieuc → Lannion_CAS)-F056", "metadata": { "location": { "latitude": 1.5, @@ -427,7 +499,7 @@ } }, { - "uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", + "uid": "fiber (Rennes_STA → Stbrieuc)-F057", "metadata": { "location": { "latitude": 0.5, @@ -445,7 +517,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "uid": "fiber (Morlaix → Lannion_CAS)-F059", "metadata": { "location": { "latitude": 2.5, @@ -463,7 +535,7 @@ } }, { - "uid": "fiber (Brest_KLA \u2192 Morlaix)-F060", + "uid": "fiber (Brest_KLA → Morlaix)-F060", "metadata": { "location": { "latitude": 3.5, @@ -479,129 +551,249 @@ "con_in": null, "con_out": null } + }, + { + "uid": "fiber (Quimper → Brest_KLA)-", + "metadata": { + "location": { + "latitude": 2.5, + "longitude": 0.5 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 75.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Lorient_KMA → Quimper)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 2.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 70.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Vannes_KBE → Ploermel)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 3.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 50.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Rennes_STA → Ploermel)-", + "metadata": { + "location": { + "latitude": 0.5, + "longitude": 1.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 55.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } } ], "connections": [ { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061" + "to_node": "fiber (Lannion_CAS → Corlay)-F061" }, { - "from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "from_node": "fiber (Corlay → Lannion_CAS)-F061", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056" + "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" }, { - "from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059" + "to_node": "fiber (Lannion_CAS → Morlaix)-F059" }, { - "from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "from_node": "fiber (Morlaix → Lannion_CAS)-F059", "to_node": "roadm Lannion_CAS" }, { - "from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061", + "from_node": "fiber (Lannion_CAS → Corlay)-F061", "to_node": "ingress fused spans in Corlay" }, { "from_node": "ingress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Loudeac)-F010" + "to_node": "fiber (Corlay → Loudeac)-F010" }, { - "from_node": "fiber (Loudeac \u2192 Corlay)-F010", + "from_node": "fiber (Loudeac → Corlay)-F010", "to_node": "egress fused spans in Corlay" }, { "from_node": "egress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061" + "to_node": "fiber (Corlay → Lannion_CAS)-F061" }, { - "from_node": "fiber (Corlay \u2192 Loudeac)-F010", + "from_node": "fiber (Corlay → Loudeac)-F010", "to_node": "ingress fused spans in Loudeac" }, { "from_node": "ingress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054" + "to_node": "fiber (Loudeac → Lorient_KMA)-F054" }, { - "from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054", + "from_node": "fiber (Lorient_KMA → Loudeac)-F054", "to_node": "egress fused spans in Loudeac" }, { "from_node": "egress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Corlay)-F010" + "to_node": "fiber (Loudeac → Corlay)-F010" + }, + { + "from_node": "roadm Lorient_KMA", + "to_node": "fiber (Lorient_KMA → Loudeac)-F054" + }, + { + "from_node": "fiber (Loudeac → Lorient_KMA)-F054", + "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054" + "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" }, { - "from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055" + "to_node": "fiber (Lorient_KMA → Quimper)-" }, { - "from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "from_node": "fiber (Quimper → Lorient_KMA)-", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Vannes_KBE", - "to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055" + "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" + }, + { + "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", + "to_node": "roadm Vannes_KBE" + }, + { + "from_node": "roadm Vannes_KBE", + "to_node": "fiber (Vannes_KBE → Ploermel)-" }, { - "from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "from_node": "fiber (Ploermel → Vannes_KBE)-", "to_node": "roadm Vannes_KBE" }, { - "from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", - "to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057" + "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", + "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" }, { - "from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", - "to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056" + "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", + "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" }, { "from_node": "roadm Rennes_STA", - "to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057" + "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" }, { - "from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", "to_node": "roadm Rennes_STA" }, { - "from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059", + "from_node": "roadm Rennes_STA", + "to_node": "fiber (Rennes_STA → Ploermel)-" + }, + { + "from_node": "fiber (Ploermel → Rennes_STA)-", + "to_node": "roadm Rennes_STA" + }, + { + "from_node": "fiber (Lannion_CAS → Morlaix)-F059", "to_node": "ingress fused spans in Morlaix" }, { "from_node": "ingress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060" + "to_node": "fiber (Morlaix → Brest_KLA)-F060" }, { - "from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060", + "from_node": "fiber (Brest_KLA → Morlaix)-F060", "to_node": "egress fused spans in Morlaix" }, { "from_node": "egress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059" + "to_node": "fiber (Morlaix → Lannion_CAS)-F059" + }, + { + "from_node": "roadm Brest_KLA", + "to_node": "fiber (Brest_KLA → Morlaix)-F060" + }, + { + "from_node": "fiber (Morlaix → Brest_KLA)-F060", + "to_node": "roadm Brest_KLA" }, { "from_node": "roadm Brest_KLA", - "to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060" + "to_node": "fiber (Brest_KLA → Quimper)-" }, { - "from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "from_node": "fiber (Quimper → Brest_KLA)-", "to_node": "roadm Brest_KLA" }, + { + "from_node": "fiber (Brest_KLA → Quimper)-", + "to_node": "fiber (Quimper → Lorient_KMA)-" + }, + { + "from_node": "fiber (Lorient_KMA → Quimper)-", + "to_node": "fiber (Quimper → Brest_KLA)-" + }, + { + "from_node": "fiber (Vannes_KBE → Ploermel)-", + "to_node": "fiber (Ploermel → Rennes_STA)-" + }, + { + "from_node": "fiber (Rennes_STA → Ploermel)-", + "to_node": "fiber (Ploermel → Vannes_KBE)-" + }, { "from_node": "trx Lannion_CAS", "to_node": "roadm Lannion_CAS" diff --git a/examples/meshTopologyExampleV2.xls b/examples/meshTopologyExampleV2.xls index a47e87a5aabfe4fae4e8a983f1242f851849bba5..38eee6b509808431286ed1f3c17284fe25ebd74e 100644 GIT binary patch delta 3051 zcmZveU2GIp6vxke?aqGAwx#Ws-FBCLcehju{XhY=AS%!rQG*GBO-Wa2YPZMf>{KfT2Xi{YY!| zUSy9ldML(040@$BTUfe4F*?7&5hmw~@7cmCM}WuP6N}5!7s#T!ouUx?BA7RRgFYpU z`0vd@qJt;r78VO-ljzm?QlY$1n6Lg3|4OUGi56#;k7tj~6lYJ(%`P8@@1fG^nRg3i zgJ^i{;KbB^qH<|wb|JgFTv%G3J}^0vJ)3ou$(dqtu2h`fGx55omCAF4BK)R2zeDgV zEKMKSy|;nr{X%iJRGye#E`P8&y?lDHFdQISzzb&GhplTArm5j_A|7i-l2lO0B@Gk?-J(W}(?NJI`M)7Y1SkosGv2bTtuFavV~JzF;1>CV zTfX2tp%a{^LvZm+vV@Fj&g>i$PXTeecTN8PQ4abrvoiIm>P<*Ct5g=JEZ8<|Fp)7bpD1)V^exSVRvC-KA?C2XKY+zd>LP24z4 zytx*oL@kw4(&ApKOKBGk>8?tLwCnJ1p^Qr^wNut54YbK69n|TP0qSxo0P1!r2r*=Y)@N#GP;3R&rqM3Y9F`y zr53QYezC75C9c7DKY3#MU;8KcNjtEoy5+;zTl4;kG!hhH)zKMh&3|oF@5y%W$c)XWZFf>G`Av z;1Xd^u2749lC!dEQN2|X*m#SYxHs7~VY}WHXMcZ7` zK_f01pi!3spfQ(%s8uhD3G$+ZKqG0_H4OB^L_lM9Dc|pkTjSEpKng4xM=57St~>1N z0jYC2zuoQmbIRENO!t86Yf3HCk!jJ!i?(t>`OWRdBGO})DL|WhK z@#cz3P588!)PDA9aj9v&-dIa&J9iEWz}rM8oUc_HRiE>p~XGs za!V&X=5o6k_1M61-52(lFTAjiIhPlG7beQDLTO(;qyFgR2Csg@NUpy=BNU;EFF=&b z(|3bvco9Cv3AIi3@vmXZ$YWOm-?LGAhn*?5qV(I2_qh8}@OH z@7cP>H;tkOkeeXx4yQ#-JZ*=MLm|-9E`>qQNaCgR&<>Xr>Z#C97gZQ{xuk)fbxFrn z@SICXJU#DH05suJ5VYGR6ST*r5aq}BIu;V#;8FUNRyCJ6Y zsU`!5$bfzJ8Ax@GK0}2y=U2n$F2P`TF|x}5Vd@!iGQ7#b`;*Q z96i9%`B#3lQuj4&-${Gs4tN^y;X^)W|0>GSBUlFP-$tgr8zxH0S(RzWJB%Xd^O<+f z@pC#?L4+n~^*+woP2!M713Rg{fRJwiNrZXQ0)lc1zN=ot3ls(qck_$cTf=|(!xwQk)$5NPW;>{bwYg{Ra|_pe+CZ delta 2493 zcmZvd+ix316vof&#k=dvZW70F9LHYg>cnwEZjM{h(k7umONv^hAcQJemWUvNCMAs@ zPyh#^yzu}!4+T|JLj4O;BOx9@MB?^_kje`R65t^{V`rZhv(K_UcRyoF<6f`g~gTC*F52_*F%R>Z(QD}Y=Z;8<#0Lnv2joPhBD8f zZvWc2YhPFAsnQE;Z&qJfUaw5doS!?Z19;C#fcf?1D;t$(7w2+sOJ1S*MQCSQl`{ zO4mPgHCvYAQwdIurw6AJdZ-oEfLP;NI>t&5=*#iTfe!lLB!wvH6YuQ+|4n&}e@6#W zIGRbbE8p49ag8R@0U1FGea;F}iCjS%QBIIf)FUWF)GNp!>JwxV^$W77 ze)fXEFrjP_AB)2GALF@rP`uGwACf zPaniP*|L-OHN(^LSf|_zZl^Qk>!zm<;ig-5hJDTQv|-#Ot>9~6Pb;92D?1~;7V)$Z zTh-g}n zLEe&8Od?suBAS#|4-?8V5u$1CnG9cNK6TyOEG0qQ6D4xbuyde=Xo0wPC+nv82;$qD?7{i_mO{^75&Ou(NnTKB#r`S_?yI4*>?N>0o$TNNg!>_;>)x_n|((FN= z>E)gF@;d3@)2Z(m8KVF;X$4T|vjJa`sra!tOt*#4kG+JmU2(pDY?_qZKQ;|g6xu&7 z{x$aZ&GCzBD|g>fI@3 Date: Fri, 16 Nov 2018 11:14:37 +0000 Subject: [PATCH 038/108] change for meshTopologyToy.xls Signed-off-by: EstherLerouzic --- examples/meshTopologyToy.xls | Bin 13824 -> 13824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/meshTopologyToy.xls b/examples/meshTopologyToy.xls index 05ae7d6ac67c4fcc96ef39bc4669590432e9ee47..d483e6fa87ea3fdb9c358f99973d8aeaabd571e7 100644 GIT binary patch delta 492 zcmZq3X~@}dg^iJU^HsJTJb^ia{0vMC3=FjlJPeFLc$pW$X9CJx=0@E)!oqEAz~BIs gXjpdP{^T5e1&#|m;l*=nuP{z-WYO7tK;MB40JJbn$p8QV delta 490 zcmY*Vu};G<6g)eL<0ffK0U?+Y5E)t_MP*~F7?BwGgN}frFflLyWnw@Kl{dbkQWiG& zh**dL#EQf(MB=%NUBAJ)cY1gC-mILJi$*Y3Yw?<#Rf9e_fLkPBRJv<1Cq~B>dk9u| z0f8#9dFEwSBzb{j<& zn-j$jHfiQ+k*9;3O`DlK$?_+E^f+hgVt8pjJ-k-g{#Gxdz{De>l2XmHw0<5netdX@ ciCL;R{#0d=$@Qz>;O?Yh>cKy#-;0U(2ftNG0ssI2 From 1480d23088e3b8ee8b29cbaf077a0cc9297f6c03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Fri, 16 Nov 2018 13:38:07 +0100 Subject: [PATCH 039/108] fix utf-8 encoding --- examples/path_requests_run.py | 4 ++-- gnpy/core/service_sheet.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 7891e7379..d7aa9cc4a 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -293,8 +293,8 @@ def path_result_json(pathresult): result = [] for p in pths: result.append(Result_element(rqs[pths.index(p)],p)) - with open(args.output, 'w') as f: + with open(args.output, 'w', encoding='utf-8') as f: f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) fnamecsv = next(s for s in args.output.split('.')) + '.csv' - with open(fnamecsv,"w") as fcsv : + with open(fnamecsv,"w", encoding='utf-8') as fcsv : jsontocsv(path_result_json(result),equipment,fcsv) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index d0822e606..2ea9aa427 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -188,7 +188,7 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil 'synchronization': [n.json[1] for n in req if n.json[1] is not None] } - with open(output_filename, 'w') as f: + with open(output_filename, 'w', encoding='utf-8') as f: f.write(dumps(data, indent=2, ensure_ascii=False)) return data From adbe283c83be99ad1593240c76e1a4286d7d66a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Mon, 19 Nov 2018 13:03:32 +0100 Subject: [PATCH 040/108] fix broken links in readme --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 1c8121e14..ee110c9e7 100644 --- a/README.rst +++ b/README.rst @@ -181,7 +181,7 @@ can be added and existing ones removed. Three different noise models are availab 1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation. 2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax` -3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. +3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/std_medium_gain_advanced_config.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. For all amplifier models: @@ -203,7 +203,7 @@ For all amplifier models: | | | Excel template topology files.) | +----------------------+-----------+-----------------------------------------+ -The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model: +The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model: +----------------------+-----------+-----------------------------------------+ | field | type | description | @@ -490,8 +490,8 @@ library. The program computes performances for the list of services (accepts json or excel format) using the same spectrum propagation modules as transmission_main_example.py. Explanation on the Excel template is provided in the `Excel_userguide.rst `_. Template for -the json format can be found here: `service_template.json -`_. +the json format can be found here: `service-template.json +`_. Contributing ------------ From fa3e54a747d186795e4d72f0e1b2ba98f2da1eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 20 Nov 2018 14:57:28 +0100 Subject: [PATCH 041/108] change handling of verbosity argument --- examples/path_requests_run.py | 6 +++--- examples/transmission_main_example.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index d7aa9cc4a..b39c328d1 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -40,8 +40,8 @@ parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') -parser.add_argument('-v', '--verbose', action='count') -parser.add_argument('-o', '--output', default=None) +parser.add_argument('-v', '--verbose', action='count', default=0) +parser.add_argument('-o', '--output') def requests_from_json(json_data,equipment): @@ -243,7 +243,7 @@ def path_result_json(pathresult): if __name__ == '__main__': args = parser.parse_args() - basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, CRITICAL)) + basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG)) logger.info(f'Computing path requests {args.service_filename} into JSON format') # for debug # print( args.eqpt_filename) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index f225e884c..a24a199ed 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -115,9 +115,9 @@ def main(network, equipment, source, destination, req = None): parser = ArgumentParser() parser.add_argument('-e', '--equipment', type=Path, default=Path(__file__).parent / 'eqpt_config.json') -parser.add_argument('-pl', '--plot', action='store_true', default=False) -parser.add_argument('-v', '--verbose', action='count') -parser.add_argument('-l', '--list-nodes', action='store_true', default=False, help='list all transceiver nodes') +parser.add_argument('-pl', '--plot', action='store_true') +parser.add_argument('-v', '--verbose', action='count', default=0) +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('-plb', '--power-lower-bound', default=0, help='power sweep lower bound') #parser.add_argument('-pub', '--power-upper-bound', default=1, help='power sweep upper bound') @@ -129,7 +129,7 @@ def main(network, equipment, source, destination, req = None): if __name__ == '__main__': args = parser.parse_args() - basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, ERROR)) + basicConfig(level={0: ERROR, 1: INFO, 2: DEBUG}.get(args.verbose, DEBUG)) equipment = load_equipment(args.equipment) # logger.info(equipment) From 5efbd17829c1af6f147b46e61cfced5df9bfafb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 20 Nov 2018 15:27:54 +0100 Subject: [PATCH 042/108] add help text --- examples/path_requests_run.py | 2 +- examples/transmission_main_example.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index b39c328d1..8954fbb6a 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -40,7 +40,7 @@ parser.add_argument('network_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') -parser.add_argument('-v', '--verbose', action='count', default=0) +parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') parser.add_argument('-o', '--output') diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index a24a199ed..42687de24 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -116,7 +116,7 @@ def main(network, equipment, source, destination, req = None): parser.add_argument('-e', '--equipment', type=Path, default=Path(__file__).parent / 'eqpt_config.json') parser.add_argument('-pl', '--plot', action='store_true') -parser.add_argument('-v', '--verbose', action='count', default=0) +parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') 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('-plb', '--power-lower-bound', default=0, help='power sweep lower bound') From 39a8fa333595ffeabf9d6be0b322ed99f0cb82a0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 26 Oct 2018 17:00:39 +0100 Subject: [PATCH 043/108] Adding aggregation feature - add bitrate in the json service file and enable to support both with and without bitrate - change mode to optional - add an aggregation function that groups identical demands (except for id and bitrate) TODO : check which mode can satisfy the request or how many transponders -> going towards dimensionning - Correcting some bug on loose parameter: hop-type attribute was forced to be always 'loose' -> now reads the excel entry - service sheets checks on header if field names are corrects, but any order is now feasible. Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 2 +- examples/meshTopologyExampleV2_services.json | 12 +++-- examples/path_requests_run.py | 9 +++- gnpy/core/request.py | 44 ++++++++++++++- gnpy/core/service_sheet.py | 57 +++++++++++++------- 5 files changed, 97 insertions(+), 27 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index c110ad0b6..70fb94650 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -134,7 +134,7 @@ "roll_off": 0.15 }, { - "format": "mode_2", + "format": "PS_SP64_2", "baud_rate": 66e9, "OSNR": 15, "bit_rate": 200e9, diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 231ceb473..332b6744d 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -19,7 +19,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 100000000000.0 } }, "optimizations": { @@ -132,7 +133,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 60000000000.0 } }, "optimizations": { @@ -158,7 +160,8 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 63, - "output-power": 0.0019952623149688794 + "output-power": 0.0019952623149688794, + "path_bandwidth": 150000000000.0 } }, "optimizations": { @@ -184,7 +187,8 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 63, - "output-power": 0.0019952623149688794 + "output-power": 0.0019952623149688794, + "path_bandwidth": 20000000000.0 } }, "optimizations": { diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 8954fbb6a..672554993 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -28,7 +28,7 @@ from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, - propagate, jsontocsv, Disjunction, compute_path_dsjctn) + propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation) from copy import copy, deepcopy from textwrap import dedent @@ -92,6 +92,11 @@ def requests_from_json(json_data,equipment): Computation stopped.''') logger.critical(msg) raise ValueError(msg) + + try : + params['bit_rate'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] + except KeyError: + pass requests_list.append(Path_request(**params)) return requests_list @@ -262,7 +267,9 @@ def path_result_json(pathresult): save_network(args.network_filename, network) rqs = requests_from_json(data, equipment) + print(rqs) rqs = correct_route_list(network, rqs) + rqs = requests_aggregation(rqs) print('The following services have been requested:') print(rqs) # pths = compute_path(network, equipment, rqs) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 76a564605..80f829270 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -70,7 +70,9 @@ def __repr__(self): f'bit_rate:\t{self.bit_rate * 1e-9} Gb/s', f'spacing:\t{self.spacing * 1e-9} GHz', f'power: \t{round(lin2db(self.power)+30,2)} dBm', - f'nb channels: \t{self.nb_channel}' + f'nb channels: \t{self.nb_channel}', + f'nodes-list:\t{self.nodes_list}', + f'loose-list:\t{self.loose_list}' '\n']) class Disjunction: def __init__(self, *args, **params): @@ -630,6 +632,8 @@ def find_reversed_path(p,network) : return total_path def ispart(a,b) : + # the functions takes two paths a and b and retrns True + # if all a elements are part of b and in the same order j = 0 for i, el in enumerate(a): if el in b : @@ -653,3 +657,41 @@ def remove_candidate(candidates, allpaths, rq, pth) : break candidates[key] = temp return candidates + +def compare_reqs(req1,req2) : + if req1.source == req2.source and \ + req1.destination == req2.destination and \ + req1.tsp == req2.tsp and \ + req1.tsp_mode == req2.tsp_mode and \ + req1.baud_rate == req2.baud_rate and \ + req1.nodes_list == req2.nodes_list and \ + req1.loose_list == req2.loose_list and \ + req1.spacing == req2.spacing and \ + req1.power == req2.power and \ + req1.nb_channel == req2.nb_channel and \ + req1.frequency == req2.frequency and \ + req1.format == req2.format and \ + req1.OSNR == req2.OSNR and \ + req1.roll_off == req2.roll_off : + print(f'coucou {req1.request_id} {req2.request_id}') + print(f'{req1.nodes_list} == {req2.nodes_list}') + return True + else: + return False + +def requests_aggregation(pathreqlist) : + # this function aggregates requests so that if several requests + # exist between same source and destination and with same transponder type + # todo maybe add conditions on mode ??, spacing ... + # currently if undefined takes the default values + local_list = pathreqlist.copy() + for req in local_list: + for r in local_list : + if req.request_id != r.request_id and compare_reqs(req, r): + # aggregate + req.bit_rate += r.bit_rate + req.request_id = ' | '.join((req.request_id,r.request_id)) + # remove request from list + local_list.remove(r) + return local_list + diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 2ea9aa427..c55e535d6 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -23,7 +23,7 @@ from gnpy.core.equipment import load_equipment from gnpy.core.utils import db2lin, lin2db -SERVICES_COLUMN = 11 +SERVICES_COLUMN = 12 #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' all_rows = lambda sheet, start=0: (sheet.row(x) for x in range(start, sheet.nrows)) @@ -31,9 +31,9 @@ # Type for input data class Request(namedtuple('Request', 'request_id source destination trx_type mode \ - spacing power nb_channel disjoint_from nodes_list is_loose')): - def __new__(cls, request_id, source, destination, trx_type, mode , spacing , power = None, nb_channel = None , disjoint_from ='' , nodes_list = None, is_loose = ''): - return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose) + spacing power nb_channel disjoint_from nodes_list is_loose path_bandwidth')): + def __new__(cls, request_id, source, destination, trx_type, mode=None , spacing= None , power = None, nb_channel = None , disjoint_from ='' , nodes_list = None, is_loose = '', path_bandwidth = None): + return super().__new__(cls, request_id, source, destination, trx_type, mode, spacing, power, nb_channel, disjoint_from, nodes_list, is_loose, path_bandwidth) # Type for output data: // from dutc class Element: @@ -64,8 +64,10 @@ def __init__(self,Request,eqpt_filename): try : if equipment['Transceiver'][Request.trx_type]: self.trx_type = Request.trx_type - if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: - self.mode = Request.mode + if Request.mode is not None : + if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: + self.mode = Request.mode + except KeyError: msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' #print(msg) @@ -115,11 +117,16 @@ def __init__(self,Request,eqpt_filename): if Request.is_loose == 'no' : print(Request.is_loose) self.loose = 'strict' + else : + print(f'esther{Request.is_loose}') + self.path_bandwidth = None + if Request.path_bandwidth is not None: + self.path_bandwidth = Request.path_bandwidth * 1e9 uid = property(lambda self: repr(self)) @property def pathrequest(self): - return { + req_dictionnary = { 'request-id':self.request_id, 'source': self.source, 'destination': self.destination, @@ -134,6 +141,7 @@ def pathrequest(self): 'spacing' : self.spacing, 'max-nb-of-channel' : self.nb_channel, 'output-power' : self.power + # 'path_bandwidth' : self.path_bandwidth } }, 'optimizations': { @@ -143,7 +151,7 @@ def pathrequest(self): 'unnumbered-hop':{ 'node-id': f'{node}', 'link-tp-id': 'link-tp-id is not used', - 'hop-type': 'loose', + 'hop-type': f'{self.loose}', 'direction': 'direction is not used' }, 'label-hop':{ @@ -158,6 +166,10 @@ def pathrequest(self): } } + if self.path_bandwidth is not None: + req_dictionnary['path-constraints']['te-bandwidth']['path_bandwidth'] = self.path_bandwidth + + return req_dictionnary @property def pathsync(self): if self.disjoint_from : @@ -206,19 +218,24 @@ def parse_excel(input_filename): def parse_service_sheet(service_sheet): logger.info(f'Validating headers on {service_sheet.name!r}') - header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN]] - expected = ['route id', 'Source', 'Destination', 'TRX type', \ - 'Mode', 'System: spacing', 'System: input power (dBm)', 'System: nb of channels',\ - 'routing: disjoint from', 'routing: path', 'routing: is loose?'] - if header != expected: - msg = f'Malformed header on Service sheet: {header} != {expected}' + # add a test on field to enable the '' field case that arises when columns on the + # right hand side are used as comments or drawing in the excel sheet + header = [x.value.strip() for x in service_sheet.row(4)[0:SERVICES_COLUMN] if len(x.value.strip())>0] + + # create a service_fieldname independant from the excel column order + # to be compatible with any version of the sheet + # the following dictionnary records the excel field names and the corresponding parameter's name + + authorized_fieldnames = {'route id':'request_id', 'Source':'source', 'Destination':'destination', \ + 'TRX type':'trx_type', 'Mode' : 'mode', 'System: spacing':'spacing', \ + 'System: input power (dBm)':'power', 'System: nb of channels':'nb_channel',\ + 'routing: disjoint from': 'disjoint_from', 'routing: path':'nodes_list',\ + 'routing: is loose?':'is_loose', 'path bandwidth':'path_bandwidth'} + try : + service_fieldnames = [authorized_fieldnames[e] for e in header] + except KeyError: + msg = f'Malformed header on Service sheet: {header} field not in {authorized_fieldnames}' logger.critical(msg) raise ValueError(msg) - - service_fieldnames = 'request_id source destination trx_type mode spacing power nb_channel disjoint_from nodes_list is_loose'.split() - # Important Note: it reads all colum on each row so that - # it is not possible to write annotation in the excel sheet - # outside the SERVICES_COLUMN ... TO BE IMPROVED - # request_id should be unique for disjunction constraints (not used yet) for row in all_rows(service_sheet, start=5): yield Request(**parse_row(row[0:SERVICES_COLUMN], service_fieldnames)) From 1bbcee8715b4e3a2fc7a1cdb85ea28aee63c12cd Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 31 Oct 2018 16:05:22 +0000 Subject: [PATCH 044/108] First version of mode optimization if mode is not given, the program automatically choses the first mode of the tsp that has the highest bitrate and baudrate - propagation for each loop on baudrate, no proppag in the loop on the mode (same propoagation applies for identical baudrate) - starts with the highest baudrate - Make power and nb-channel optional fields - power uses defaulf power in SI - nb channel computed based on min max frequency and spacing Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 31 +++++++++++++++++-- examples/path_requests_run.py | 39 +++++++++++++++-------- gnpy/core/equipment.py | 35 ++++++++++++++------- gnpy/core/request.py | 58 +++++++++++++++++++++++++++++------ gnpy/core/service_sheet.py | 18 +++++------ 5 files changed, 137 insertions(+), 44 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 70fb94650..8ad726408 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -157,13 +157,40 @@ "roll_off": 0.15 }, { - "format": "QPSK", + "format": "mode 1", "baud_rate": 32e9, "OSNR": 12, "bit_rate": 100e9, "roll_off": 0.15 + }, + { + "format": "mode 3", + "baud_rate": 44e9, + "OSNR": 20, + "bit_rate": 300e9, + "roll_off": 0.15 + }, + { + "format": "QPSK", + "baud_rate": 44e9, + "OSNR": 14, + "bit_rate": 150e9, + "roll_off": 0.15 + }, + { + "format": "mode 2", + "baud_rate": 66e9, + "OSNR": 21, + "bit_rate": 400e9, + "roll_off": 0.15 + }, + { + "format": "mode 4", + "baud_rate": 66e9, + "OSNR": 16, + "bit_rate": 200e9, + "roll_off": 0.15 } - ] } ] diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 672554993..df2e4818f 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -28,7 +28,8 @@ from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, - propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation) + propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, + propagate_and_optimize_mode) from copy import copy, deepcopy from textwrap import dedent @@ -71,11 +72,18 @@ def requests_from_json(json_data,equipment): if req['path-constraints']['te-bandwidth']['output-power']: params['power'] = req['path-constraints']['te-bandwidth']['output-power'] # same process for nb-channel - if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] : + fmin = trx_params['frequency']['min'] + fmax = trx_params['frequency']['max'] + if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : # check if requested nb_channels is consistant with baudrate and min-max frequencies - min_recommanded_spacing = automatic_spacing(trx_params['baud_rate']) - fmin = trx_params['frequency']['min'] - fmax = trx_params['frequency']['max'] + if trx_params['baud_rate'] is not None: + min_recommanded_spacing = automatic_spacing(trx_params['baud_rate']) + # needed for printing - else argument with quote are making errors in the print + temp = params['baud_rate']*1e-9 + else: + min_recommanded_spacing = params['spacing'] + temp = 'undetermined baudrate' + max_recommanded_nb_channels = automatic_nch(fmin,fmax, min_recommanded_spacing) @@ -92,9 +100,10 @@ def requests_from_json(json_data,equipment): Computation stopped.''') logger.critical(msg) raise ValueError(msg) - + else : + params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) try : - params['bit_rate'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] + params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] except KeyError: pass requests_list.append(Path_request(**params)) @@ -194,7 +203,14 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): # for debug # print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}') if total_path : - total_path = propagate(total_path,pathreq,equipment, show=False) + if pathreq.baud_rate is not None: + total_path = propagate(total_path,pathreq,equipment, show=False) + else: + total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) + pathreq.baud_rate = mode['baud_rate'] + pathreq.tsp_mode = mode['format'] + pathreq.format = mode['format'] + pathreq.OSNR = mode['OSNR'] else: total_path = [] # we record the last tranceiver object in order to have th whole @@ -215,7 +231,7 @@ def correct_route_list(network, pathreqlist): for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): # replace possibly wrong name with a formated roadm name - print(n_id) + # print(n_id) if n_id not in anytype : nodes_suggestion = [uid for uid in anytype \ if n_id.lower() in uid.lower()] @@ -267,7 +283,6 @@ def path_result_json(pathresult): save_network(args.network_filename, network) rqs = requests_from_json(data, equipment) - print(rqs) rqs = correct_route_list(network, rqs) rqs = requests_aggregation(rqs) print('The following services have been requested:') @@ -278,14 +293,14 @@ def path_result_json(pathresult): propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR'] + header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode'] data = [] data.append(header) for i, p in enumerate(propagatedpths): if p: line = [f'{rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ - f'{rqs[i].OSNR}'] + f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}'] else: line = [f'no path from {rqs[i].source} to {rqs[i].destination} '] data.append(line) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 157ee8ad8..4f6528621 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -148,18 +148,27 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F """return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)""" trx_params = {} default_si_data = equipment['SI']['default'] + print(trx_mode) try: trxs = equipment['Transceiver'] - mode_params = next(mode for trx in trxs \ - if trx == trx_type_variety \ - for mode in trxs[trx].mode \ - if mode['format'] == trx_mode) + if trx_mode is not None: + mode_params = next(mode for trx in trxs \ + if trx == trx_type_variety \ + for mode in trxs[trx].mode \ + if mode['format'] == trx_mode) + else: + mode_params = {"format": "undetermined", + "baud_rate": None, + "OSNR": None, + "bit_rate": None, + "roll_off": None} trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency + # TODO: novel automatic feature maybe unwanted if spacing is specified - trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) - temp = trx_params['spacing'] - print(f'spacing {temp}') + # trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) + # temp = trx_params['spacing'] + # print(f'spacing {temp}') except StopIteration : if error_message: print(f'could not find tsp : {trx_type_variety} with mode: {trx_mode} in eqpt library') @@ -167,17 +176,21 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F exit() else: # default transponder charcteristics + # mainly used with transmission_main_example.py trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max} trx_params['baud_rate'] = default_si_data.baud_rate trx_params['spacing'] = default_si_data.spacing trx_params['OSNR'] = default_si_data.OSNR trx_params['bit_rate'] = default_si_data.bit_rate trx_params['roll_off'] = default_si_data.roll_off + trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], + trx_params['frequency']['max'], + trx_params['spacing']) trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3 - trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], - trx_params['frequency']['max'], - trx_params['spacing']) - print('N channels = ', trx_params['nb_channel']) + # trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], + # trx_params['frequency']['max'], + # trx_params['spacing']) + # print('N channels = ', trx_params['nb_channel']) return trx_params def automatic_spacing(baud_rate): diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 80f829270..ff72ede33 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -61,13 +61,20 @@ def __str__(self): f'source: {self.source}', f'destination: {self.destination}']) def __repr__(self): + if self.baud_rate is not None: + temp = self.baud_rate * 1e-9 + temp2 = self.bit_rate * 1e-9 + else: + temp = self.baud_rate + temp2 = self.bit_rate + return '\n\t'.join([ f'{type(self).__name__} {self.request_id}', f'source: \t{self.source}', f'destination:\t{self.destination}', f'trx type:\t{self.tsp}', f'trx mode:\t{self.tsp_mode}', - f'baud_rate:\t{self.baud_rate * 1e-9} Gbaud', - f'bit_rate:\t{self.bit_rate * 1e-9} Gb/s', + f'baud_rate:\t{temp} Gbaud', + f'bit_rate:\t{temp2} Gb/s', f'spacing:\t{self.spacing * 1e-9} GHz', f'power: \t{round(lin2db(self.power)+30,2)} dBm', f'nb channels: \t{self.nb_channel}', @@ -290,6 +297,38 @@ def propagate(path, req, equipment, show=False): print(el) return path +def propagate_and_optimize_mode(path, req, equipment, show=False): + #update roadm loss in case of power sweep (power mode only) + set_roadm_loss(path, equipment, lin2db(req.power*1e3)) + # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the + # spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1 + # 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 m['baud_rate']< req.spacing+1.1])) + baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) + for b in baudrate_to_explore : + + modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode + if m['baud_rate'] == b] + modes_to_explore = sorted(modes_to_explore, + key = lambda x: x['bit_rate'], reverse=True) + # 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 + si = create_input_spectral_information( + req.frequency['min'], equipment['SI']['default'].roll_off, + b, req.power, req.spacing, req.nb_channel) + for el in path: + si = el(si) + if show : + print(el) + for m in modes_to_explore : + if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : + found_a_feasible_mode = True + return path, m + # if no feasible path were found + return found_a_feasible_mode + def jsontocsv(json_data,equipment,fileout): # read json path result file in accordance with: @@ -299,8 +338,9 @@ def jsontocsv(json_data,equipment,fileout): mywriter = writer(fileout) mywriter.writerow(('path-id','source','destination','transponder-type',\ - 'transponder-mode','baud rate (Gbaud)', 'input power (dBm)','path',\ - 'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?')) + 'transponder-mode',\ + 'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?',\ + 'baud rate (Gbaud)', 'input power (dBm)','path')) tspjsondata = equipment['Transceiver'] #print(tspjsondata) for p in json_data['path']: @@ -338,14 +378,14 @@ def jsontocsv(json_data,equipment,fileout): destination, tsp, mode, - baud_rate*1e-9, - round(lin2db(power)+30,2), - pth, output_osnrbandwidth, output_osnr, output_snrbandwidth, output_snr, - isok + isok, + baud_rate*1e-9, + round(lin2db(power)+30,2), + pth )) @@ -673,8 +713,6 @@ def compare_reqs(req1,req2) : req1.format == req2.format and \ req1.OSNR == req2.OSNR and \ req1.roll_off == req2.roll_off : - print(f'coucou {req1.request_id} {req2.request_id}') - print(f'{req1.nodes_list} == {req2.nodes_list}') return True else: return False diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index c55e535d6..752b7e9b0 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -67,7 +67,8 @@ def __init__(self,Request,eqpt_filename): if Request.mode is not None : if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: self.mode = Request.mode - + else: + self.mode = Request.mode except KeyError: msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' #print(msg) @@ -93,32 +94,31 @@ def __init__(self,Request,eqpt_filename): self.nodes_list = [] if Request.nodes_list : self.nodes_list = Request.nodes_list.split(' | ') + + # cleaning the list of nodes to remove source and destination + # (because the remaining of the program assumes that the nodes list are nodes + # on the path and should not include source and destination) try : self.nodes_list.remove(self.source) msg = f'{self.source} removed from explicit path node-list' logger.info(msg) - # print(msg) except ValueError: msg = f'{self.source} already removed from explicit path node-list' logger.info(msg) - # print(msg) + try : self.nodes_list.remove(self.destination) msg = f'{self.destination} removed from explicit path node-list' logger.info(msg) - # print(msg) except ValueError: msg = f'{self.destination} already removed from explicit path node-list' logger.info(msg) - # print(msg) + # the excel parser applies the same hop-type to all nodes in the route nodes_list. + # user can change this per node in the generated json self.loose = 'loose' - print(Request.is_loose) if Request.is_loose == 'no' : - print(Request.is_loose) self.loose = 'strict' - else : - print(f'esther{Request.is_loose}') self.path_bandwidth = None if Request.path_bandwidth is not None: self.path_bandwidth = Request.path_bandwidth * 1e9 From 88c2e2bd7086a93d2606c5c9333b880d207e4cdc Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 31 Oct 2018 17:41:15 +0000 Subject: [PATCH 045/108] adding the example file used for disjunction testing adding the counting feature on standard output adding tsp nb in csv + small bug fixes small fixes to improve stdout and csv printing small bug fixes on service aggregation and automatic correction of names Signed-off-by: EstherLerouzic --- examples/meshTopologyToy2.xls | Bin 0 -> 12800 bytes examples/path_requests_run.py | 23 ++++++---- examples/transmission_main_example.py | 1 + gnpy/core/equipment.py | 4 +- gnpy/core/request.py | 58 ++++++++++++++++++-------- gnpy/core/service_sheet.py | 2 + 6 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 examples/meshTopologyToy2.xls diff --git a/examples/meshTopologyToy2.xls b/examples/meshTopologyToy2.xls new file mode 100644 index 0000000000000000000000000000000000000000..428e2ee19470b29b80ec1c5727681c212c0ccf36 GIT binary patch literal 12800 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 literal 0 HcmV?d00001 diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index df2e4818f..133219f3a 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -32,6 +32,7 @@ propagate_and_optimize_mode) from copy import copy, deepcopy from textwrap import dedent +from math import ceil #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -211,6 +212,7 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): pathreq.tsp_mode = mode['format'] pathreq.format = mode['format'] pathreq.OSNR = mode['OSNR'] + pathreq.bit_rate = mode['bit_rate'] else: total_path = [] # we record the last tranceiver object in order to have th whole @@ -227,7 +229,7 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints - anytype = [n.uid for n in network.nodes()] + anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): # replace possibly wrong name with a formated roadm name @@ -293,30 +295,35 @@ def path_result_json(pathresult): propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode'] + header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode', 'Gbit/s' , 'nb of tsp'] data = [] data.append(header) for i, p in enumerate(propagatedpths): if p: line = [f'{rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ - f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}'] + f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] else: line = [f'no path from {rqs[i].source} to {rqs[i].destination} '] data.append(line) - col_width = max(len(word) for row in data for word in row) # padding + col_width = max(len(word) for row in data for word in row[1:]) # padding + firstcol_width = max(len(row[0]) for row in data ) # padding for row in data: - print(''.join(word.ljust(col_width) for word in row)) + firstcol = ''.join(row[0].ljust(firstcol_width)) + remainingcols = ''.join(word.ljust(col_width) for word in row[1:]) + print(f'{firstcol} {remainingcols}') if args.output : result = [] - for p in pths: - result.append(Result_element(rqs[pths.index(p)],p)) + for p in propagatedpths: + result.append(Result_element(rqs[propagatedpths.index(p)],p)) + temp = path_result_json(result) with open(args.output, 'w', encoding='utf-8') as f: f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) fnamecsv = next(s for s in args.output.split('.')) + '.csv' with open(fnamecsv,"w", encoding='utf-8') as fcsv : - jsontocsv(path_result_json(result),equipment,fcsv) + jsontocsv(temp,equipment,fcsv) + diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index 42687de24..d50669824 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -189,6 +189,7 @@ def main(network, equipment, source, destination, req = None): params['nodes_list'] = [destination.uid] params['loose_list'] = ['strict'] params['format'] = '' + params['path_bandwidth'] = 0 trx_params = trx_mode_params(equipment) if args.power: trx_params['power'] = db2lin(float(args.power))*1e-3 diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 4f6528621..6cb2d1b3e 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -148,7 +148,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F """return the trx and SI parameters from eqpt_config for a given type_variety and mode (ie format)""" trx_params = {} default_si_data = equipment['SI']['default'] - print(trx_mode) + try: trxs = equipment['Transceiver'] if trx_mode is not None: @@ -164,7 +164,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "roll_off": None} trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency - + # TODO: novel automatic feature maybe unwanted if spacing is specified # trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) # temp = trx_params['spacing'] diff --git a/gnpy/core/request.py b/gnpy/core/request.py index ff72ede33..00b6fe17e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -28,12 +28,13 @@ from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power from copy import copy, deepcopy from csv import writer +from math import ceil logger = getLogger(__name__) RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off') +' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -55,6 +56,7 @@ def __init__(self, *args, **params): self.OSNR = params.OSNR self.bit_rate = params.bit_rate self.roll_off = params.roll_off + self.path_bandwidth = params.path_bandwidth def __str__(self): return '\n\t'.join([ f'{type(self).__name__} {self.request_id}', @@ -142,6 +144,10 @@ def pathresult(self): { 'metric-type': 'reference_power', 'accumulative-value': self.path_request.power + }, + { + 'metric-type': 'path_bandwidth', + 'accumulative-value': self.path_request.path_bandwidth } ], 'path-srlgs': { @@ -210,6 +216,10 @@ def pathresult(self): { 'metric-type': 'reference_power', 'accumulative-value': self.path_request.power + }, + { + 'metric-type': 'path_bandwidth', + 'accumulative-value': self.path_request.path_bandwidth } ], 'path-srlgs': { @@ -247,7 +257,9 @@ def compute_constrained_path(network, req): roadm = [n for n in network.nodes() if isinstance(n, Roadm)] edfa = [n for n in network.nodes() if isinstance(n, Edfa)] anytypenode = [n for n in network.nodes()] + # print(req.source) source = next(el for el in trx if el.uid == req.source) + # start the path with its source # TODO : avoid loops due to constraints , guess name based on string, # avoid crashing if on req is not correct @@ -337,10 +349,10 @@ def jsontocsv(json_data,equipment,fileout): # and write results in an CSV file mywriter = writer(fileout) - mywriter.writerow(('path-id','source','destination','transponder-type',\ - 'transponder-mode',\ - 'OSNR@bandwidth','OSNR@0.1nm','SNR@bandwidth','SNR@0.1nm','Pass?',\ - 'baud rate (Gbaud)', 'input power (dBm)','path')) + mywriter.writerow(('path-id','source','destination','path_bandwidth','Pass?',\ + 'nb of tsp','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 p in json_data['path']: @@ -349,15 +361,17 @@ def jsontocsv(json_data,equipment,fileout): ['path-route-object']['unnumbered-hop']['node-id'] destination = p['path-properties']['path-route-objects'][-1]\ ['path-route-object']['unnumbered-hop']['node-id'] + # selects only roadm nodes pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] - for e in p['path-properties']['path-route-objects']]) + for e in p['path-properties']['path-route-objects'] + if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm')]) [tsp,mode] = p['path-properties']['path-route-objects'][0]\ ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') # 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: - [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] + [minosnr, baud_rate, bit_rate] = next([m['OSNR'] , m['baud_rate'] , m['bit_rate']] for m in equipment['Transceiver'][tsp].mode if m['format']==mode) output_snr = next(e['accumulative-value'] for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') @@ -369,21 +383,25 @@ def jsontocsv(json_data,equipment,fileout): for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth') power = next(e['accumulative-value'] for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power') + path_bandwidth = next(e['accumulative-value'] + for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') if isinstance(output_snr, str): isok = '' else: isok = output_snr >= minosnr + nb_tsp = ceil(path_bandwidth / bit_rate) mywriter.writerow((path_id, source, destination, + round(path_bandwidth*1e-9,2), + isok, + nb_tsp, tsp, mode, - output_osnrbandwidth, - output_osnr, - output_snrbandwidth, - output_snr, - isok, - baud_rate*1e-9, + round(output_osnr,2), + round(output_snr,2), + round(output_snrbandwidth,2), + round(baud_rate*1e-9,2), round(lin2db(power)+30,2), pth )) @@ -723,13 +741,19 @@ def requests_aggregation(pathreqlist) : # todo maybe add conditions on mode ??, spacing ... # currently if undefined takes the default values local_list = pathreqlist.copy() - for req in local_list: + for req in pathreqlist: for r in local_list : + if req.request_id == '648' and r.request_id == '644 | 642 | 643 | 645 | 647 | 646': + print(r.__repr__()) if req.request_id != r.request_id and compare_reqs(req, r): # aggregate - req.bit_rate += r.bit_rate - req.request_id = ' | '.join((req.request_id,r.request_id)) + r.path_bandwidth += req.path_bandwidth + r.request_id = ' | '.join((r.request_id,req.request_id)) # remove request from list - local_list.remove(r) + local_list.remove(req) + break + + + return local_list diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 752b7e9b0..2523f29d2 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -122,6 +122,8 @@ def __init__(self,Request,eqpt_filename): self.path_bandwidth = None if Request.path_bandwidth is not None: self.path_bandwidth = Request.path_bandwidth * 1e9 + else: + self.path_bandwidth = 0 uid = property(lambda self: repr(self)) @property From a46c8c5398cc5209652116f8afadbfd4283d1b3c Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Fri, 2 Nov 2018 10:29:30 +0100 Subject: [PATCH 046/108] OpenRoadm amplifier model! nf = f(Pin) Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 5 +++++ gnpy/core/equipment.py | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 7e23cff9d..a843f97d2 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -420,6 +420,7 @@ def __init__(self, *args, params={}, operational={}, **kwargs): self.nf = None # dB edfa nf at operational.gain_target self.gprofile = None self.pin_db = None + self.nch = None self.pout_db = None self.dp_db = None #delta P with Pref (power swwep) in power mode self.target_pch_db = None @@ -485,6 +486,7 @@ def interpol_params(self, frequencies, pin, baud_rates, pref): self.interpol_gain_ripple = interp(self.channel_freq, amplifier_freq, self.params.gain_ripple) self.interpol_nf_ripple =interp(self.channel_freq, amplifier_freq, self.params.nf_ripple) + self.nch = frequencies.size self.pin_db = lin2db(sum(pin*1e3)) """check power saturation and correct target_gain accordingly:""" @@ -521,6 +523,9 @@ def _calc_nf(self, avg = False): 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 + elif self.params.type_def == 'openroadm': + print('openroadm',self.pin_db - lin2db(self.nch) ) + nf_avg = polyval(self.params.nf_model.nf_coef, self.pin_db - lin2db(self.nch)) else: nf_avg = polyval(self.params.nf_fit_coeff, -dg) if avg: diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 6cb2d1b3e..e46c5d61c 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -20,6 +20,7 @@ Model_vg = namedtuple('Model_vg', 'nf1 nf2 delta_p') Model_fg = namedtuple('Model_fg', 'nf0') +Model_openroadm = namedtuple('Model_openroadm', 'nf_coef') Fiber = namedtuple('Fiber', 'type_variety dispersion gamma') Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ max_loss padding EOL con_in con_out') @@ -79,6 +80,13 @@ def from_default_json(cls, filename, **kwargs): except KeyError: pass #nf0 is not needed for variable gain amp nf1, nf2, delta_p = nf_model(type_variety, gain_min, gain_max, nf_min, nf_max) nf_def = Model_vg(nf1, nf2, delta_p) + elif type_def == 'openroadm': + try: + nf_coef = kwargs.pop('nf_coef') + except KeyError: #nf_coef is expected for openroadm amp + print(f'missing nf_coef input for amplifier: {type_variety} in eqpt_config.json') + exit() + nf_def = Model_openroadm(nf_coef) return cls(**{**kwargs, **json_data, 'nf_model': nf_def}) @@ -140,8 +148,11 @@ def edfa_nf(gain_target, variety_type, equipment): params = amp_params._asdict(), operational = { 'gain_target': gain_target, - 'tilt_target': 0, - }) + 'tilt_target': 0 + } + ) + amp.pin_db = 0 + amp.nch = 88 return amp._calc_nf(True) def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=False): From ca97cba18b8b990399cfef74f830ee02668768f4 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Fri, 2 Nov 2018 11:59:39 +0100 Subject: [PATCH 047/108] display channel count information when running transmission_main Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 7 +++++-- gnpy/core/equipment.py | 8 ++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index a843f97d2..46bd24346 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -524,8 +524,11 @@ def _calc_nf(self, avg = False): elif self.params.type_def == 'fixed_gain': nf_avg = self.params.nf_model.nf0 elif self.params.type_def == 'openroadm': - print('openroadm',self.pin_db - lin2db(self.nch) ) - nf_avg = polyval(self.params.nf_model.nf_coef, self.pin_db - lin2db(self.nch)) + pin_ch = self.pin_db - lin2db(self.nch) + # model NF = f(Pin) + nf_avg = polyval(self.params.nf_model.nf_coef, pin_ch) + # model OSNR = f(Pin) + #nf_avg = pin_ch - nf_avg + 58 else: nf_avg = polyval(self.params.nf_fit_coeff, -dg) if avg: diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index e46c5d61c..0501cbebf 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -198,10 +198,10 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['frequency']['max'], trx_params['spacing']) trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3 - # trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], - # trx_params['frequency']['max'], - # trx_params['spacing']) - # print('N channels = ', trx_params['nb_channel']) + nch = automatic_nch(trx_params['frequency']['min'], + trx_params['frequency']['max'], + trx_params['spacing']) + print(f'There are {nch} channels propagating') return trx_params def automatic_spacing(baud_rate): From f009306030d818652d9e48326cfc6f0a683ee47d Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 7 Nov 2018 15:40:57 +0100 Subject: [PATCH 048/108] small fix Signed-off-by: Jean-Luc Auge --- gnpy/core/equipment.py | 10 ++++++---- gnpy/core/request.py | 3 ++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 0501cbebf..2a9aa8715 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -197,11 +197,13 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], trx_params['spacing']) + nch = automatic_nch(trx_params['frequency']['min'], + trx_params['frequency']['max'], + trx_params['spacing']) + print(f'There are {nch} channels propagating') + trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3 - nch = automatic_nch(trx_params['frequency']['min'], - trx_params['frequency']['max'], - trx_params['spacing']) - print(f'There are {nch} channels propagating') + return trx_params def automatic_spacing(baud_rate): diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 00b6fe17e..3e3f4ea72 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -316,8 +316,9 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1 # 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 m['baud_rate']< req.spacing+1.1])) + if float(m['baud_rate'])+12.5e9< req.spacing])) baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) + for b in baudrate_to_explore : modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode From d68637c2c86ffe3f5e0829726a2af71d3f55dbe2 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 7 Nov 2018 15:48:30 +0100 Subject: [PATCH 049/108] clean eqpt_config.json, add low noise openroadm amp Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 51 +++++++++------------------------------ 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 8ad726408..fdbafb63f 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -17,6 +17,15 @@ "nf_max": 10, "out_voa_auto": false, "allowed_for_design": false + }, + { + "type_variety": "low_noise", + "type_def": "openroadm", + "gain_flatmax": 27, + "gain_min": 11, + "p_max": 22, + "nf_coef": [8.1e-4,6.142e-2,1.558,19.97], + "allowed_for_design": false }, { "type_variety": "std_medium_gain", @@ -48,17 +57,7 @@ "p_max": 21, "nf0": 5.5, "allowed_for_design": false - }, - { - "type_variety": "test", - "type_def": "variable_gain", - "gain_flatmax": 25, - "gain_min": 15, - "p_max": 21, - "nf_min": 7, - "nf_max": 11, - "allowed_for_design": false - } + } ], "Fiber":[{ "type_variety": "SSMF", @@ -125,20 +124,6 @@ "OSNR": 15, "bit_rate": 200e9, "roll_off": 0.15 - }, - { - "format": "PS_SP64_1", - "baud_rate": 32e9, - "OSNR": 11, - "bit_rate": 100e9, - "roll_off": 0.15 - }, - { - "format": "PS_SP64_2", - "baud_rate": 66e9, - "OSNR": 15, - "bit_rate": 200e9, - "roll_off": 0.15 } ] }, @@ -149,13 +134,6 @@ "max": 196.1e12 }, "mode":[ - { - "format": "16QAM", - "baud_rate": 32e9, - "OSNR": 19, - "bit_rate": 200e9, - "roll_off": 0.15 - }, { "format": "mode 1", "baud_rate": 32e9, @@ -166,18 +144,11 @@ { "format": "mode 3", "baud_rate": 44e9, - "OSNR": 20, + "OSNR": 18, "bit_rate": 300e9, "roll_off": 0.15 }, { - "format": "QPSK", - "baud_rate": 44e9, - "OSNR": 14, - "bit_rate": 150e9, - "roll_off": 0.15 - }, - { "format": "mode 2", "baud_rate": 66e9, "OSNR": 21, From 7b3bfea614f28a7fa0940e7155370f96f13ea1f4 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 9 Nov 2018 10:08:52 +0000 Subject: [PATCH 050/108] correction on aggregation to support disjunction feature - correction of json to csv function - small correction on disjunction format to avoid double (double crash the program) - adding time stamp to measure perf - demands with all the same disjunction can be aggregated - a new set of disjunction list is created to avoid inconsistancy with the aggregated request-ids Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 57 +++++++- gnpy/core/request.py | 244 +++++++++++++++++++++++++--------- gnpy/core/service_sheet.py | 4 +- 3 files changed, 238 insertions(+), 67 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 133219f3a..ec64b087c 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -33,6 +33,7 @@ from copy import copy, deepcopy from textwrap import dedent from math import ceil +import time #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -121,6 +122,7 @@ def disjunctions_from_json(json_data): params['node_diverse'] = snc['svec']['node-diverse'] params['disjunctions_req'] = snc['svec']['request-id-number'] disjunctions_list.append(Disjunction(**params)) + print(disjunctions_list) return disjunctions_list @@ -229,7 +231,9 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints + # also correct source and destination anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] + transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): # replace possibly wrong name with a formated roadm name @@ -252,9 +256,29 @@ def correct_route_list(network, pathreqlist): logger.critical(msg) raise ValueError(msg) + if pathreq.source not in transponders: + msg = f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.' + logger.critical(msg) + print(f'{msg}\nComputation stopped.') + exit() + + if pathreq.destination not in transponders: + msg = f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.' + logger.critical(msg) + print(f'{msg}\nComputation stopped.') + exit() + # TODO remove endpoints from this list in case they were added by the user in the xls or json files return pathreqlist +def correct_disjn(disjn): + local_disjn = disjn.copy() + for el in local_disjn: + for d in local_disjn: + if set(el.disjunctions_req) == set(d.disjunctions_req) and\ + el.disjunction_id != d.disjunction_id: + local_disjn.remove(d) + return local_disjn def path_result_json(pathresult): @@ -265,6 +289,7 @@ def path_result_json(pathresult): if __name__ == '__main__': + start = time.time() args = parser.parse_args() basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG)) logger.info(f'Computing path requests {args.service_filename} into JSON format') @@ -285,17 +310,40 @@ def path_result_json(pathresult): 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) - rqs = requests_aggregation(rqs) - print('The following services have been requested:') - print(rqs) + # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) + # print('ohohoho') + # print(dsjn) + # need to warn or correct in case of wrong disjunction form + # disjunction must not be repeated with same or different ids + dsjn = correct_disjn(dsjn) + + # Aggregate demands with same exact constraints + rqs,dsjn = requests_aggregation(rqs,dsjn) + # TODO export novel set of aggregated demands in a json file + + print('WARNING: The following services have been requested:') + print(rqs) + pths = compute_path_dsjctn(network, equipment, rqs, dsjn) propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) + end = time.time() + print(f'computation time {end-start}') - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode', 'Gbit/s' , 'nb of tsp'] + header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode', 'Gbit/s' , 'nb of tsp pairs'] data = [] data.append(header) for i, p in enumerate(propagatedpths): @@ -315,7 +363,6 @@ def path_result_json(pathresult): print(f'{firstcol} {remainingcols}') - if args.output : result = [] for p in propagatedpths: diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 3e3f4ea72..c11e6a28a 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -80,6 +80,7 @@ def __repr__(self): f'spacing:\t{self.spacing * 1e-9} GHz', f'power: \t{round(lin2db(self.power)+30,2)} dBm', f'nb channels: \t{self.nb_channel}', + f'path_bandwidth: \t{round(self.path_bandwidth * 1e-9,2)} Gbit/s', f'nodes-list:\t{self.nodes_list}', f'loose-list:\t{self.loose_list}' '\n']) @@ -99,11 +100,12 @@ def __str__(self): f'request-id-numbers: {self.disjunctions_req}'] ) def __repr__(self): - return '\n\t'.join([f'relaxable: {self.relaxable}', + return '\n\t'.join([ f'{type(self).__name__} {self.disjunction_id}', + f'relaxable: {self.relaxable}', f'link-diverse: {self.link_diverse}', f'node-diverse: {self.node_diverse}', - f'request-id-numbers: {self.disjunctions_req}'] - ) + f'request-id-numbers: {self.disjunctions_req}' + '\n']) class Result_element(Element): def __init__(self,path_request,computed_path): @@ -111,11 +113,15 @@ def __init__(self,path_request,computed_path): self.path_request = path_request self.computed_path = computed_path hop_type = [] - for e in computed_path : - if isinstance(e, Transceiver) : - hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode])) - else: - hop_type.append('not recorded') + if len(computed_path)>0 : + for e in computed_path : + if isinstance(e, Transceiver) : + hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode])) + else: + hop_type.append('not recorded') + else: + mode = 'no mode' + hop_type = ' - '.join([path_request.tsp,mode]) self.hop_type = hop_type uid = property(lambda self: repr(self)) @property @@ -161,7 +167,7 @@ def pathresult(self): 'unnumbered-hop': { 'node-id': self.path_request.source, 'link-tp-id': self.path_request.source, - 'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]), + 'hop-type': self.hop_type, 'direction': 'not used' }, 'label-hop': { @@ -178,7 +184,7 @@ def pathresult(self): 'unnumbered-hop': { 'node-id': self.path_request.destination, 'link-tp-id': self.path_request.destination, - 'hop-type': ' - '.join([self.path_request.tsp, self.path_request.tsp_mode]), + 'hop-type': self.hop_type, 'direction': 'not used' }, 'label-hop': { @@ -257,44 +263,119 @@ def compute_constrained_path(network, req): roadm = [n for n in network.nodes() if isinstance(n, Roadm)] edfa = [n for n in network.nodes() if isinstance(n, Edfa)] anytypenode = [n for n in network.nodes()] - # print(req.source) + source = next(el for el in trx if el.uid == req.source) - # start the path with its source - # TODO : avoid loops due to constraints , guess name based on string, - # avoid crashing if on req is not correct - total_path = [source] - for n in req.nodes_list: + # This method ensures that the constraint can be satisfied without loops + # except when it is not possible : eg if constraints makes a loop + # It requires that the source, dest and nodes are correct (no error in the names) + destination = next(el for el in trx if el.uid == req.destination) + nodes_list = [] + for n in req.nodes_list : + nodes_list.append(next(el for el in anytypenode if el.uid == n)) + # nodes_list contains at least the destination + if nodes_list is None : + msg = f'Request {req.request_id} problem in the constitution of nodes_list: should at least include destination' + logger.critical(msg) + exit() + if req.nodes_list[-1] != req.destination: + msg = f'Request {req.request_id} malformed list of nodes: last node should be destination trx' + logger.critical(msg) + exit() + + if len(nodes_list) == 1 : try : - node = next(el for el in trx if el.uid == n) - except StopIteration: - try: - node = next(el for el in anytypenode if el.uid == n) - except StopIteration: - try: - # TODO this test is not giving good results: full name of the - # amp is required to avoid ambiguity on the direction - node = next(el for el in anytypenode - if el.uid.find(f'{n}')) - except StopIteration: - msg = f'could not find node : {n} in network topology: \ - not a trx, roadm, edfa, fiber or fused element' - logger.critical(msg) - raise ValueError(msg) - # extend path list without repeating source -> skip first element in the list - try: - total_path.extend(dijkstra_path(network, source, node)[1:]) - source = node + total_path = dijkstra_path(network, source, destination) except NetworkXNoPath: + msg = f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology' + logger.critical(msg) + print(msg) + total_path = [] + else : + all_simp_pths = list(all_simple_paths(network,source=source,\ + target=destination)) + candidate = [] + for p in all_simp_pths : + if ispart(nodes_list, p) : + # print(f'selection{[el.uid for el in p if el in roadm]}') + candidate.append(p) + # select the shortest path (in nb of hops) + if len(candidate)>0 : + candidate.sort(key=lambda x: len(x)) + total_path = candidate[0] + else: if req.loose_list[req.nodes_list.index(n)] == 'loose': - print(f'could not find a path from {source.uid} to loose node : {n} in network topology') - print(f'node {n} is skipped') + print(f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology') + print(f'constraint ignored') + total_path = dijkstra_path(network, source, destination) else: - msg = f'could not find a path from {source.uid} to node : {n} in network topology' + msg = f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed' logger.critical(msg) print(msg) total_path = [] + # obsolete method: this does not guaranty to avoid loops or correct results + # Here is the demonstration : + # 1 1 + # eg a----b-----c + # |1 |0.5 |1 + # e----f--h--g + # 1 0.5 0.5 + # if I have to compute a to g with constraint f-c + # result will be a concatenation of: a-b-f and f-b-c and c-g + # which means a loop. + # if to avoid loops I iteratively suppress edges of the segmenst in the topo + # segment 1 = a-b-f + # 1 + # eg a b-----c + # |1 |1 + # e----f--h--g + # 1 0.5 0.5 + # then + # segment 2 = f-h-g-c + # 1 + # eg a b-----c + # |1 + # e----f h g + # 1 + # then there is no more path to g destination + # + # + # total_path = [source] + + # for n in req.nodes_list: + # try : + # node = next(el for el in trx if el.uid == n) + # except StopIteration: + # try: + # node = next(el for el in anytypenode if el.uid == n) + # except StopIteration: + # try: + # # TODO this test is not giving good results: full name of the + # # amp is required to avoid ambiguity on the direction + # node = next(el for el in anytypenode + # if n in el.uid) + # except StopIteration: + # msg = f'could not find node : {n} in network topology: \ + # not a trx, roadm, edfa, fiber or fused element' + # logger.critical(msg) + # raise ValueError(msg) + # # extend path list without repeating source -> skip first element in the list + # try: + # # to avoid looping back: use an alternate graph were current path edges and vertex are suppressed + + # total_path.extend(dijkstra_path(network, source, node)[1:]) + # source = node + # except NetworkXNoPath: + # if req.loose_list[req.nodes_list.index(n)] == 'loose': + # print(f'could not find a path from {source.uid} to loose node : {n} in network topology') + # print(f'node {n} is skipped') + # else: + # msg = f'could not find a path from {source.uid} to node : {n} in network topology' + # logger.critical(msg) + # print(msg) + # total_path = [] + return total_path def propagate(path, req, equipment, show=False): @@ -318,7 +399,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode if float(m['baud_rate'])+12.5e9< req.spacing])) baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) - + found_a_feasible_mode = False for b in baudrate_to_explore : modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode @@ -351,7 +432,7 @@ def jsontocsv(json_data,equipment,fileout): mywriter = writer(fileout) mywriter.writerow(('path-id','source','destination','path_bandwidth','Pass?',\ - 'nb of tsp','transponder-type','transponder-mode',\ + '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'] @@ -372,8 +453,11 @@ def jsontocsv(json_data,equipment,fileout): # 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: - [minosnr, baud_rate, bit_rate] = next([m['OSNR'] , m['baud_rate'] , m['bit_rate']] - for m in equipment['Transceiver'][tsp].mode if m['format']==mode) + if mode !='no mode' : + [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 p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') output_snrbandwidth = next(e['accumulative-value'] @@ -388,22 +472,38 @@ def jsontocsv(json_data,equipment,fileout): for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') if isinstance(output_snr, str): isok = '' + nb_tsp = '' + pthbdbw = '' + rosnr = '' + rsnr = '' + rsnrb = '' + br = '' + pw = '' + total_cost = '' else: - isok = output_snr >= minosnr - nb_tsp = ceil(path_bandwidth / bit_rate) + 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, - round(path_bandwidth*1e-9,2), + pthbdbw, isok, nb_tsp, + total_cost, tsp, mode, - round(output_osnr,2), - round(output_snr,2), - round(output_snrbandwidth,2), - round(baud_rate*1e-9,2), - round(lin2db(power)+30,2), + rosnr, + rsnr, + rsnrb, + br, + pw, pth )) @@ -717,7 +817,24 @@ def remove_candidate(candidates, allpaths, rq, pth) : candidates[key] = temp return candidates -def compare_reqs(req1,req2) : +def compare_reqs(req1,req2,disjlist) : + dis1 = [d for d in disjlist if req1.request_id in d.disjunctions_req] + dis2 = [d for d in disjlist if req2.request_id in d.disjunctions_req] + same_disj = False + if dis1 and dis2 : + temp1 = [] + for d in dis1: + temp1.extend(d.disjunctions_req) + temp1.remove(req1.request_id) + temp2 = [] + for d in dis2: + temp2.extend(d.disjunctions_req) + temp2.remove(req2.request_id) + if set(temp1) == set(temp2) : + same_disj = True + elif not dis2 and not dis1: + same_disj = True + if req1.source == req2.source and \ req1.destination == req2.destination and \ req1.tsp == req2.tsp and \ @@ -731,12 +848,13 @@ def compare_reqs(req1,req2) : req1.frequency == req2.frequency and \ req1.format == req2.format and \ req1.OSNR == req2.OSNR and \ - req1.roll_off == req2.roll_off : + req1.roll_off == req2.roll_off and \ + same_disj : return True else: return False -def requests_aggregation(pathreqlist) : +def requests_aggregation(pathreqlist,disjlist) : # this function aggregates requests so that if several requests # exist between same source and destination and with same transponder type # todo maybe add conditions on mode ??, spacing ... @@ -744,17 +862,21 @@ def requests_aggregation(pathreqlist) : local_list = pathreqlist.copy() for req in pathreqlist: for r in local_list : - if req.request_id == '648' and r.request_id == '644 | 642 | 643 | 645 | 647 | 646': - print(r.__repr__()) - if req.request_id != r.request_id and compare_reqs(req, r): + if req.request_id != r.request_id and compare_reqs(req, r, disjlist): # aggregate r.path_bandwidth += req.path_bandwidth + temp_r_id = r.request_id r.request_id = ' | '.join((r.request_id,req.request_id)) # remove request from list local_list.remove(req) + # todo change also disjunction req with new demand + + for d in disjlist : + if req.request_id in d.disjunctions_req : + d.disjunctions_req.remove(req.request_id) + d.disjunctions_req.append(r.request_id) + for d in disjlist : + if temp_r_id in d.disjunctions_req : + disjlist.remove(d) break - - - - return local_list - + return local_list, disjlist diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 2523f29d2..f04ef3d33 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -56,6 +56,8 @@ def __init__(self,Request,eqpt_filename): self.request_id = Request.request_id self.source = Request.source self.destination = Request.destination + # TODO: the automatic naming generated by excel parser requires that source and dest name + # be a string starting with 'trx' : this is manually added here. self.srctpid = f'trx {Request.source}' self.dsttpid = f'trx {Request.destination}' # test that trx_type belongs to eqpt_config.json @@ -90,7 +92,7 @@ def __init__(self,Request,eqpt_filename): value = value[:-2] else: value = Request.disjoint_from - self.disjoint_from = [n for n in value.split()] + self.disjoint_from = [n for n in value.split(' | ') if value] self.nodes_list = [] if Request.nodes_list : self.nodes_list = Request.nodes_list.split(' | ') From 9ea96e431c61830033dea2e773a1a722be2f4392 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 9 Nov 2018 18:54:51 +0000 Subject: [PATCH 051/108] Test disjunction file corrected to include novel path_bandwidth parameter Correction of data file for test_parser.py + adding a test on path non looping - service json files now include path_nadwidth value - previous constrained routing did not ensure loop free path. This has been corrected in A test has been added to avoid loosing this feature in future versions. Signed-off-by: EstherLerouzic --- .../data/excelTestFile_services_expected.json | 8 +++-- ...pologyExampleV2Eqpt_services_expected.json | 17 ++++++---- ...shTopologyExampleV2_services_expected.json | 17 ++++++---- tests/data/meshTopologyToy_services.json | 30 +++++++++++------ tests/test_disjunction.py | 33 +++++++++++++++++++ 5 files changed, 80 insertions(+), 25 deletions(-) diff --git a/tests/data/excelTestFile_services_expected.json b/tests/data/excelTestFile_services_expected.json index a77666b14..8537b2baf 100644 --- a/tests/data/excelTestFile_services_expected.json +++ b/tests/data/excelTestFile_services_expected.json @@ -19,7 +19,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 0 } }, "optimizations": { @@ -45,7 +46,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 0 } }, "optimizations": { @@ -79,4 +81,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/meshTopologyExampleV2Eqpt_services_expected.json b/tests/data/meshTopologyExampleV2Eqpt_services_expected.json index df872965b..43bd9b8e2 100644 --- a/tests/data/meshTopologyExampleV2Eqpt_services_expected.json +++ b/tests/data/meshTopologyExampleV2Eqpt_services_expected.json @@ -19,7 +19,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -45,7 +46,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -102,7 +104,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -128,7 +131,8 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 64, - "output-power": 0.0019952623149688794 + "output-power": 0.0019952623149688794, + "path_bandwidth": 0 } }, "optimizations": { @@ -154,7 +158,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -216,4 +221,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/meshTopologyExampleV2_services_expected.json b/tests/data/meshTopologyExampleV2_services_expected.json index df872965b..43bd9b8e2 100644 --- a/tests/data/meshTopologyExampleV2_services_expected.json +++ b/tests/data/meshTopologyExampleV2_services_expected.json @@ -19,7 +19,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -45,7 +46,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -102,7 +104,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -128,7 +131,8 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 64, - "output-power": 0.0019952623149688794 + "output-power": 0.0019952623149688794, + "path_bandwidth": 0 } }, "optimizations": { @@ -154,7 +158,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -216,4 +221,4 @@ } } ] -} \ No newline at end of file +} diff --git a/tests/data/meshTopologyToy_services.json b/tests/data/meshTopologyToy_services.json index 4fb23d406..11b7ef6df 100644 --- a/tests/data/meshTopologyToy_services.json +++ b/tests/data/meshTopologyToy_services.json @@ -19,7 +19,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -45,7 +46,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -71,7 +73,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -97,7 +100,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -154,7 +158,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -180,7 +185,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -206,7 +212,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -248,7 +255,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -290,7 +298,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { @@ -316,7 +325,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.001 + "output-power": 0.001, + "path_bandwidth": 300000000000.0 } }, "optimizations": { diff --git a/tests/test_disjunction.py b/tests/test_disjunction.py index 1e1f9aa40..1bb6877e3 100644 --- a/tests/test_disjunction.py +++ b/tests/test_disjunction.py @@ -64,3 +64,36 @@ def test_disjunction(net,eqpt,serv): print(dsjn_list) assert test +@pytest.mark.parametrize("net",[network_file_name]) +@pytest.mark.parametrize("eqpt", [eqpt_library_name]) +@pytest.mark.parametrize("serv",[service_file_name]) +def test_does_not_loop_back(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) + + # check that computed paths do not loop back ie each element appears only once + test = True + for p in pths : + for el in p: + p.remove(el) + a = [e for e in p if e.uid == el.uid] + if a : + test = False + break + + assert test \ No newline at end of file From 15304890f599f14eced789ef8d5d40a7a116f2e3 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 13 Nov 2018 17:11:07 +0000 Subject: [PATCH 052/108] Corrections related to tests and cost feature - adding cost in every config files including tests files, - correcting wrong usage reactions (if value is not correct) - correction of orthograph Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 21 ++-- examples/meshTopologyExampleV2.json | 104 ------------------- examples/meshTopologyExampleV2_services.json | 9 +- examples/path_requests_run.py | 9 +- gnpy/core/equipment.py | 6 +- gnpy/core/request.py | 4 +- gnpy/core/service_sheet.py | 7 +- tests/data/eqpt_config.json | 12 ++- 8 files changed, 44 insertions(+), 128 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index fdbafb63f..66d2d1db5 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -100,7 +100,8 @@ "power_range_db": [0,0,0.5], "roll_off": 0.15, "OSNR": 11, - "bit_rate":100e9 + "bit_rate":100e9, + "cost":1 }], "Transceiver":[ { @@ -116,14 +117,16 @@ "baud_rate": 32e9, "OSNR": 11, "bit_rate": 100e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 }, { "format": "mode 2", "baud_rate": 66e9, "OSNR": 15, "bit_rate": 200e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 } ] }, @@ -139,28 +142,32 @@ "baud_rate": 32e9, "OSNR": 12, "bit_rate": 100e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 }, { "format": "mode 3", "baud_rate": 44e9, "OSNR": 18, "bit_rate": 300e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 }, { "format": "mode 2", "baud_rate": 66e9, "OSNR": 21, "bit_rate": 400e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 }, { "format": "mode 4", "baud_rate": 66e9, "OSNR": 16, "bit_rate": 200e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 } ] } diff --git a/examples/meshTopologyExampleV2.json b/examples/meshTopologyExampleV2.json index 107b54ff1..94788653a 100644 --- a/examples/meshTopologyExampleV2.json +++ b/examples/meshTopologyExampleV2.json @@ -336,78 +336,6 @@ "con_out": null } }, - { - "uid": "fiber (Brest_KLA → Quimper)-", - "metadata": { - "location": { - "latitude": 2.5, - "longitude": 0.5 - } - }, - "type": "Fiber", - "type_variety": "SSMF", - "params": { - "length": 75.0, - "length_units": "km", - "loss_coef": 0.2, - "con_in": null, - "con_out": null - } - }, - { - "uid": "fiber (Quimper → Lorient_KMA)-", - "metadata": { - "location": { - "latitude": 1.5, - "longitude": 2.0 - } - }, - "type": "Fiber", - "type_variety": "SSMF", - "params": { - "length": 70.0, - "length_units": "km", - "loss_coef": 0.2, - "con_in": null, - "con_out": null - } - }, - { - "uid": "fiber (Ploermel → Vannes_KBE)-", - "metadata": { - "location": { - "latitude": 1.5, - "longitude": 3.0 - } - }, - "type": "Fiber", - "type_variety": "SSMF", - "params": { - "length": 50.0, - "length_units": "km", - "loss_coef": 0.2, - "con_in": null, - "con_out": null - } - }, - { - "uid": "fiber (Ploermel → Rennes_STA)-", - "metadata": { - "location": { - "latitude": 0.5, - "longitude": 1.0 - } - }, - "type": "Fiber", - "type_variety": "SSMF", - "params": { - "length": 55.0, - "length_units": "km", - "loss_coef": 0.2, - "con_in": null, - "con_out": null - } - }, { "uid": "fiber (Corlay → Lannion_CAS)-F061", "metadata": { @@ -698,14 +626,6 @@ "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", "to_node": "roadm Lorient_KMA" }, - { - "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA → Quimper)-" - }, - { - "from_node": "fiber (Quimper → Lorient_KMA)-", - "to_node": "roadm Lorient_KMA" - }, { "from_node": "roadm Vannes_KBE", "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" @@ -714,14 +634,6 @@ "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", "to_node": "roadm Vannes_KBE" }, - { - "from_node": "roadm Vannes_KBE", - "to_node": "fiber (Vannes_KBE → Ploermel)-" - }, - { - "from_node": "fiber (Ploermel → Vannes_KBE)-", - "to_node": "roadm Vannes_KBE" - }, { "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" @@ -738,14 +650,6 @@ "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", "to_node": "roadm Rennes_STA" }, - { - "from_node": "roadm Rennes_STA", - "to_node": "fiber (Rennes_STA → Ploermel)-" - }, - { - "from_node": "fiber (Ploermel → Rennes_STA)-", - "to_node": "roadm Rennes_STA" - }, { "from_node": "fiber (Lannion_CAS → Morlaix)-F059", "to_node": "ingress fused spans in Morlaix" @@ -770,14 +674,6 @@ "from_node": "fiber (Morlaix → Brest_KLA)-F060", "to_node": "roadm Brest_KLA" }, - { - "from_node": "roadm Brest_KLA", - "to_node": "fiber (Brest_KLA → Quimper)-" - }, - { - "from_node": "fiber (Quimper → Brest_KLA)-", - "to_node": "roadm Brest_KLA" - }, { "from_node": "fiber (Brest_KLA → Quimper)-", "to_node": "fiber (Quimper → Lorient_KMA)-" diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 332b6744d..88eb8da53 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -10,7 +10,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "Voyager", - "trx_mode": "16QAM", + "trx_mode": null, "effective-freq-slot": [ { "n": "null", @@ -37,7 +37,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "Voyager", - "trx_mode": "16QAM", + "trx_mode": null, "effective-freq-slot": [ { "n": "null", @@ -46,7 +46,8 @@ ], "spacing": 50000000000.0, "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673 + "output-power": 0.0012589254117941673, + "path_bandwidth": 0 } }, "optimizations": { @@ -151,7 +152,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 2", + "trx_mode": null, "effective-freq-slot": [ { "n": "null", diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index ec64b087c..fc27508b6 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -34,6 +34,7 @@ from textwrap import dedent from math import ceil import time +from textwrap import dedent #EQPT_LIBRARY_FILENAME = Path(__file__).parent / 'eqpt_config.json' @@ -92,16 +93,16 @@ def requests_from_json(json_data,equipment): if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] <= max_recommanded_nb_channels : params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] else: - temp = params['baud_rate'] - msg = dedent(f''' Requested channel number is not consistent with frequency range: - {fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp*1e-9} GHz + {fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp} GHz min recommanded spacing is {min_recommanded_spacing} max recommanded nb of channels is {max_recommanded_nb_channels} Computation stopped.''') logger.critical(msg) - raise ValueError(msg) + exit() + + else : params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) try : diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 2a9aa8715..92f932a8b 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db OSNR bit_rate') + power_dbm power_range_db OSNR bit_rate cost') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -172,7 +172,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "baud_rate": None, "OSNR": None, "bit_rate": None, - "roll_off": None} + "roll_off": None, + "cost":None} trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency @@ -193,6 +194,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['spacing'] = default_si_data.spacing trx_params['OSNR'] = default_si_data.OSNR trx_params['bit_rate'] = default_si_data.bit_rate + trx_params['cost'] = default_si_data.cost trx_params['roll_off'] = default_si_data.roll_off trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], diff --git a/gnpy/core/request.py b/gnpy/core/request.py index c11e6a28a..0c7f91881 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -34,7 +34,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off path_bandwidth') +' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off cost path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -56,6 +56,7 @@ def __init__(self, *args, **params): self.OSNR = params.OSNR self.bit_rate = params.bit_rate self.roll_off = params.roll_off + self.cost = params.cost self.path_bandwidth = params.path_bandwidth def __str__(self): @@ -401,7 +402,6 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) found_a_feasible_mode = False for b in baudrate_to_explore : - modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode if m['baud_rate'] == b] modes_to_explore = sorted(modes_to_explore, diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index f04ef3d33..9af3abf15 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -77,7 +77,12 @@ def __init__(self,Request,eqpt_filename): logger.critical(msg) exit() # excel input are in GHz and dBm - self.spacing = Request.spacing * 1e9 + if Request.spacing is not None: + self.spacing = Request.spacing * 1e9 + else: + msg = f'Request {self.request_id} missing spacing: spacing is mandatory.\ncomputation stopped' + logger.critical(msg) + exit() if Request.power is not None: self.power = db2lin(Request.power) * 1e-3 else: diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index b58362dec..a1f006261 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -77,7 +77,8 @@ "power_range_db": [0,0.5,0.5], "roll_off": 0.15, "OSNR": 15, - "bit_rate":100e9 + "bit_rate":100e9, + "cost":1 }], "Transceiver":[ { @@ -92,14 +93,16 @@ "baud_rate": 32e9, "OSNR": 11, "bit_rate": 100e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 }, { "format": "PS_SP64_2", "baud_rate": 64e9, "OSNR": 15, "bit_rate": 200e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 } ] }, @@ -115,7 +118,8 @@ "baud_rate": 32e9, "OSNR": 19, "bit_rate": 200e9, - "roll_off": 0.15 + "roll_off": 0.15, + "cost":1 } ] } From 02a7e467e2c4dd5bbe90d71d92623fd38908e2a2 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 15 Nov 2018 11:51:01 +0000 Subject: [PATCH 053/108] Major correction on mode optimization behaviour + small fixes - this version handles special cases: if no baudrate satisfies the spacing , if no mode satisfies the OSNR threshold from transponders - template of service corrected wih the novel path_bandwidth - some ideas TODO added for testing Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 34 +++++++---------- gnpy/core/request.py | 70 +++++++++++++++++++++-------------- service-template.json | 3 +- tests/test_disjunction.py | 10 ++++- 4 files changed, 68 insertions(+), 49 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index fc27508b6..c64f6a99d 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -180,21 +180,10 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): # TODO change all these req, dsjct, res lists into dict ! path_res_list = [] - - # # 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) - # TODO : get the designed power to set it when it is not an input - # pathreq.power to be adapted for i,pathreq in enumerate(pathreqlist): # use the power specified in requests but might be different from the one specified for design - # TODO: set the power as an optional parameter for requests definition + # 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) @@ -211,13 +200,18 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): total_path = propagate(total_path,pathreq,equipment, show=False) else: total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) - pathreq.baud_rate = mode['baud_rate'] - pathreq.tsp_mode = mode['format'] - pathreq.format = mode['format'] - pathreq.OSNR = mode['OSNR'] - pathreq.bit_rate = mode['bit_rate'] - else: - total_path = [] + # 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 : + # propagate_and_optimize_mode function returns the mode with the highest bitrate + # that passes. if no mode passes, then it returns an empty path + pathreq.baud_rate = mode['baud_rate'] + pathreq.tsp_mode = mode['format'] + pathreq.format = mode['format'] + pathreq.OSNR = mode['OSNR'] + pathreq.bit_rate = mode['bit_rate'] + else : + total_path = [] # we record the last tranceiver object in order to have th whole # information about spectrum. Important Note: since transceivers # attached to roadms are actually logical elements to simulate @@ -301,7 +295,7 @@ def path_result_json(pathresult): network = load_network(args.network_filename,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 + # 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 diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 0c7f91881..0969b3a4b 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -121,7 +121,9 @@ def __init__(self,path_request,computed_path): else: hop_type.append('not recorded') else: - mode = 'no mode' + # TODO differentiate empty path in case not feasible because of tsp or not feasible because + # ther is no path connecting the nodes (whatever the tsp) + mode = 'not feasible with this transponder' hop_type = ' - '.join([path_request.tsp,mode]) self.hop_type = hop_type uid = property(lambda self: repr(self)) @@ -400,28 +402,42 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): baudrate_to_explore = list(set([m['baud_rate'] for m in equipment['Transceiver'][req.tsp].mode if float(m['baud_rate'])+12.5e9< req.spacing])) baudrate_to_explore = sorted(baudrate_to_explore, reverse=True) - found_a_feasible_mode = False - for b in baudrate_to_explore : - modes_to_explore = [m for m in equipment['Transceiver'][req.tsp].mode - if m['baud_rate'] == b] - modes_to_explore = sorted(modes_to_explore, - key = lambda x: x['bit_rate'], reverse=True) - # 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 - si = create_input_spectral_information( - req.frequency['min'], equipment['SI']['default'].roll_off, - b, req.power, req.spacing, req.nb_channel) - for el in path: - si = el(si) - if show : - print(el) - for m in modes_to_explore : - if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : - found_a_feasible_mode = True - return path, m - # if no feasible path were found - return found_a_feasible_mode + 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] + modes_to_explore = sorted(modes_to_explore, + key = lambda x: x['bit_rate'], reverse=True) + # 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.frequency['min'], equipment['SI']['default'].roll_off, + b, req.power, req.spacing, req.nb_channel) + for el in path: + si = el(si) + if show : + print(el) + for m in modes_to_explore : + if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : + found_a_feasible_mode = True + return path, m + else: + mode = m + # only get to this point if no budrate/mode staisfies OSNR requirement + # returns the last propagated path and mode + msg = f'Warning! 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'Warning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' + print(msg) + logger.info(msg) + return path, None def jsontocsv(json_data,equipment,fileout): @@ -453,7 +469,7 @@ def jsontocsv(json_data,equipment,fileout): # 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 !='no 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: @@ -471,9 +487,9 @@ def jsontocsv(json_data,equipment,fileout): path_bandwidth = next(e['accumulative-value'] for e in p['path-properties']['path-metric'] if e['metric-type'] == 'path_bandwidth') if isinstance(output_snr, str): - isok = '' - nb_tsp = '' - pthbdbw = '' + isok = False + nb_tsp = 0 + pthbdbw = round(path_bandwidth*1e-9,2) rosnr = '' rsnr = '' rsnrb = '' diff --git a/service-template.json b/service-template.json index fd23b58f8..8d7a1d777 100644 --- a/service-template.json +++ b/service-template.json @@ -19,7 +19,8 @@ ], "spacing": null, "max-nb-of-channel": null, - "output-power": null + "output-power": null, + "path_bandwidth": null } }, "optimizations": { diff --git a/tests/test_disjunction.py b/tests/test_disjunction.py index 1bb6877e3..286851b40 100644 --- a/tests/test_disjunction.py +++ b/tests/test_disjunction.py @@ -96,4 +96,12 @@ def test_does_not_loop_back(net,eqpt,serv): test = False break - assert test \ No newline at end of file + assert test + + # TODO : test that identical requests are correctly agregated + # and reproduce disjunction vector as well as route constraints + # check that requests with different parameters are not aggregated + + # check that the total agregated bandwidth is the same after aggregation + + # \ No newline at end of file From b78d3d8edaa012136d079277aa8f3961af506267 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 16 Nov 2018 10:18:03 +0000 Subject: [PATCH 054/108] small fixes Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 4 ++-- gnpy/core/request.py | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index c64f6a99d..736639650 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -343,11 +343,11 @@ def path_result_json(pathresult): data.append(header) for i, p in enumerate(propagatedpths): if p: - line = [f'{rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ + line = [f'{rqs[i].request_id} {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] else: - line = [f'no path from {rqs[i].source} to {rqs[i].destination} '] + line = [f'{rqs[i].request_id} no path from {rqs[i].source} to {rqs[i].destination} '] data.append(line) col_width = max(len(word) for row in data for word in row[1:]) # padding diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 0969b3a4b..d9ef09d9d 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -424,8 +424,6 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : found_a_feasible_mode = True return path, m - else: - mode = m # only get to this point if no budrate/mode staisfies OSNR requirement # returns the last propagated path and mode msg = f'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' @@ -437,7 +435,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): msg = f'Warning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' print(msg) logger.info(msg) - return path, None + return [], None def jsontocsv(json_data,equipment,fileout): From 17f638e991ef22f6cc11dded673275decbb3588b Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 16 Nov 2018 10:38:36 +0000 Subject: [PATCH 055/108] New mesh example with correct inputs + small fix small fix: correct wrong numbering of request in json and csv output when -o is used ; correct example files signed-off-by: EstherLerouzic --- examples/meshTopologyExampleV2.json | 104 +++++++++++++++++++ examples/meshTopologyExampleV2.xls | Bin 14848 -> 14848 bytes examples/meshTopologyExampleV2_services.json | 6 +- examples/meshTopologyToy.xls | Bin 13824 -> 13824 bytes examples/path_requests_run.py | 6 +- 5 files changed, 110 insertions(+), 6 deletions(-) diff --git a/examples/meshTopologyExampleV2.json b/examples/meshTopologyExampleV2.json index 94788653a..107b54ff1 100644 --- a/examples/meshTopologyExampleV2.json +++ b/examples/meshTopologyExampleV2.json @@ -336,6 +336,78 @@ "con_out": null } }, + { + "uid": "fiber (Brest_KLA → Quimper)-", + "metadata": { + "location": { + "latitude": 2.5, + "longitude": 0.5 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 75.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Quimper → Lorient_KMA)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 2.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 70.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Ploermel → Vannes_KBE)-", + "metadata": { + "location": { + "latitude": 1.5, + "longitude": 3.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 50.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, + { + "uid": "fiber (Ploermel → Rennes_STA)-", + "metadata": { + "location": { + "latitude": 0.5, + "longitude": 1.0 + } + }, + "type": "Fiber", + "type_variety": "SSMF", + "params": { + "length": 55.0, + "length_units": "km", + "loss_coef": 0.2, + "con_in": null, + "con_out": null + } + }, { "uid": "fiber (Corlay → Lannion_CAS)-F061", "metadata": { @@ -626,6 +698,14 @@ "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", "to_node": "roadm Lorient_KMA" }, + { + "from_node": "roadm Lorient_KMA", + "to_node": "fiber (Lorient_KMA → Quimper)-" + }, + { + "from_node": "fiber (Quimper → Lorient_KMA)-", + "to_node": "roadm Lorient_KMA" + }, { "from_node": "roadm Vannes_KBE", "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" @@ -634,6 +714,14 @@ "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", "to_node": "roadm Vannes_KBE" }, + { + "from_node": "roadm Vannes_KBE", + "to_node": "fiber (Vannes_KBE → Ploermel)-" + }, + { + "from_node": "fiber (Ploermel → Vannes_KBE)-", + "to_node": "roadm Vannes_KBE" + }, { "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" @@ -650,6 +738,14 @@ "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", "to_node": "roadm Rennes_STA" }, + { + "from_node": "roadm Rennes_STA", + "to_node": "fiber (Rennes_STA → Ploermel)-" + }, + { + "from_node": "fiber (Ploermel → Rennes_STA)-", + "to_node": "roadm Rennes_STA" + }, { "from_node": "fiber (Lannion_CAS → Morlaix)-F059", "to_node": "ingress fused spans in Morlaix" @@ -674,6 +770,14 @@ "from_node": "fiber (Morlaix → Brest_KLA)-F060", "to_node": "roadm Brest_KLA" }, + { + "from_node": "roadm Brest_KLA", + "to_node": "fiber (Brest_KLA → Quimper)-" + }, + { + "from_node": "fiber (Quimper → Brest_KLA)-", + "to_node": "roadm Brest_KLA" + }, { "from_node": "fiber (Brest_KLA → Quimper)-", "to_node": "fiber (Quimper → Lorient_KMA)-" diff --git a/examples/meshTopologyExampleV2.xls b/examples/meshTopologyExampleV2.xls index 38eee6b509808431286ed1f3c17284fe25ebd74e..ade7566e51c1b5f2821dae409e913c59c58e7c6c 100644 GIT binary patch delta 771 zcmYjPO)ErE7=F(Eo_pucm@$)FB@snvd?a>KN=WQj$=71WcV>rXq_L4wXD5FE8{7pJ zinYbHu=5A}g^A)lV{y(ob?bSbbD#Hl-gC;ua`86j56<|YWnyM~b#1Q#9|xKL!gPLX zy;k^b`Y(*{?i3d*NaBmH`WJSa`APNZ&`jJIF$>rvftEi0n9f`Mzi^4k)oX07kV-a3Nry-oJyEa-ah;THb?As3i47Qt>LfPF zw#wVEQJ^7+C5GhCR{0oWX;c*fx8^%+`!>k8o}%BS?-U5zC$aGS6qi_xxZRalT#Tb&k>O0aSjsSlKp6^!KrthS;L-z8LwfLVs0hSs8TY#$ zwAch?ct$g9{@R`8vpw@{ieL4V=-b(Q>Cx2_&%`4L-+MdQeQsHIM=(mo^2W+J-xz3d a?j{?&gQE||kBsi{WnfrmKVfyq*8c!S-)kiR delta 764 zcmY*XyGjE=6uq-M`^YAnH8G7Opkm=$5+fmv5CtoffQ_vnT8Qflqar>M!GKm4(@DNS zEyOK?*w|YwSSg6#U?(f+ofVmJ29`bd?3{bfoo&<_wcD&cF>eDd4XPVU%iB$`;W+hM zD6g!qhJ_&gTbS9}tSvObfj72fUmC;OE7mKX7Qd#Z29PVvO;y-^a>dE%0Bb1MGyV-0 zelgIgkU2NufPx2(AO-}tZwss-<3|Nn(T~jQpo1F|ya7Ob?;!Q0p{VL0iv=`>Z#sML zZL;d%ZqAL11H7SZK;aFfhA4o7=joEpi^=^OzIh_G1obhFgJeVh(0Q);F|(d zpx}21tf5tzH^2ZFTk^#RW8#+i2t<+)NCH-9wA~j_o;-oadGZ8q!}v>H56rHAqYi9= z!%@ct=8iKR0&~X?F@ZIlRKF!KzY0sjJi&!@m6%`}GSWye0_>#32rT?1}1{Dm}sFkiGiRA!8~jPn>02e5$iM(6?79{L`z9h1Oy2v)7jXG zf<^^2&oKyfFIx@sCQy9;|wf&&Y649xwlns)q8n+@!kSFI+PE$cN!hAVG#Wz z*7go+NLX+;!wC&mj*m_{5P=HYvagM%c8P4+)0}lJ90J(K`b3dcVoCi6OcX9+3fXX@ z5m4~m8TKJ~eOq7!6@N@%6;sH12twdmLN@@2?+qv3w1Vphnzxm06y8Q^i`lU3W9o}G z&!ukM{UIXSsGx#R1=et4e_UW4)5y932DoU$51)?WmGv-$A&}}#00ndGCI`xFK&St0 zC9nk^ZG~;7;)Q2RusRMkX9~=BLnX}9bXhMIkDgA7%;<@h@2MNbSs(zJk!c3LfpzEm zWR;cEHT*uMZyg=e<70HeKgzBWaj%UrL{>o=ebSwYJ=9zY^Bg PvR*&8q>_#o6bx&3h!AJTj*>o?)6S}&Y64W%+&3=-O3pg_d4KHz}i{gs5ZfXZsd ziK8AF`gax2V7PY`x}1vV+d5+vT*}lK_HUzc*iUm#Z&UHe!63_wGjZg5>RP0q=_ox( zKMEF|H!DF4L$@IYgj(hZeK8v$f(F>#TSHap^jO?!j#q_;%$lU-U Date: Fri, 30 Nov 2018 09:59:51 +0100 Subject: [PATCH 056/108] Minor typos solved in README --- README.rst | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index ee110c9e7..290c3b5d4 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ planning and optimization tools in real-world mesh optical networks.** `gnpy `__ is: -- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_ +- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_. - fully community-driven, fully open source library - driven by a consortium of operators, vendors, and academic researchers - intended for rapid development of production-grade route planning tools @@ -135,8 +135,8 @@ By default, this script operates on a single span network defined in `examples/edfa_example_network.json `_ You can specify a different network at the command line as follows. For -example, to use the CORONET Global network defined in -`examples/CORONET_Global_Topology.json `_: +example, to use the CORONET Continental US (CONUS) network defined in +`examples/coronet_conus_example.json `_: .. code-block:: shell @@ -150,9 +150,10 @@ further instructions on how to prepare the Excel input file, see `Excel_userguide.rst `_. The main transmission example will calculate the average signal OSNR and SNR -across network elements (transceiver, ROADMs, fibers, and amplifiers) -between two transceivers selected by the user. (By default, for the CORONET Global -network, it will show the transmission of spectral information between Abilene, Texas and Albany, New York.) +across 93 network elements (transceiver, ROADMs, fibers, and amplifiers) +between two transceivers selected by the user. (By default, for the CORONET US +network, it will show the transmission of spectral information between Abilene, +Texas and Albany, New York.) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. @@ -181,7 +182,7 @@ can be added and existing ones removed. Three different noise models are availab 1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation. 2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax` -3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/std_medium_gain_advanced_config.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. +3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. For all amplifier models: @@ -203,12 +204,12 @@ For all amplifier models: | | | Excel template topology files.) | +----------------------+-----------+-----------------------------------------+ -The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model: +The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model: +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the fiber in the | +| `type_variety` | (string) | a unique name to ID the amplifier in the| | | | JSON or Excel template topology input | | | | file | +----------------------+-----------+-----------------------------------------+ @@ -225,7 +226,7 @@ path_request_run.py routine. +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the transceiver in | +| `type_variety` | (string) | a unique name to ID the amplifier in | | | | the JSON or Excel template topology | | | | input file | +----------------------+-----------+-----------------------------------------+ @@ -251,7 +252,7 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `bit_rate` | (number) | in bit/s | +----------------------+-----------+-----------------------------------------+ -| `roll_off` | (number) | Not used. | +| `roll_off` | (number) | | +----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. @@ -268,8 +269,8 @@ For amplifiers defined in the topology JSON input but whose gain = 0 (placeholder), auto-design will set its gain automatically: see `power_mode` in the `Spans` library to find out how the gain is calculated. -Span configuration is performed as follows. It is not a list (which may change -in later releases) and the user can only modify the value of existing +Span configuration is performed as followws. It is not a list (which may change +in later releases,) and the user can only modify the value of existing parameters: +------------------------+-----------+---------------------------------------------+ @@ -469,6 +470,11 @@ dBm/channel. These are not yet parametrized but can be modified directly in the script (via the SpectralInformation structure) to accomodate any baud rate, spacing, power or channel count demand. +The amplifier's gain is set to exactly compensate for the loss in each network +element. The amplifier is currently defined with gain range of 15 dB to 25 dB +and 21 dBm max output power. Ripple and NF models are defined in +`examples/std_medium_gain_advanced_config.json `_ + Use `examples/path_requests_run.py `_ to run multiple optimizations as follows: .. code-block:: shell @@ -490,8 +496,8 @@ library. The program computes performances for the list of services (accepts json or excel format) using the same spectrum propagation modules as transmission_main_example.py. Explanation on the Excel template is provided in the `Excel_userguide.rst `_. Template for -the json format can be found here: `service-template.json -`_. +the json format can be found here: `service_template.json +`_. Contributing ------------ From 50f884663f5af95f8c5b2488398cb8b30f43f69a Mon Sep 17 00:00:00 2001 From: miquelgarr Date: Fri, 30 Nov 2018 10:25:17 +0100 Subject: [PATCH 057/108] Minor typo changes in README --- README.rst | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/README.rst b/README.rst index 290c3b5d4..37e5ddde4 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ planning and optimization tools in real-world mesh optical networks.** `gnpy `__ is: -- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_. +- a sponsored project of the `OOPT/PSE `_ working group of the `Telecom Infra Project `_ - fully community-driven, fully open source library - driven by a consortium of operators, vendors, and academic researchers - intended for rapid development of production-grade route planning tools @@ -135,8 +135,8 @@ By default, this script operates on a single span network defined in `examples/edfa_example_network.json `_ You can specify a different network at the command line as follows. For -example, to use the CORONET Continental US (CONUS) network defined in -`examples/coronet_conus_example.json `_: +example, to use the CORONET Global network defined in +`examples/CORONET_Global_Topology.json `_: .. code-block:: shell @@ -150,10 +150,9 @@ further instructions on how to prepare the Excel input file, see `Excel_userguide.rst `_. The main transmission example will calculate the average signal OSNR and SNR -across 93 network elements (transceiver, ROADMs, fibers, and amplifiers) -between two transceivers selected by the user. (By default, for the CORONET US -network, it will show the transmission of spectral information between Abilene, -Texas and Albany, New York.) +across network elements (transceiver, ROADMs, fibers, and amplifiers) +between two transceivers selected by the user. (By default, for the CORONET Global +network, it will show the transmission of spectral information between Abilene, Texas and Albany, New York.) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. @@ -182,7 +181,7 @@ can be added and existing ones removed. Three different noise models are availab 1. `'type_def': 'variable_gain'` is a simplified model simulating a 2-coil EDFA with internal, input and output VOAs. The NF vs gain response is calculated accordingly based on the input parameters: `nf_min`, `nf_max`, and `gain_flatmax`. It is not a simple interpolation but a 2-stage NF calculation. 2. `'type_def': 'fixed_gain'` is a fixed gain model. `NF == Cte == nf0` if `gain_min < gain < gain_flatmax` -3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/advanced_config_from.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. +3. `'type_def': None` is an advanced model. A detailed json configuration file is required (by default `examples/std_medium_gain_advanced_config.json `_.) It uses a 3rd order polynomial where NF = f(gain), NF_ripple = f(frequency), gain_ripple = f(frequency), N-array dgt = f(frequency). Compared to the previous models, NF ripple and gain ripple are modelled. For all amplifier models: @@ -204,12 +203,12 @@ For all amplifier models: | | | Excel template topology files.) | +----------------------+-----------+-----------------------------------------+ -The fiber library currently describes SSMF but additional fiber types can be entered by the user following the same model: +The fiber library currently describes SSMF and NZDF but additional fiber types can be entered by the user following the same model: +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the amplifier in the| +| `type_variety` | (string) | a unique name to ID the fiber in the | | | | JSON or Excel template topology input | | | | file | +----------------------+-----------+-----------------------------------------+ @@ -226,7 +225,7 @@ path_request_run.py routine. +----------------------+-----------+-----------------------------------------+ | field | type | description | +======================+===========+=========================================+ -| `type_variety` | (string) | a unique name to ID the amplifier in | +| `type_variety` | (string) | a unique name to ID the transceiver in | | | | the JSON or Excel template topology | | | | input file | +----------------------+-----------+-----------------------------------------+ @@ -252,7 +251,7 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `bit_rate` | (number) | in bit/s | +----------------------+-----------+-----------------------------------------+ -| `roll_off` | (number) | | +| `roll_off` | (number) | Not used. | +----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. @@ -269,8 +268,8 @@ For amplifiers defined in the topology JSON input but whose gain = 0 (placeholder), auto-design will set its gain automatically: see `power_mode` in the `Spans` library to find out how the gain is calculated. -Span configuration is performed as followws. It is not a list (which may change -in later releases,) and the user can only modify the value of existing +Span configuration is performed as follows. It is not a list (which may change +in later releases) and the user can only modify the value of existing parameters: +------------------------+-----------+---------------------------------------------+ @@ -470,11 +469,6 @@ dBm/channel. These are not yet parametrized but can be modified directly in the script (via the SpectralInformation structure) to accomodate any baud rate, spacing, power or channel count demand. -The amplifier's gain is set to exactly compensate for the loss in each network -element. The amplifier is currently defined with gain range of 15 dB to 25 dB -and 21 dBm max output power. Ripple and NF models are defined in -`examples/std_medium_gain_advanced_config.json `_ - Use `examples/path_requests_run.py `_ to run multiple optimizations as follows: .. code-block:: shell @@ -496,8 +490,8 @@ library. The program computes performances for the list of services (accepts json or excel format) using the same spectrum propagation modules as transmission_main_example.py. Explanation on the Excel template is provided in the `Excel_userguide.rst `_. Template for -the json format can be found here: `service_template.json -`_. +the json format can be found here: `service-template.json +`_. Contributing ------------ @@ -588,4 +582,4 @@ License ``gnpy`` is distributed under a standard BSD 3-Clause License. -See `LICENSE `__ for more details. +See `LICENSE `__ for more details. \ No newline at end of file From 72e329b08e49e3b7ea6a40aecb4305495faaaeff Mon Sep 17 00:00:00 2001 From: miquelgarr Date: Fri, 30 Nov 2018 10:28:41 +0100 Subject: [PATCH 058/108] Now applying corrections to the README --- README.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.rst b/README.rst index 37e5ddde4..afff7a6fd 100644 --- a/README.rst +++ b/README.rst @@ -152,7 +152,7 @@ further instructions on how to prepare the Excel input file, see The main transmission example will calculate the average signal OSNR and SNR across network elements (transceiver, ROADMs, fibers, and amplifiers) between two transceivers selected by the user. (By default, for the CORONET Global -network, it will show the transmission of spectral information between Abilene, Texas and Albany, New York.) +network, it will show the transmission of spectral information between Albuquerque and Atlanta (Georgia)) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. @@ -170,8 +170,8 @@ Further Instructions for Use (`transmission_main_example.py`, `path_requests_run Design and transmission parameters are defined in a dedicated json file. By default, this information is read from `examples/eqpt_config.json -`_. This file defines the equipement librairies that -can be customized (EDFAs, fibers, and transcievers). +`_. This file defines the equipment libraries that +can be customized (EDFAs, fibers, and transceivers). It also defines the simulation parameters (spans, ROADMs, and the spectral information to transmit.) @@ -276,7 +276,7 @@ parameters: | field | type | description | +========================+===========+=============================================+ | `power_mode` | (boolean) | If false, gain mode. Auto-design sets | -| | | amplifier gain = preceeding span loss, | +| | | amplifier gain = preceding span loss, | | | | unless the amplifier exists and its | | | | gain > 0 in the topology input json. | | | | If true, power mode (recommended for | @@ -290,7 +290,7 @@ parameters: | | | (see power_range_db in the SI | | | | configuration library) the power sweep | | | | is performed w/r/t this power target, | -| | | regardless of preceeding amplifiers | +| | | regardless of preceding amplifiers | | | | power saturation/limitations. | +------------------------+-----------+---------------------------------------------+ | `delta_power_range_db` | (number) | Auto-design only, power-mode | @@ -395,7 +395,7 @@ parameters: } ROADMs can be configured as follows. The user can only modify the value of -existing parmeters: +existing parameters: +-------------------------+-----------+---------------------------------------------+ | field | type | description | @@ -415,7 +415,7 @@ existing parmeters: | | | its egress channel power = power_mode_pref, | | | | regardless of existing loss settings | | | | from the topology JSON input. It means | -| | | that the ouput power from a ROADM (and | +| | | that the output power from a ROADM (and | | | | therefore its OSNR contribution) is Cte | | | | and not depending from power_dbm and | | | | power_range_db sweep settings. This | @@ -464,9 +464,9 @@ one power/channel definition. +----------------------+-----------+-------------------------------------------+ The `transmission_main_example.py `_ -script propagates a specrum of channels at 32 Gbaud, 50 GHz spacing and 0 +script propagates a spectrum of channels at 32 Gbaud, 50 GHz spacing and 0 dBm/channel. These are not yet parametrized but can be modified directly in the -script (via the SpectralInformation structure) to accomodate any baud rate, +script (via the SpectralInformation structure) to accommodate any baud rate, spacing, power or channel count demand. Use `examples/path_requests_run.py `_ to run multiple optimizations as follows: @@ -507,7 +507,7 @@ To get involved, please contact James Powell See the `Onboarding Guide `_ for -specific details on code contribtions. +specific details on code contributions. See `AUTHORS.rst `_ for past and present contributors. @@ -517,7 +517,7 @@ Project Background Data Centers are built upon interchangeable, highly standardized node and network architectures rather than a sum of isolated solutions. This also translates to optical networking. It leads to a push in enabling multi-vendor -optical network by disaggregating HW and SW functions and focussing on +optical network by disaggregating HW and SW functions and focusing on interoperability. In this paradigm, the burden of responsibility for ensuring the performance of such disaggregated open optical systems falls on the operators. Consequently, operators and vendors are collaborating in defining From 81c5ef4a231f7efe5c703513059cfb6c83f76604 Mon Sep 17 00:00:00 2001 From: miquelgarr Date: Fri, 30 Nov 2018 12:16:22 +0100 Subject: [PATCH 059/108] Adding "transmission_main_example.py -h" --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index afff7a6fd..9ca10262f 100644 --- a/README.rst +++ b/README.rst @@ -151,7 +151,7 @@ further instructions on how to prepare the Excel input file, see The main transmission example will calculate the average signal OSNR and SNR across network elements (transceiver, ROADMs, fibers, and amplifiers) -between two transceivers selected by the user. (By default, for the CORONET Global +between two transceivers selected by the user. Additional details are provided by doing ``transmission_main_example.py -h``. (By default, for the CORONET Global network, it will show the transmission of spectral information between Albuquerque and Atlanta (Georgia)) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. From f51061d6503ac84b9fa4f2b121a94d5a5a6476e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 11 Dec 2018 16:59:05 +0100 Subject: [PATCH 060/108] Add support for Tx OSNR. --- README.rst | 4 ++++ examples/eqpt_config.json | 7 +++++++ gnpy/core/equipment.py | 3 ++- gnpy/core/info.py | 7 ++++--- gnpy/core/request.py | 9 +++++---- 5 files changed, 22 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index ee110c9e7..2e5c95b56 100644 --- a/README.rst +++ b/README.rst @@ -253,6 +253,8 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `roll_off` | (number) | Not used. | +----------------------+-----------+-----------------------------------------+ +| `tx_osnr` | (number) | In dB. OSNR out from transponder. | ++----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. @@ -444,6 +446,8 @@ one power/channel definition. +----------------------+-----------+-------------------------------------------+ | `bit_rate` | (number) | Not used. | +----------------------+-----------+-------------------------------------------+ +| `tx_osnr` | (number) | In dB. OSNR out from transponder. | ++----------------------+-----------+-------------------------------------------+ | `power_dbm` | (number) | Reference channel power. In gain mode | | | | (see spans/power_mode = false), all gain | | | | settings are offset w/r/t this reference | diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 66d2d1db5..4654cddd3 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -101,6 +101,7 @@ "roll_off": 0.15, "OSNR": 11, "bit_rate":100e9, + "tx_osnr": 45, "cost":1 }], "Transceiver":[ @@ -118,6 +119,7 @@ "OSNR": 11, "bit_rate": 100e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 }, { @@ -126,6 +128,7 @@ "OSNR": 15, "bit_rate": 200e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 } ] @@ -143,6 +146,7 @@ "OSNR": 12, "bit_rate": 100e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 }, { @@ -151,6 +155,7 @@ "OSNR": 18, "bit_rate": 300e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 }, { @@ -159,6 +164,7 @@ "OSNR": 21, "bit_rate": 400e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 }, { @@ -167,6 +173,7 @@ "OSNR": 16, "bit_rate": 200e9, "roll_off": 0.15, + "tx_osnr": 45, "cost":1 } ] diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 92f932a8b..26b624cab 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db OSNR bit_rate cost') + power_dbm power_range_db OSNR bit_rate tx_osnr cost') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -199,6 +199,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], trx_params['spacing']) + trx_params['tx_osnr'] = default_si_data.tx_osnr nch = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], trx_params['spacing']) diff --git a/gnpy/core/info.py b/gnpy/core/info.py index d63b3069c..12596e7e6 100644 --- a/gnpy/core/info.py +++ b/gnpy/core/info.py @@ -11,7 +11,7 @@ from collections import namedtuple from numpy import array -from gnpy.core.utils import lin2db +from gnpy.core.utils import lin2db, db2lin from json import loads from gnpy.core.utils import load_json @@ -56,13 +56,14 @@ def merge_input_spectral_information(*si): #TODO pass -def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel): +def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel, tx_osnr): # pref in dB : convert power lin into power in dB pref = lin2db(power * 1e3) + ase_power = (power / db2lin(tx_osnr)) * (baud_rate / 12.5e9) si = SpectralInformation(pref=Pref(pref, pref)) si = si.update(carriers=[ Channel(f, (f_min+spacing*f), - baud_rate, roll_off, Power(power, 0, 0)) for f in range(1,nb_channel+1) + baud_rate, roll_off, Power(power, 0, ase_power)) for f in range(1,nb_channel+1) ]) return si diff --git a/gnpy/core/request.py b/gnpy/core/request.py index d9ef09d9d..4a9ac9f39 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -34,7 +34,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off cost path_bandwidth') +' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr cost path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -56,6 +56,7 @@ def __init__(self, *args, **params): self.OSNR = params.OSNR self.bit_rate = params.bit_rate self.roll_off = params.roll_off + self.tx_osnr = params.tx_osnr self.cost = params.cost self.path_bandwidth = params.path_bandwidth @@ -385,8 +386,8 @@ def propagate(path, req, equipment, show=False): #update roadm loss in case of power sweep (power mode only) set_roadm_loss(path, equipment, lin2db(req.power*1e3)) si = create_input_spectral_information( - req.frequency['min'], req.roll_off, - req.baud_rate, req.power, req.spacing, req.nb_channel) + req.frequency['min'], req.roll_off, req.baud_rate, + req.power, req.spacing, req.nb_channel, req.tx_osnr) for el in path: si = el(si) if show : @@ -415,7 +416,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # TODO : if the loop in mode optimization does not have a feasible path, then bugs si = create_input_spectral_information( req.frequency['min'], equipment['SI']['default'].roll_off, - b, req.power, req.spacing, req.nb_channel) + b, req.power, req.spacing, req.nb_channel, req.tx_osnr) for el in path: si = el(si) if show : From 0927a92652590862556bd612a56b044080f7f027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 11 Dec 2018 17:25:26 +0100 Subject: [PATCH 061/108] fix amplifier test --- tests/test_amplifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_amplifier.py b/tests/test_amplifier.py index ace6b0941..25d6b66e6 100644 --- a/tests/test_amplifier.py +++ b/tests/test_amplifier.py @@ -66,7 +66,7 @@ def setup_trx(): def si(nch_and_spacing, bw): """parametrize a channel comb with nb_channel, spacing and signal bw""" nb_channel, spacing = nch_and_spacing - return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel) + return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel, 45) @pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)]) def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): From b0bb41bac646519d81c3fadde8939a6f9b270f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 11 Dec 2018 20:28:20 +0100 Subject: [PATCH 062/108] fix eqpt_config test file --- tests/data/eqpt_config.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index a1f006261..1585e25a7 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -78,6 +78,7 @@ "roll_off": 0.15, "OSNR": 15, "bit_rate":100e9, + "tx_osnr": 100, "cost":1 }], "Transceiver":[ @@ -94,6 +95,7 @@ "OSNR": 11, "bit_rate": 100e9, "roll_off": 0.15, + "tx_osnr": 100, "cost":1 }, { @@ -102,6 +104,7 @@ "OSNR": 15, "bit_rate": 200e9, "roll_off": 0.15, + "tx_osnr": 100, "cost":1 } ] @@ -119,6 +122,7 @@ "OSNR": 19, "bit_rate": 200e9, "roll_off": 0.15, + "tx_osnr": 100, "cost":1 } ] From b6bc995e40d49c37acffdbea769683e82fef3e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Tue, 11 Dec 2018 21:29:17 +0100 Subject: [PATCH 063/108] fix OSNR test --- tests/test_amplifier.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_amplifier.py b/tests/test_amplifier.py index 25d6b66e6..22e60b090 100644 --- a/tests/test_amplifier.py +++ b/tests/test_amplifier.py @@ -66,7 +66,7 @@ def setup_trx(): def si(nch_and_spacing, bw): """parametrize a channel comb with nb_channel, spacing and signal bw""" nb_channel, spacing = nch_and_spacing - return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel, 45) + return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel, 100) @pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)]) def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): From 61289119cb451e8bc2183c22c8ef1e181bb7da16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20M=C3=A5rtensson?= Date: Thu, 13 Dec 2018 11:07:16 +0100 Subject: [PATCH 064/108] fix OSNR calculation when ASE or NLI is zero --- gnpy/core/elements.py | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 46bd24346..a969a14dc 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -18,7 +18,7 @@ unique identifier and a printable name. ''' -from numpy import abs, arange, arcsinh, array, exp +from numpy import abs, arange, arcsinh, array, exp, divide, errstate from numpy import interp, log10, mean, pi, polyfit, polyval, sum from scipy.constants import c, h from collections import namedtuple @@ -36,23 +36,19 @@ def __init__(self, *args, **kwargs): self.snr = None self.passive = False - def _calc_snr(self, spectral_info): - ase = [c.power.ase for c in spectral_info.carriers] - nli = [c.power.nli for c in spectral_info.carriers] - if min(ase) > 1e-20: - self.osnr_ase = [lin2db(c.power.signal/c.power.ase) - for c in spectral_info.carriers] + def _calc_snr(self, spectral_info): + with errstate(divide='ignore'): + self.osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) + for c in spectral_info.carriers] ratio_01nm = [lin2db(12.5e9/c.baud_rate) for c in spectral_info.carriers] self.osnr_ase_01nm = [ase - ratio for ase, ratio in zip(self.osnr_ase, ratio_01nm)] - if min(nli) > 1e-20: - self.osnr_nli = [lin2db(c.power.signal/c.power.nli) + self.osnr_nli = [lin2db(divide(c.power.signal, c.power.nli)) for c in spectral_info.carriers] - self.snr = [lin2db(c.power.signal/(c.power.nli+c.power.ase)) + self.snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase)) for c in spectral_info.carriers] - @property def to_json(self): return {'uid' : self.uid, @@ -80,7 +76,7 @@ def __str__(self): return '\n'.join([f'{type(self).__name__} {self.uid}', - f' OSNR ASE (1nm): {osnr_ase_01nm:.2f}', + f' OSNR ASE (0.1nm): {osnr_ase_01nm:.2f}', f' OSNR ASE (signal bw): {osnr_ase:.2f}', f' SNR total (signal bw): {snr:.2f}']) From 2d515eea4ca1dc877a1f11a967babb68a96839a6 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 13 Dec 2018 11:28:42 +0000 Subject: [PATCH 065/108] Corrections of some bugs - output file of -o option in path_requests_run.py was not correctly handled in case of path indirection eg ../../bar.foo - fiber constraint is not correctly handled in case of several parrallel directions of same fiber name. remove if from the set of constraint till problem is not solved - loose_list was not correctly indexed in request.py. assume the first element attribute (except of the transceiver) applies for the whole list. This will need further corrections - handle the case when path.snr is none in the optimization of mode process --- examples/path_requests_run.py | 14 ++++++++------ gnpy/core/request.py | 11 +++++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index f3e7d3435..2fa110b95 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -25,7 +25,7 @@ from gnpy.core.utils import load_json from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing -from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused +from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, @@ -44,7 +44,7 @@ parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') -parser.add_argument('-o', '--output') +parser.add_argument('-o', '--output', type = Path) def requests_from_json(json_data,equipment): @@ -226,7 +226,9 @@ def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints # also correct source and destination - anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] + anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)] + # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms + # so fiber constraint is not supported transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): @@ -249,7 +251,6 @@ def correct_route_list(network, pathreqlist): msg = f'could not find node : {n_id} in network topology. Strict constraint can not be applied.' logger.critical(msg) raise ValueError(msg) - if pathreq.source not in transponders: msg = f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.' logger.critical(msg) @@ -363,9 +364,10 @@ def path_result_json(pathresult): for i,p in enumerate(propagatedpths): result.append(Result_element(rqs[i],p)) temp = path_result_json(result) - with open(args.output, 'w', encoding='utf-8') as f: + fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' + fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json' + with open(fnamejson, 'w', encoding='utf-8') as f: f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) - fnamecsv = next(s for s in args.output.split('.')) + '.csv' with open(fnamecsv,"w", encoding='utf-8') as fcsv : jsontocsv(temp,equipment,fcsv) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index d9ef09d9d..7c8b81573 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -307,7 +307,7 @@ def compute_constrained_path(network, req): candidate.sort(key=lambda x: len(x)) total_path = candidate[0] else: - if req.loose_list[req.nodes_list.index(n)] == 'loose': + if req.loose_list[1] == 'loose': print(f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination) @@ -421,9 +421,12 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if show : print(el) for m in modes_to_explore : - if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : - found_a_feasible_mode = True - return path, m + if path[-1].snr is not None: + if round(mean(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 budrate/mode staisfies OSNR requirement # returns the last propagated path and mode msg = f'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' From 3cdc8511a8645bd04d2d762d051c21cc2bc19761 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 13 Dec 2018 11:37:26 +0000 Subject: [PATCH 066/108] add a limit cutoff for path search all_simple_path search leads to a very long time process in case of large network a cutoff parameters has been added to avoid this. Value needs to be parametrized: right now correspond to my experience Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 7c8b81573..0d3fccfbf 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -275,6 +275,7 @@ def compute_constrained_path(network, req): destination = next(el for el in trx if el.uid == req.destination) nodes_list = [] for n in req.nodes_list : + # for debug excel print(n) nodes_list.append(next(el for el in anytypenode if el.uid == n)) # nodes_list contains at least the destination if nodes_list is None : @@ -296,7 +297,7 @@ def compute_constrained_path(network, req): total_path = [] else : all_simp_pths = list(all_simple_paths(network,source=source,\ - target=destination)) + target=destination, cutoff=120)) candidate = [] for p in all_simp_pths : if ispart(nodes_list, p) : @@ -463,7 +464,7 @@ def jsontocsv(json_data,equipment,fileout): # selects only roadm nodes pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] for e in p['path-properties']['path-route-objects'] - if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm')]) + if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm') or e['path-route-object']['unnumbered-hop']['node-id'].startswith('Edfa')]) [tsp,mode] = p['path-properties']['path-route-objects'][0]\ ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') From 6af137a085239012ab95d5d45c7ee3cdff4bceea Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 14 Dec 2018 18:06:03 +0000 Subject: [PATCH 067/108] Fixes concerning service sheet reading - indicate which request has incorrect tsp type or mode - correct wrong reading from excel : if string values contain only a number xlrd interprets as a float eg 1 -> 1.0 . A function is used to correct this when filling the Request class Signed-off-by: EstherLerouzic --- gnpy/core/service_sheet.py | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 9af3abf15..3759e07e1 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -47,13 +47,7 @@ def __init__(self,Request,eqpt_filename): # request_id is str # excel has automatic number formatting that adds .0 on integer values # the next lines recover the pure int value, assuming this .0 is unwanted - if not isinstance(Request.request_id,str): - value = str(int(Request.request_id)) - if value.endswith('.0'): - value = value[:-2] - self.request_id = value - else: - self.request_id = Request.request_id + self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) self.source = Request.source self.destination = Request.destination # TODO: the automatic naming generated by excel parser requires that source and dest name @@ -65,14 +59,21 @@ def __init__(self,Request,eqpt_filename): equipment = load_equipment(eqpt_filename) try : if equipment['Transceiver'][Request.trx_type]: - self.trx_type = Request.trx_type + self.trx_type = correct_xlrd_int_to_str_reading(Request.trx_type) if Request.mode is not None : - if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: - self.mode = Request.mode + Requestmode = correct_xlrd_int_to_str_reading(Request.mode) + if [mode for mode in equipment['Transceiver'][Request.trx_type].mode if mode['format'] == Requestmode]: + self.mode = Requestmode + else : + msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.' + #print(msg) + logger.critical(msg) + exit(1) else: + Requestmode = None self.mode = Request.mode except KeyError: - msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' + msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.' #print(msg) logger.critical(msg) exit() @@ -91,12 +92,8 @@ def __init__(self,Request,eqpt_filename): self.nb_channel = int(Request.nb_channel) else: self.nb_channel = None - if not isinstance(Request.disjoint_from,str): - value = str(int(Request.disjoint_from)) - if value.endswith('.0'): - value = value[:-2] - else: - value = Request.disjoint_from + + value = correct_xlrd_int_to_str_reading(Request.disjoint_from) self.disjoint_from = [n for n in value.split(' | ') if value] self.nodes_list = [] if Request.nodes_list : @@ -213,6 +210,15 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil f.write(dumps(data, indent=2, ensure_ascii=False)) return data +def correct_xlrd_int_to_str_reading(v) : + if not isinstance(v,str): + value = str(int(v)) + if value.endswith('.0'): + value = value[:-2] + else: + value = v + return value + # to be used from dutc def parse_row(row, fieldnames): return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN]) From 020d8527589d6c3e2588cda65469ca9241387e60 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 19 Dec 2018 10:49:31 +0000 Subject: [PATCH 068/108] adding some exception handling to help user debugging exception handling add some info on which element (uid) is causing a problem this can help the user to identify where he must correct the input files Signed-off-by: EstherLerouzic --- gnpy/core/convert.py | 13 +++++++++---- gnpy/core/network.py | 32 +++++++++++++++++++++++++++----- gnpy/core/request.py | 3 ++- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index b356e03e4..f27436d46 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -285,10 +285,15 @@ def eqpt_connection_by_city(city_name): # Then len(other_cities) == 2 direction = ['ingress', 'egress'] for i in range(2): - from_ = fiber_link(other_cities[i], city_name) - in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) - to_ = fiber_link(city_name, other_cities[1-i]) - subdata += connect_eqpt(from_, in_, to_) + try: + from_ = fiber_link(other_cities[i], city_name) + in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) + to_ = fiber_link(city_name, other_cities[1-i]) + subdata += connect_eqpt(from_, in_, to_) + except IndexError: + msg = f'In {__name__} eqpt_connection_by_city:\n\t{city_name} is not properly connected' + print(msg) + exit(1) elif nodes_by_city[city_name].node_type.lower() == 'roadm': for other_city in other_cities: from_ = f'roadm {city_name}' diff --git a/gnpy/core/network.py b/gnpy/core/network.py index e14947459..dbd6523d3 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -64,7 +64,12 @@ def network_from_json(json_data, equipment): for cx in json_data['connections']: from_node, to_node = cx['from_node'], cx['to_node'] - g.add_edge(nodes[from_node], nodes[to_node]) + try: + g.add_edge(nodes[from_node], nodes[to_node]) + except KeyError: + msg = f'In {__name__} network_from_json function:\n\tcan not find {from_node} or {to_node} defined in {cx}' + print(msg) + exit(1) return g @@ -171,7 +176,13 @@ def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp def prev_node_generator(network, node): """fused spans interest: iterate over all predecessors while they are Fused or Fiber type""" - prev_node = next(n for n in network.predecessors(node)) + try: + prev_node = next(n for n in network.predecessors(node)) + except StopIteration: + msg = f'In {__name__} prev_node_generator function:\n\t{node.uid} is not properly connected, please check network topology' + print(msg) + logger.critical(msg) + exit(1) # yield and re-iterate if isinstance(prev_node, Fused) or isinstance(node, Fused): yield prev_node @@ -182,7 +193,11 @@ def prev_node_generator(network, node): def next_node_generator(network, node): """fused spans interest: iterate over all successors while they are Fused or Fiber type""" - next_node = next(n for n in network.successors(node)) + try: + next_node = next(n for n in network.successors(node)) + except StopIteration: + print(f'In {__name__} next_node_generator function:\n\t{node.uid} is not properly connected, please check network topology') + exit(1) # yield and re-iterate if isinstance(next_node, Fused) or isinstance(node, Fused): yield next_node @@ -334,7 +349,8 @@ def split_fiber(network, fiber, bounds, target_length, equipment): next_node = next(network.successors(fiber)) prev_node = next(network.predecessors(fiber)) except StopIteration: - print(f'{repr(fiber)} is not properly connected, please check network topology') + + print(f'In {__name__} split_fiber function:\n\t{fiber.uid} is not properly connected, please check network topology') exit() network.remove_node(fiber) @@ -367,7 +383,13 @@ def add_fiber_padding(network, fibers, padding): if isinstance(fiber, Fiber))""" for fiber in fibers: this_span_loss = span_loss(network, fiber) - next_node = next(network.successors(fiber)) + try: + next_node = next(network.successors(fiber)) + except StopIteration: + msg = f'In {__name__} add_fiber_padding function:\n\t{fiber.uid} is not properly connected, please check network topology' + print(msg) + logger.critical(msg) + exit(1) if this_span_loss < padding and not (isinstance(next_node, Fused)): #add a padding att_in at the input of the 1st fiber: #address the case when several fibers are spliced together diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 0d3fccfbf..d5e9b0f19 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -588,7 +588,8 @@ def __init__(self, req, pth, simplepth): for pathreq in pathreqlist_disjt : all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ - target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + target=next(el for el in network.nodes() if el.uid == pathreq.destination),\ + cutoff=80)) # sort them all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) # reversed direction paths required to check disjunction on both direction From 9cfb57dc4bc0fffcae8638990b492755b4c4ff2c Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 19 Dec 2018 15:22:03 +0000 Subject: [PATCH 069/108] bug fix due to tx_osnr add tx_osnr was impacting automatic mode selection feature : this commit solves the error + add a novel version of the excel example with an empty mode cases Signed-off-by: EstherLerouzic --- examples/meshTopologyExampleV2.xls | Bin 14848 -> 14848 bytes examples/meshTopologyExampleV2_services.json | 16 ++++++------- examples/path_requests_run.py | 2 ++ gnpy/core/equipment.py | 1 + gnpy/core/request.py | 24 +++++++++++-------- 5 files changed, 25 insertions(+), 18 deletions(-) diff --git a/examples/meshTopologyExampleV2.xls b/examples/meshTopologyExampleV2.xls index ade7566e51c1b5f2821dae409e913c59c58e7c6c..ab72a957dea0dfc90573bc450b271184e07b7213 100644 GIT binary patch delta 823 zcmY*WJ4+l<7(I6$cV=g2_T?fQwh&oT?6R(}Ai+ull4cR?(k+6`AO=kq8(%B9g;>cg zZ2SXu!6}SjVP#>MPL!ldTH6#$NJ#SCS@hnWY36+A&UYTS)9$oC%?FjuBIvQv4BxMQ z>>|L=a(}R}vEFWVk;T1O2~OG9h-IpLDa>bK(19KJVD!d;!Ui#MT49sc>O2QW=qug< zSR8E?zu2_Y%o0lj5~MFKzK?drVs&#e%%~rvpl-mB0-7K#poNF!IWC8V#3)1bZM4bu zN}90TbD&{q{4A?^x)xALBr0R^q(s3!LuylOkD{NC!6;EM#6qvQ80mOUgDk3@eO78AMg une&Nd)1P{UIkMLIXB=ONwdoP}u|E6m^T{0(`&K==o_=kzo9Ne!WBv!*tZ-!j delta 815 zcmZuvyGjE=6g`uDn%&Jq;wuY^1i^$P8br}93Zkfut&NB_#s`Y&G$bP03BndO_JV?_ zTUi9`1-G&D2mFQL^Uhk#2+qL3nRD;C=iZq{wNbsDbTS7i(9*E5zP+(`3S%nb1iGt8K$ zJ04?_LJma+I%9N~!xx=dbcQ+Sg=M#iZnF4al;OF>hOJU0BR>3Gm&FXbA6`-H(geyx jG&WbyZoI9LKJ#v_%Q`rGVcwyd_a8^b753q`#th{LxGZYG diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index eacbea1c7..3fef7f95d 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -10,7 +10,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "Voyager", - "trx_mode": "mode 1", + "trx_mode": null, "effective-freq-slot": [ { "n": "null", @@ -45,7 +45,7 @@ } ], "spacing": 50000000000.0, - "max-nb-of-channel": 80, + "max-nb-of-channel": null, "output-power": 0.0012589254117941673, "path_bandwidth": 0 } @@ -133,8 +133,8 @@ } ], "spacing": 50000000000.0, - "max-nb-of-channel": 80, - "output-power": 0.0012589254117941673, + "max-nb-of-channel": null, + "output-power": null, "path_bandwidth": 60000000000.0 } }, @@ -152,7 +152,7 @@ "te-bandwidth": { "technology": "flexi-grid", "trx_type": "vendorA_trx-type1", - "trx_mode": "mode 2", + "trx_mode": null, "effective-freq-slot": [ { "n": "null", @@ -160,8 +160,8 @@ } ], "spacing": 75000000000.0, - "max-nb-of-channel": 63, - "output-power": 0.0019952623149688794, + "max-nb-of-channel": null, + "output-power": null, "path_bandwidth": 150000000000.0 } }, @@ -188,7 +188,7 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 63, - "output-power": 0.0019952623149688794, + "output-power": null, "path_bandwidth": 20000000000.0 } }, diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index f3e7d3435..dcbf45806 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -69,6 +69,7 @@ def requests_from_json(json_data,equipment): # 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['tx_osnr']) # optical power might be set differently in the request. if it is indicated then the # params['power'] is updated if req['path-constraints']['te-bandwidth']['output-power']: @@ -208,6 +209,7 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): pathreq.tsp_mode = mode['format'] pathreq.format = mode['format'] pathreq.OSNR = mode['OSNR'] + pathreq.tx_osnr = mode['tx_osnr'] pathreq.bit_rate = mode['bit_rate'] else : total_path = [] diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 26b624cab..5dfa34fe8 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -173,6 +173,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "OSNR": None, "bit_rate": None, "roll_off": None, + "tx_osnr":None, "cost":None} trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 4a9ac9f39..bab680c4e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -410,22 +410,26 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if m['baud_rate'] == b] 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.frequency['min'], equipment['SI']['default'].roll_off, - b, req.power, req.spacing, req.nb_channel, req.tx_osnr) - for el in path: - si = el(si) + for m in modes_to_explore : + si = create_input_spectral_information( + req.frequency['min'], equipment['SI']['default'].roll_off, + b, req.power, req.spacing, req.nb_channel, m['tx_osnr']) + for el in path: + si = el(si) if show : print(el) - for m in modes_to_explore : - if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : - found_a_feasible_mode = True - return path, m - # only get to this point if no budrate/mode staisfies OSNR requirement + if path[-1].snr is not None: + if round(mean(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'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' print(msg) From cb45c7ef1629bc46f89bd31bf23e7081f21fbb3a Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 19 Dec 2018 18:13:05 +0000 Subject: [PATCH 070/108] new test to check all combination of service specifications add combination of [mode, pow, nb channel] filled or empty leading to feasible path or not, and check the propagate and propagate_and_optimize_mode functions work as expected on a reference toy example Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 2 +- tests/data/eqpt_config.json | 54 ++++ tests/data/meshTopologyToy_services.json | 298 ++++++++++++++++++++++- tests/test_automaticmodefeature.py | 88 +++++++ 4 files changed, 429 insertions(+), 13 deletions(-) create mode 100644 tests/test_automaticmodefeature.py diff --git a/gnpy/core/request.py b/gnpy/core/request.py index bab680c4e..992864ac0 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -410,7 +410,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if m['baud_rate'] == b] modes_to_explore = sorted(modes_to_explore, key = lambda x: x['bit_rate'], reverse=True) - print(modes_to_explore) + # 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 diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index 1585e25a7..7a93b1d1c 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -126,6 +126,60 @@ "cost":1 } ] + }, + { + "type_variety": "Voyager", + "frequency":{ + "min": 191.35e12, + "max": 196.1e12 + }, + "mode":[ + { + "format": "mode 1", + "baud_rate": 32e9, + "OSNR": 12, + "bit_rate": 100e9, + "roll_off": 0.15, + "tx_osnr": 45, + "cost":1 + }, + { + "format": "mode 3", + "baud_rate": 44e9, + "OSNR": 18, + "bit_rate": 300e9, + "roll_off": 0.15, + "tx_osnr": 45, + "cost":1 + }, + { + "format": "mode 2", + "baud_rate": 66e9, + "OSNR": 21, + "bit_rate": 400e9, + "roll_off": 0.15, + "tx_osnr": 45, + "cost":1 + }, + { + "format": "mode 2 - fake", + "baud_rate": 66e9, + "OSNR": 21, + "bit_rate": 400e9, + "roll_off": 0.15, + "tx_osnr": 45, + "cost":1 + }, + { + "format": "mode 4", + "baud_rate": 66e9, + "OSNR": 16, + "bit_rate": 200e9, + "roll_off": 0.15, + "tx_osnr": 45, + "cost":1 + } + ] } ] diff --git a/tests/data/meshTopologyToy_services.json b/tests/data/meshTopologyToy_services.json index 11b7ef6df..83e5ae85b 100644 --- a/tests/data/meshTopologyToy_services.json +++ b/tests/data/meshTopologyToy_services.json @@ -148,16 +148,16 @@ "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", - "trx_type": "vendorA_trx-type1", - "trx_mode": "PS_SP64_1", + "trx_type": "Voyager", + "trx_mode": "mode 2 - fake", "effective-freq-slot": [ { "n": "null", "m": "null" } ], - "spacing": 50000000000.0, - "max-nb-of-channel": 80, + "spacing": 75000000000.0, + "max-nb-of-channel": 63, "output-power": 0.001, "path_bandwidth": 300000000000.0 } @@ -175,16 +175,16 @@ "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", - "trx_type": "Voyager_16QAM", - "trx_mode": "16QAM", + "trx_type": "Voyager", + "trx_mode": "mode 2", "effective-freq-slot": [ { "n": "null", "m": "null" } ], - "spacing": 50000000000.0, - "max-nb-of-channel": 80, + "spacing": 75000000000.0, + "max-nb-of-channel": 63, "output-power": 0.001, "path_bandwidth": 300000000000.0 } @@ -245,16 +245,16 @@ "path-constraints": { "te-bandwidth": { "technology": "flexi-grid", - "trx_type": "vendorA_trx-type1", - "trx_mode": "PS_SP64_1", + "trx_type": "Voyager", + "trx_mode": "mode 3", "effective-freq-slot": [ { "n": "null", "m": "null" } ], - "spacing": 50000000000.0, - "max-nb-of-channel": 80, + "spacing": 62500000000.0, + "max-nb-of-channel": 76, "output-power": 0.001, "path_bandwidth": 300000000000.0 } @@ -363,6 +363,280 @@ } ] } + }, + { + "request-id": "e:1# /", + "source": "a", + "destination": "g", + "src-tp-id": "trx a", + "dst-tp-id": "trx g", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager_16QAM", + "trx_mode": "16QAM", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": null, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "b-2a", + "source": "a", + "destination": "h", + "src-tp-id": "trx a", + "dst-tp-id": "trx h", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": null, + "output-power": 0.001, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "3a;?", + "source": "f", + "destination": "b", + "src-tp-id": "trx f", + "dst-tp-id": "trx b", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": "PS_SP64_1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": null, + "output-power": null, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "ee-s", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "vendorA_trx-type1", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 80, + "output-power": 0.001, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [ + { + "index": 0, + "unnumbered-hop": { + "node-id": "roadm e", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + }, + { + "index": 1, + "unnumbered-hop": { + "node-id": "roadm g", + "link-tp-id": "link-tp-id is not used", + "hop-type": "loose", + "direction": "direction is not used" + }, + "label-hop": { + "te-label": { + "generic": "generic is not used", + "direction": "direction is not used" + } + } + } + ] + } + }, + { + "request-id": "ff-b", + "source": "c", + "destination": "f", + "src-tp-id": "trx c", + "dst-tp-id": "trx f", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": null, + "output-power": 0.001, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "10-z", + "source": "a", + "destination": "g", + "src-tp-id": "trx a", + "dst-tp-id": "trx g", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 75000000000.0, + "max-nb-of-channel": 63, + "output-power": null, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "11 g", + "source": "a", + "destination": "h", + "src-tp-id": "trx a", + "dst-tp-id": "trx h", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": null, + "output-power": null, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "12<", + "source": "f", + "destination": "b", + "src-tp-id": "trx f", + "dst-tp-id": "trx b", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 75000000000.0, + "max-nb-of-channel": null, + "output-power": null, + "path_bandwidth": null + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "12>", + "source": "f", + "destination": "b", + "src-tp-id": "trx f", + "dst-tp-id": "trx b", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": null, + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 30000000000.0, + "max-nb-of-channel": null, + "output-power": null, + "path_bandwidth": null + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } } ], "synchronization": [ diff --git a/tests/test_automaticmodefeature.py b/tests/test_automaticmodefeature.py new file mode 100644 index 000000000..0617df3fa --- /dev/null +++ b/tests/test_automaticmodefeature.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# TelecomInfraProject/gnpy/examples +# Module name : test_automaticmodefeature.py +# Version : +# License : BSD 3-Clause Licence +# Copyright (c) 2018, Telecom Infra Project + +""" +@author: esther.lerouzic +checks that empty info on mode, power, nbchannel in service file are supported + uses combination of [mode, pow, nb channel] filled or empty defined in meshTopologyToy_services.json + leading to feasible path or not, and check the propagate and propagate_and_optimize_mode + return the expected based on a reference toy example + +""" + +from pathlib import Path +import pytest +from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch +from gnpy.core.network import load_network, build_network +from examples.path_requests_run import (requests_from_json , correct_route_list , + load_requests , disjunctions_from_json) +from gnpy.core.request import (compute_path_dsjctn, isdisjoint , find_reversed_path, + propagate,propagate_and_optimize_mode) +from gnpy.core.utils import db2lin, lin2db +from gnpy.core.elements import Roadm + +network_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy.json' +service_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_services.json' +result_file_name = Path(__file__).parent.parent / 'tests/data/meshTopologyToy_results.json' +eqpt_library_name = Path(__file__).parent.parent / 'tests/data/eqpt_config.json' + +@pytest.mark.parametrize("net",[network_file_name]) +@pytest.mark.parametrize("eqpt", [eqpt_library_name]) +@pytest.mark.parametrize("serv",[service_file_name]) +@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 3', 'mode 1', 'mode 3', 'nok']]) +def test_automaticmodefeature(net,eqpt,serv,expected_mode): + 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 = [] + 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}') + print(f'with path constraint: {[pathreq.source]+pathreq.nodes_list}') #adding first node to be clearer on the output + + 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, show=False) + else: + total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) + # 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 + From 346f24022a1467db690971b16579b568122677bf Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 18 Dec 2018 10:47:13 +0000 Subject: [PATCH 071/108] small fixes on stdout - indicate when a mode is selected if the selected path does not pass + some reformatting of columns Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 21 +++++++++++++++------ gnpy/core/request.py | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index dcbf45806..1ffac3b4b 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -198,6 +198,13 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): if total_path : if pathreq.baud_rate is not None: total_path = propagate(total_path,pathreq,equipment, show=False) + temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))),2) + if temp_snr01nm < pathreq.OSNR : + msg = f'\tWarning! Request {pathreq.request_id} computed path from {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}\n' +\ + f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n' + print(msg) + logger.warning(msg) + total_path = [] else: total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) # if no baudrate satisfies spacing, no mode is returned and an empty path is returned @@ -339,24 +346,26 @@ def path_result_json(pathresult): end = time.time() print(f'computation time {end-start}') - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode', 'Gbit/s' , 'nb of tsp pairs'] + header = ['req id', ' demand',' snr@bandwidth',' snr@0.1nm',' Receiver minOSNR', ' mode', ' Gbit/s' , ' nb of tsp pairs'] data = [] data.append(header) for i, p in enumerate(propagatedpths): if p: - line = [f'{rqs[i].request_id} {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ + line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] else: - line = [f'{rqs[i].request_id} no path from {rqs[i].source} to {rqs[i].destination} '] + line = [f'{rqs[i].request_id}',f' {rqs[i].source} to {rqs[i].destination} : not feasible '] data.append(line) - col_width = max(len(word) for row in data for word in row[1:]) # padding + 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)) - remainingcols = ''.join(word.ljust(col_width) for word in row[1:]) - print(f'{firstcol} {remainingcols}') + secondcol = ''.join(row[1].ljust(secondcol_width)) + remainingcols = ''.join(word.center(col_width,' ') for word in row[2:]) + print(f'{firstcol} {secondcol} {remainingcols}') if args.output : diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 992864ac0..a47f11221 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -431,13 +431,13 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): return [], None # only get to this point if no baudrate/mode satisfies OSNR requirement # returns the last propagated path and mode - msg = f'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' + 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'Warning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' + msg = f'\tWarning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' print(msg) logger.info(msg) return [], None From 2df500e0275c28c45cd9ab78d3753a0b99e14925 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 18 Dec 2018 12:27:46 +0000 Subject: [PATCH 072/108] cosmetic changes add colors on messages to highlight them add messages explaining program steps update readme and excel user guide with latest fetures Signed-off-by: EstherLerouzic --- Excel_userguide.rst | 19 +++++++++++-------- README.rst | 2 ++ examples/path_requests_run.py | 24 ++++++++++++++++-------- gnpy/core/convert.py | 12 +++++++----- gnpy/core/request.py | 6 +++--- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Excel_userguide.rst b/Excel_userguide.rst index edc535349..5471236e1 100644 --- a/Excel_userguide.rst +++ b/Excel_userguide.rst @@ -189,27 +189,30 @@ Service sheet must contain 11 columns:: route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose? -- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash) +- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash or coma) - **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries) - **Destination** is mandatory. It is the name of the destination node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries) -- **TRX type and mode** are mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation. +- **TRX type ** is mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation. -- **System: spacing ; System: input power (dBm) ; System: nb of channels** are mandatory input defining the system parameters for the propagation simulation. +- **mode** is optional. If not specified, the program will search for the mode of the defined transponder with the highest baudrate fitting within the spacing value. + +- **System: spacing** is mandatory. Spacing is the channel spacing defined in GHz difined for the feasibility propagation simulation, assuming system full load. + +- **System: input power (dBm) ; System: nb of channels** are optional input defining the system parameters for the propagation simulation. - - spacing is the channel spacing defined in GHz - input power is the channel optical input power in dBm - nb of channels is the number of channels to be used for the simulation. - **routing: disjoint from ; routing: path ; routing: is loose?** are optional. - - disjoint from: (work not started) identifies the requests from which this request must be disjoint. It is not used yet - - path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. - - is loose? (in progress) 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable. + - disjoint from: identifies the requests from which this request must be disjoint. If filled it must contain request ids separated by ' | ' + - path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. If filled it must contain ROADM ids separated by ' | '. Exact names are required. + - is loose? 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable. -# to be completed # +- ** path bandwidth** is optional. It is the amount of capacity required between source and destination in Gbit/s. Default value is 0.0 Gbit/s. convert_service_sheet.py ------------------------ diff --git a/README.rst b/README.rst index 6586ec05b..5279f43c4 100644 --- a/README.rst +++ b/README.rst @@ -255,6 +255,8 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `tx_osnr` | (number) | In dB. OSNR out from transponder. | +----------------------+-----------+-----------------------------------------+ +| `cost` | (number) | Arbitrary unit | ++----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 1ffac3b4b..b76b1bfda 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -123,7 +123,6 @@ def disjunctions_from_json(json_data): params['node_diverse'] = snc['svec']['node-diverse'] params['disjunctions_req'] = snc['svec']['request-id-number'] disjunctions_list.append(Disjunction(**params)) - print(disjunctions_list) return disjunctions_list @@ -251,22 +250,22 @@ def correct_route_list(network, pathreqlist): \n\'{n_id}\', replaced with \'{new_n}\'') pathreq.nodes_list[i] = new_n else: - print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') + print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!'+'\x1b[0m') pathreq.nodes_list.remove(n_id) pathreq.loose_list.pop(i) else: - msg = f'could not find node : {n_id} in network topology. Strict constraint can not be applied.' + msg = f'\x1b[1;33;40m'+f'could not find node : {n_id} in network topology. Strict constraint can not be applied.'+'\x1b[0m' logger.critical(msg) raise ValueError(msg) if pathreq.source not in transponders: - msg = f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.' + msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.'+'\x1b[0m' logger.critical(msg) print(f'{msg}\nComputation stopped.') exit() if pathreq.destination not in transponders: - msg = f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.' + msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.'+'\x1b[0m' logger.critical(msg) print(f'{msg}\nComputation stopped.') exit() @@ -296,6 +295,7 @@ def path_result_json(pathresult): args = parser.parse_args() basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG)) logger.info(f'Computing path requests {args.service_filename} into JSON format') + print('\x1b[1;34;40m'+f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') # for debug # print( args.eqpt_filename) data = load_requests(args.service_filename,args.eqpt_filename) @@ -327,24 +327,31 @@ def path_result_json(pathresult): # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) - # print('ohohoho') - # print(dsjn) + + print('\x1b[1;34;40m'+f'List of disjunctions'+ '\x1b[0m') + print(dsjn) # need to warn or correct in case of wrong disjunction form # disjunction must not be repeated with same or different ids dsjn = correct_disjn(dsjn) # Aggregate demands with same exact constraints + print('\x1b[1;34;40m'+f'Aggregating similar requests'+ '\x1b[0m') + rqs,dsjn = requests_aggregation(rqs,dsjn) # TODO export novel set of aggregated demands in a json file - print('WARNING: The following services have been requested:') + print('\x1b[1;34;40m'+'The following services have been requested:'+ '\x1b[0m') print(rqs) + print('\x1b[1;34;40m'+f'Computing all paths with constraints'+ '\x1b[0m') pths = compute_path_dsjctn(network, equipment, rqs, dsjn) + + print('\x1b[1;34;40m'+f'Propagating on selected path'+ '\x1b[0m') propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) end = time.time() print(f'computation time {end-start}') + print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') header = ['req id', ' demand',' snr@bandwidth',' snr@0.1nm',' Receiver minOSNR', ' mode', ' Gbit/s' , ' nb of tsp pairs'] data = [] @@ -379,4 +386,5 @@ def path_result_json(pathresult): fnamecsv = next(s for s in args.output.split('.')) + '.csv' with open(fnamecsv,"w", encoding='utf-8') as fcsv : jsontocsv(temp,equipment,fcsv) + print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index b356e03e4..d37d6c79f 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -81,19 +81,21 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): and (test_links == [] or test_links ==[''])\ and (test_eqpts == [] or test_eqpts ==['']) except AssertionError: - print(f'!names in Nodes and Links sheets do no match, check:\ + print(f'\x1b[1;31;40m'+ f'!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') + \n{test_eqpts} in Eqpt sheet'+ '\x1b[0m') exit(1) for city,link in links_by_city.items(): - if nodes_by_city[city].node_type.lower()=='ila' and len(link) != 2: + if (nodes_by_city[city].node_type.lower()=='ila' or nodes_by_city[city].node_type.lower()=='fused') 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') + msg = f'\x1b[1;33;40m'+ f'\n\tInvalid node type ({nodes_by_city[city].node_type}) '+\ + f'specified in {city}, replaced by ROADM'+ '\x1b[0m' + print(msg) + # TODO create a logger ? nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM') nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes] return nodes diff --git a/gnpy/core/request.py b/gnpy/core/request.py index a47f11221..ba05e425e 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -291,7 +291,7 @@ def compute_constrained_path(network, req): try : total_path = dijkstra_path(network, source, destination) except NetworkXNoPath: - msg = f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology' + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology'+ '\x1b[0m' logger.critical(msg) print(msg) total_path = [] @@ -309,11 +309,11 @@ def compute_constrained_path(network, req): total_path = candidate[0] else: if req.loose_list[req.nodes_list.index(n)] == 'loose': - print(f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology') + print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology'+ '\x1b[0m') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination) else: - msg = f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed' + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed'+ '\x1b[0m' logger.critical(msg) print(msg) total_path = [] From 96f3d5a8050101f51819c46e60312a4c33a30742 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 21 Dec 2018 10:49:08 +0000 Subject: [PATCH 073/108] Adding min_spacing parameter in transponder mode library with this feature, spacing is checked with respect to eqpt library instead of hard coded values in equipment + mode selection is based on the minspacing instead of a coef or margin on baudrate value some sanity check are introduced: - request spacing must be greater than min spacing - tsp baudrate must be smaller than min spacing Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 7 ++ examples/meshTopologyExampleV2.xls | Bin 14848 -> 15360 bytes examples/meshTopologyExampleV2_services.json | 87 ++++++++++++++++++- examples/path_requests_run.py | 57 ++++++------ gnpy/core/equipment.py | 13 ++- gnpy/core/request.py | 8 +- tests/data/eqpt_config.json | 9 ++ tests/test_automaticmodefeature.py | 2 +- 8 files changed, 146 insertions(+), 37 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 4654cddd3..056b4b9e3 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -102,6 +102,7 @@ "OSNR": 11, "bit_rate":100e9, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }], "Transceiver":[ @@ -120,6 +121,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }, { @@ -129,6 +131,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] @@ -147,6 +150,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }, { @@ -156,6 +160,7 @@ "bit_rate": 300e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 62.5e9, "cost":1 }, { @@ -165,6 +170,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -174,6 +180,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] diff --git a/examples/meshTopologyExampleV2.xls b/examples/meshTopologyExampleV2.xls index ab72a957dea0dfc90573bc450b271184e07b7213..eb75594aeeae542470e058166f3cf6a1315e2e1e 100644 GIT binary patch delta 683 zcmZ9IJuC!46vzLwx4U*NX)m*p~hB`VQr6rvD~L?IH+o88N?Hrbi^zc=sw-hZ~TTkHERRt$wN3e=o-#T2`m0^j+3 zUjDU(bDiAX73I=s8?j`8hQX!jnblob*r@aa1G9^BUf{#)siAjeIXk|KGCYWB>nNPi zp6O!7)k@=MG#e;Ehgln-N5F(}7ru3^$r8MNDw zUccyUfRPFpl|rhls6wRrib^GFKPkyGh-c%5l$`_lqoP(C%Lf`B@J#iATwX$)Rte+* zAg9`2Aje-I$6p}-1^(mZWK-#|NB0q?2OKB>%>d900L=i<3;<0JDBs#H0Hs^spjqU- zVn`%Y^PGEc$mAsMacYc-$JEqW%S6a(>p(ZvA$=ViR>fjE9?@UKOXl#NiEXvny-J@e Q%oc0yJIa*fezxnOAF8x+B>(^b delta 300 zcmZpuXegOrz$?W71p*8V;u~$HnMD8n|NkF^C4mwkS{g)e44llkiN%1^#E%!Go|VBb zKP9!em4TO`Oavm}lbM$d7WfR(094^xSWpt*%D~TXZE_0lEOuqqn?PZo&8&QKtgQbT zxPVMu;V5xNuE`x*zCiMtmiy*N?E{RH)peKf)-iE0Z~)EFV$fq`nEXID*9auS2@_#p zWMTvI*nsj7adsfO2;Z<$Mh?VlN(udHXktE!o(;sxzwVW>6pgmUlwf40Lns1G5`Po diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 3fef7f95d..cdce3cc0e 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -47,7 +47,7 @@ "spacing": 50000000000.0, "max-nb-of-channel": null, "output-power": 0.0012589254117941673, - "path_bandwidth": 0 + "path_bandwidth": 200000000000.0 } }, "optimizations": { @@ -161,7 +161,7 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": null, - "output-power": null, + "output-power": 0.0019952623149688794, "path_bandwidth": 150000000000.0 } }, @@ -188,13 +188,94 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 63, - "output-power": null, + "output-power": 0.0019952623149688794, "path_bandwidth": 20000000000.0 } }, "optimizations": { "explicit-route-include-objects": [] } + }, + { + "request-id": "6", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 76, + "output-power": 0.001, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "7", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 76, + "output-power": 0.001, + "path_bandwidth": 400000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "7b", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 75000000000.0, + "max-nb-of-channel": 50, + "output-power": 0.001, + "path_bandwidth": 400000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } } ], "synchronization": [ diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index dcbf45806..7f090c8ef 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -69,42 +69,19 @@ def requests_from_json(json_data,equipment): # 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['tx_osnr']) + # print(trx_params['min_spacing']) # optical power might be set differently in the request. if it is indicated then the # params['power'] is updated if req['path-constraints']['te-bandwidth']['output-power']: params['power'] = req['path-constraints']['te-bandwidth']['output-power'] # same process for nb-channel - fmin = trx_params['frequency']['min'] - fmax = trx_params['frequency']['max'] + fmin = params['frequency']['min'] + fmax = params['frequency']['max'] if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : - # check if requested nb_channels is consistant with baudrate and min-max frequencies - if trx_params['baud_rate'] is not None: - min_recommanded_spacing = automatic_spacing(trx_params['baud_rate']) - # needed for printing - else argument with quote are making errors in the print - temp = params['baud_rate']*1e-9 - else: - min_recommanded_spacing = params['spacing'] - temp = 'undetermined baudrate' - - max_recommanded_nb_channels = automatic_nch(fmin,fmax, - min_recommanded_spacing) - - if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] <= max_recommanded_nb_channels : - params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] - else: - msg = dedent(f''' - Requested channel number is not consistent with frequency range: - {fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp} GHz - min recommanded spacing is {min_recommanded_spacing} - max recommanded nb of channels is {max_recommanded_nb_channels} - Computation stopped.''') - logger.critical(msg) - exit() - - + params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] else : params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) + consitency_check(params) try : params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] except KeyError: @@ -112,6 +89,30 @@ def requests_from_json(json_data,equipment): requests_list.append(Path_request(**params)) return requests_list +def consitency_check(params): + fmin = params['frequency']['min'] + fmax = params['frequency']['max'] + max_recommanded_nb_channels = automatic_nch(fmin,fmax, + params['spacing']) + if params['baud_rate'] is not None: + #implicitely means that a mode is defined with min_spacing + if params['min_spacing']>params['spacing'] : + msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\ + f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\ + 'Computation stopped' + print(msg) + logger.critical(msg) + exit() + if params['nb_channel']>max_recommanded_nb_channels: + msg = dedent(f''' + Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz + is not consistent with frequency range {fmin*1e-12} THz, {fmax*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. + max recommanded nb of channels is {max_recommanded_nb_channels} + Computation stopped.''') + logger.critical(msg) + exit() + + def disjunctions_from_json(json_data): disjunctions_list = [] diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 5dfa34fe8..f5c17f62b 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db OSNR bit_rate tx_osnr cost') + power_dbm power_range_db OSNR bit_rate tx_osnr min_spacing cost') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -167,6 +167,13 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F if trx == trx_type_variety \ for mode in trxs[trx].mode \ if mode['format'] == trx_mode) + trx_params = {**mode_params} + # sanity check: spacing baudrate must be smaller than min spacing + if trx_params['baud_rate'] > trx_params['min_spacing'] : + msg = f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\ + f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.' + print(msg) + exit() else: mode_params = {"format": "undetermined", "baud_rate": None, @@ -174,8 +181,9 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "bit_rate": None, "roll_off": None, "tx_osnr":None, + "min_spacing":None, "cost":None} - trx_params = {**mode_params} + trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency # TODO: novel automatic feature maybe unwanted if spacing is specified @@ -201,6 +209,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['frequency']['max'], trx_params['spacing']) trx_params['tx_osnr'] = default_si_data.tx_osnr + trx_params['min_spacing'] = default_si_data.min_spacing nch = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], trx_params['spacing']) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 992864ac0..965212fde 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -34,7 +34,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr cost path_bandwidth') +' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -57,6 +57,7 @@ def __init__(self, *args, **params): self.bit_rate = params.bit_rate self.roll_off = params.roll_off self.tx_osnr = params.tx_osnr + self.min_spacing = params.min_spacing self.cost = params.cost self.path_bandwidth = params.path_bandwidth @@ -401,7 +402,8 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1 # 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['baud_rate'])+12.5e9< req.spacing])) + if float(m['min_spacing'])<= req.spacing])) + # TODO be carefull on limits cases if min_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 @@ -424,7 +426,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if show : print(el) if path[-1].snr is not None: - if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : + if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : found_a_feasible_mode = True return path, m else: diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index 7a93b1d1c..8c2b58ecf 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -79,6 +79,7 @@ "OSNR": 15, "bit_rate":100e9, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 }], "Transceiver":[ @@ -96,6 +97,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 }, { @@ -105,6 +107,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 75e9, "cost":1 } ] @@ -123,6 +126,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 } ] @@ -141,6 +145,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 50e9, "cost":1 }, { @@ -150,6 +155,7 @@ "bit_rate": 300e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 62.5e9, "cost":1 }, { @@ -159,6 +165,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -168,6 +175,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -177,6 +185,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] diff --git a/tests/test_automaticmodefeature.py b/tests/test_automaticmodefeature.py index 0617df3fa..d5a2ec7ad 100644 --- a/tests/test_automaticmodefeature.py +++ b/tests/test_automaticmodefeature.py @@ -33,7 +33,7 @@ @pytest.mark.parametrize("net",[network_file_name]) @pytest.mark.parametrize("eqpt", [eqpt_library_name]) @pytest.mark.parametrize("serv",[service_file_name]) -@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 3', 'mode 1', 'mode 3', 'nok']]) +@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']]) def test_automaticmodefeature(net,eqpt,serv,expected_mode): data = load_requests(serv,eqpt) equipment = load_equipment(eqpt) From 3d7362743d78980f1ed2716a85ce6a2cfc57a45d Mon Sep 17 00:00:00 2001 From: David Boertjes Date: Thu, 10 Jan 2019 13:15:59 -0500 Subject: [PATCH 074/108] Update node.py Fix return value error in *coords* property which was intended to build a tuple from two values for latitude and longitude. --- gnpy/core/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnpy/core/node.py b/gnpy/core/node.py index 52265338a..72e9c5102 100644 --- a/gnpy/core/node.py +++ b/gnpy/core/node.py @@ -36,7 +36,7 @@ def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operat @property def coords(self): - return tuple(self.lng, self.lat) + return (self.lng, self.lat) @property def location(self): From aef43e6bcaf47e9a387efbc1d0dc0a47610f356f Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 13 Dec 2018 11:28:42 +0000 Subject: [PATCH 075/108] Corrections of some bugs - output file of -o option in path_requests_run.py was not correctly handled in case of path indirection eg ../../bar.foo - fiber constraint is not correctly handled in case of several parrallel directions of same fiber name. remove if from the set of constraint till problem is not solved - loose_list was not correctly indexed in request.py. assume the first element attribute (except of the transceiver) applies for the whole list. This will need further corrections - handle the case when path.snr is none in the optimization of mode process --- examples/path_requests_run.py | 14 ++++++++------ gnpy/core/request.py | 3 ++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index dcbf45806..c228c327c 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -25,7 +25,7 @@ from gnpy.core.utils import load_json from gnpy.core.network import load_network, build_network, set_roadm_loss, save_network from gnpy.core.equipment import load_equipment, trx_mode_params, automatic_nch, automatic_spacing -from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused +from gnpy.core.elements import Transceiver, Roadm, Edfa, Fused, Fiber from gnpy.core.utils import db2lin, lin2db from gnpy.core.request import (Path_request, Result_element, compute_constrained_path, propagate, jsontocsv, Disjunction, compute_path_dsjctn, requests_aggregation, @@ -44,7 +44,7 @@ parser.add_argument('service_filename', nargs='?', type = Path, default= Path(__file__).parent / 'meshTopologyExampleV2.xls') parser.add_argument('eqpt_filename', nargs='?', type = Path, default=Path(__file__).parent / 'eqpt_config.json') parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') -parser.add_argument('-o', '--output') +parser.add_argument('-o', '--output', type = Path) def requests_from_json(json_data,equipment): @@ -228,7 +228,9 @@ def correct_route_list(network, pathreqlist): # prepares the format of route list of nodes to be consistant # remove wrong names, remove endpoints # also correct source and destination - anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver)] + anytype = [n.uid for n in network.nodes() if not isinstance(n, Transceiver) and not isinstance(n, Fiber)] + # TODO there is a problem of identification of fibers in case of parallel fibers bitween two adjacent roadms + # so fiber constraint is not supported transponders = [n.uid for n in network.nodes() if isinstance(n, Transceiver)] for pathreq in pathreqlist: for i,n_id in enumerate(pathreq.nodes_list): @@ -251,7 +253,6 @@ def correct_route_list(network, pathreqlist): msg = f'could not find node : {n_id} in network topology. Strict constraint can not be applied.' logger.critical(msg) raise ValueError(msg) - if pathreq.source not in transponders: msg = f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.' logger.critical(msg) @@ -365,9 +366,10 @@ def path_result_json(pathresult): for i,p in enumerate(propagatedpths): result.append(Result_element(rqs[i],p)) temp = path_result_json(result) - with open(args.output, 'w', encoding='utf-8') as f: + fnamecsv = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.csv' + fnamejson = f'{str(args.output)[0:len(str(args.output))-len(str(args.output.suffix))]}.json' + with open(fnamejson, 'w', encoding='utf-8') as f: f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) - fnamecsv = next(s for s in args.output.split('.')) + '.csv' with open(fnamecsv,"w", encoding='utf-8') as fcsv : jsontocsv(temp,equipment,fcsv) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 992864ac0..41a899132 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -308,7 +308,7 @@ def compute_constrained_path(network, req): candidate.sort(key=lambda x: len(x)) total_path = candidate[0] else: - if req.loose_list[req.nodes_list.index(n)] == 'loose': + if req.loose_list[1] == 'loose': print(f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination) @@ -430,6 +430,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): else: return [], None # only get to this point if no baudrate/mode satisfies OSNR requirement + # returns the last propagated path and mode msg = f'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' print(msg) From b258d22d2543dbf262d4bd0551a8b4e816610751 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Thu, 13 Dec 2018 11:37:26 +0000 Subject: [PATCH 076/108] add a limit cutoff for path search all_simple_path search leads to a very long time process in case of large network a cutoff parameters has been added to avoid this. Value needs to be parametrized: right now correspond to my experience Signed-off-by: EstherLerouzic --- gnpy/core/request.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 41a899132..dc18610f4 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -276,6 +276,7 @@ def compute_constrained_path(network, req): destination = next(el for el in trx if el.uid == req.destination) nodes_list = [] for n in req.nodes_list : + # for debug excel print(n) nodes_list.append(next(el for el in anytypenode if el.uid == n)) # nodes_list contains at least the destination if nodes_list is None : @@ -297,7 +298,7 @@ def compute_constrained_path(network, req): total_path = [] else : all_simp_pths = list(all_simple_paths(network,source=source,\ - target=destination)) + target=destination, cutoff=120)) candidate = [] for p in all_simp_pths : if ispart(nodes_list, p) : @@ -466,7 +467,7 @@ def jsontocsv(json_data,equipment,fileout): # selects only roadm nodes pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] for e in p['path-properties']['path-route-objects'] - if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm')]) + if e['path-route-object']['unnumbered-hop']['node-id'].startswith('roadm') or e['path-route-object']['unnumbered-hop']['node-id'].startswith('Edfa')]) [tsp,mode] = p['path-properties']['path-route-objects'][0]\ ['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') From 4c1c17eea64df380e9124ca33070c419ae1524b5 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Wed, 19 Dec 2018 10:49:31 +0000 Subject: [PATCH 077/108] adding some exception handling to help user debugging exception handling add some info on which element (uid) is causing a problem this can help the user to identify where he must correct the input files Signed-off-by: EstherLerouzic --- gnpy/core/convert.py | 13 +++++++++---- gnpy/core/network.py | 32 +++++++++++++++++++++++++++----- gnpy/core/request.py | 3 ++- 3 files changed, 38 insertions(+), 10 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index b356e03e4..f27436d46 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -285,10 +285,15 @@ def eqpt_connection_by_city(city_name): # Then len(other_cities) == 2 direction = ['ingress', 'egress'] for i in range(2): - from_ = fiber_link(other_cities[i], city_name) - in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) - to_ = fiber_link(city_name, other_cities[1-i]) - subdata += connect_eqpt(from_, in_, to_) + try: + from_ = fiber_link(other_cities[i], city_name) + in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) + to_ = fiber_link(city_name, other_cities[1-i]) + subdata += connect_eqpt(from_, in_, to_) + except IndexError: + msg = f'In {__name__} eqpt_connection_by_city:\n\t{city_name} is not properly connected' + print(msg) + exit(1) elif nodes_by_city[city_name].node_type.lower() == 'roadm': for other_city in other_cities: from_ = f'roadm {city_name}' diff --git a/gnpy/core/network.py b/gnpy/core/network.py index e14947459..dbd6523d3 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -64,7 +64,12 @@ def network_from_json(json_data, equipment): for cx in json_data['connections']: from_node, to_node = cx['from_node'], cx['to_node'] - g.add_edge(nodes[from_node], nodes[to_node]) + try: + g.add_edge(nodes[from_node], nodes[to_node]) + except KeyError: + msg = f'In {__name__} network_from_json function:\n\tcan not find {from_node} or {to_node} defined in {cx}' + print(msg) + exit(1) return g @@ -171,7 +176,13 @@ def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp def prev_node_generator(network, node): """fused spans interest: iterate over all predecessors while they are Fused or Fiber type""" - prev_node = next(n for n in network.predecessors(node)) + try: + prev_node = next(n for n in network.predecessors(node)) + except StopIteration: + msg = f'In {__name__} prev_node_generator function:\n\t{node.uid} is not properly connected, please check network topology' + print(msg) + logger.critical(msg) + exit(1) # yield and re-iterate if isinstance(prev_node, Fused) or isinstance(node, Fused): yield prev_node @@ -182,7 +193,11 @@ def prev_node_generator(network, node): def next_node_generator(network, node): """fused spans interest: iterate over all successors while they are Fused or Fiber type""" - next_node = next(n for n in network.successors(node)) + try: + next_node = next(n for n in network.successors(node)) + except StopIteration: + print(f'In {__name__} next_node_generator function:\n\t{node.uid} is not properly connected, please check network topology') + exit(1) # yield and re-iterate if isinstance(next_node, Fused) or isinstance(node, Fused): yield next_node @@ -334,7 +349,8 @@ def split_fiber(network, fiber, bounds, target_length, equipment): next_node = next(network.successors(fiber)) prev_node = next(network.predecessors(fiber)) except StopIteration: - print(f'{repr(fiber)} is not properly connected, please check network topology') + + print(f'In {__name__} split_fiber function:\n\t{fiber.uid} is not properly connected, please check network topology') exit() network.remove_node(fiber) @@ -367,7 +383,13 @@ def add_fiber_padding(network, fibers, padding): if isinstance(fiber, Fiber))""" for fiber in fibers: this_span_loss = span_loss(network, fiber) - next_node = next(network.successors(fiber)) + try: + next_node = next(network.successors(fiber)) + except StopIteration: + msg = f'In {__name__} add_fiber_padding function:\n\t{fiber.uid} is not properly connected, please check network topology' + print(msg) + logger.critical(msg) + exit(1) if this_span_loss < padding and not (isinstance(next_node, Fused)): #add a padding att_in at the input of the 1st fiber: #address the case when several fibers are spliced together diff --git a/gnpy/core/request.py b/gnpy/core/request.py index dc18610f4..7199aa7c8 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -591,7 +591,8 @@ def __init__(self, req, pth, simplepth): for pathreq in pathreqlist_disjt : all_simp_pths = list(all_simple_paths(network,\ source=next(el for el in network.nodes() if el.uid == pathreq.source),\ - target=next(el for el in network.nodes() if el.uid == pathreq.destination))) + target=next(el for el in network.nodes() if el.uid == pathreq.destination),\ + cutoff=80)) # sort them all_simp_pths = sorted(all_simp_pths, key=lambda path: len(path)) # reversed direction paths required to check disjunction on both direction From 03948d6785928e4131c65bb2a55ad39e347fac25 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 14 Dec 2018 18:06:03 +0000 Subject: [PATCH 078/108] Fixes concerning service sheet reading - indicate which request has incorrect tsp type or mode - correct wrong reading from excel : if string values contain only a number xlrd interprets as a float eg 1 -> 1.0 . A function is used to correct this when filling the Request class Signed-off-by: EstherLerouzic --- gnpy/core/service_sheet.py | 40 ++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/gnpy/core/service_sheet.py b/gnpy/core/service_sheet.py index 9af3abf15..3759e07e1 100644 --- a/gnpy/core/service_sheet.py +++ b/gnpy/core/service_sheet.py @@ -47,13 +47,7 @@ def __init__(self,Request,eqpt_filename): # request_id is str # excel has automatic number formatting that adds .0 on integer values # the next lines recover the pure int value, assuming this .0 is unwanted - if not isinstance(Request.request_id,str): - value = str(int(Request.request_id)) - if value.endswith('.0'): - value = value[:-2] - self.request_id = value - else: - self.request_id = Request.request_id + self.request_id = correct_xlrd_int_to_str_reading(Request.request_id) self.source = Request.source self.destination = Request.destination # TODO: the automatic naming generated by excel parser requires that source and dest name @@ -65,14 +59,21 @@ def __init__(self,Request,eqpt_filename): equipment = load_equipment(eqpt_filename) try : if equipment['Transceiver'][Request.trx_type]: - self.trx_type = Request.trx_type + self.trx_type = correct_xlrd_int_to_str_reading(Request.trx_type) if Request.mode is not None : - if [mode for mode in equipment['Transceiver'][Request.trx_type].mode]: - self.mode = Request.mode + Requestmode = correct_xlrd_int_to_str_reading(Request.mode) + if [mode for mode in equipment['Transceiver'][Request.trx_type].mode if mode['format'] == Requestmode]: + self.mode = Requestmode + else : + msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.' + #print(msg) + logger.critical(msg) + exit(1) else: + Requestmode = None self.mode = Request.mode except KeyError: - msg = f'could not find tsp : {Request.trx_type} with mode: {Request.mode} in eqpt library \nComputation stopped.' + msg = f'Request Id: {self.request_id} - could not find tsp : \'{Request.trx_type}\' with mode: \'{Requestmode}\' in eqpt library \nComputation stopped.' #print(msg) logger.critical(msg) exit() @@ -91,12 +92,8 @@ def __init__(self,Request,eqpt_filename): self.nb_channel = int(Request.nb_channel) else: self.nb_channel = None - if not isinstance(Request.disjoint_from,str): - value = str(int(Request.disjoint_from)) - if value.endswith('.0'): - value = value[:-2] - else: - value = Request.disjoint_from + + value = correct_xlrd_int_to_str_reading(Request.disjoint_from) self.disjoint_from = [n for n in value.split(' | ') if value] self.nodes_list = [] if Request.nodes_list : @@ -213,6 +210,15 @@ def convert_service_sheet(input_filename, eqpt_filename, output_filename='', fil f.write(dumps(data, indent=2, ensure_ascii=False)) return data +def correct_xlrd_int_to_str_reading(v) : + if not isinstance(v,str): + value = str(int(v)) + if value.endswith('.0'): + value = value[:-2] + else: + value = v + return value + # to be used from dutc def parse_row(row, fieldnames): return {f: r.value for f, r in zip(fieldnames, row[0:SERVICES_COLUMN]) From 2548a2eee8723fc54b1fc5b627d7aa8a53839d37 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 11 Jan 2019 10:55:01 +0000 Subject: [PATCH 079/108] small fix on test eqpt_config file Signed-off-by: EstherLerouzic --- tests/data/eqpt_config.json | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index 7a93b1d1c..1d232e877 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -106,7 +106,25 @@ "roll_off": 0.15, "tx_osnr": 100, "cost":1 - } + }, + { + "format": "mode 1", + "baud_rate": 32e9, + "OSNR": 11, + "bit_rate": 100e9, + "roll_off": 0.15, + "tx_osnr": 100, + "cost":1 + }, + { + "format": "mode 2", + "baud_rate": 64e9, + "OSNR": 15, + "bit_rate": 200e9, + "roll_off": 0.15, + "tx_osnr": 100, + "cost":1 + } ] }, { From dd4ce4cea4b1ea4679944a60944accdecc1f51f6 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 18 Dec 2018 10:47:13 +0000 Subject: [PATCH 080/108] small fixes on stdout - indicate when a mode is selected if the selected path does not pass + some reformatting of columns Signed-off-by: EstherLerouzic --- examples/path_requests_run.py | 21 +++++++++++++++------ gnpy/core/request.py | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index c228c327c..79d1799e8 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -198,6 +198,13 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): if total_path : if pathreq.baud_rate is not None: total_path = propagate(total_path,pathreq,equipment, show=False) + temp_snr01nm = round(mean(total_path[-1].snr+lin2db(pathreq.baud_rate/(12.5e9))),2) + if temp_snr01nm < pathreq.OSNR : + msg = f'\tWarning! Request {pathreq.request_id} computed path from {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}\n' +\ + f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}\n' + print(msg) + logger.warning(msg) + total_path = [] else: total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) # if no baudrate satisfies spacing, no mode is returned and an empty path is returned @@ -340,24 +347,26 @@ def path_result_json(pathresult): end = time.time() print(f'computation time {end-start}') - header = ['demand','snr@bandwidth','snr@0.1nm','Receiver minOSNR', 'mode', 'Gbit/s' , 'nb of tsp pairs'] + header = ['req id', ' demand',' snr@bandwidth',' snr@0.1nm',' Receiver minOSNR', ' mode', ' Gbit/s' , ' nb of tsp pairs'] data = [] data.append(header) for i, p in enumerate(propagatedpths): if p: - line = [f'{rqs[i].request_id} {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ + line = [f'{rqs[i].request_id}', f' {rqs[i].source} to {rqs[i].destination} : ', f'{round(mean(p[-1].snr),2)}',\ f'{round(mean(p[-1].snr+lin2db(rqs[i].baud_rate/(12.5e9))),2)}',\ f'{rqs[i].OSNR}', f'{rqs[i].tsp_mode}' , f'{round(rqs[i].path_bandwidth * 1e-9,2)}' , f'{ceil(rqs[i].path_bandwidth / rqs[i].bit_rate) }'] else: - line = [f'{rqs[i].request_id} no path from {rqs[i].source} to {rqs[i].destination} '] + line = [f'{rqs[i].request_id}',f' {rqs[i].source} to {rqs[i].destination} : not feasible '] data.append(line) - col_width = max(len(word) for row in data for word in row[1:]) # padding + 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)) - remainingcols = ''.join(word.ljust(col_width) for word in row[1:]) - print(f'{firstcol} {remainingcols}') + secondcol = ''.join(row[1].ljust(secondcol_width)) + remainingcols = ''.join(word.center(col_width,' ') for word in row[2:]) + print(f'{firstcol} {secondcol} {remainingcols}') if args.output : diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 7199aa7c8..56856a69b 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -433,13 +433,13 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # only get to this point if no baudrate/mode satisfies OSNR requirement # returns the last propagated path and mode - msg = f'Warning! Request {req.request_id}: no mode satisfies path SNR requirement.\n' + 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'Warning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' + msg = f'\tWarning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' print(msg) logger.info(msg) return [], None From be731a5977f548a9bf4589bd750be88ae0b8d3e1 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Tue, 18 Dec 2018 12:27:46 +0000 Subject: [PATCH 081/108] cosmetic changes add colors on messages to highlight them add messages explaining program steps update readme and excel user guide with latest fetures Signed-off-by: EstherLerouzic --- Excel_userguide.rst | 19 +++++++++++-------- README.rst | 2 ++ examples/path_requests_run.py | 24 ++++++++++++++++-------- gnpy/core/convert.py | 12 +++++++----- gnpy/core/request.py | 6 +++--- 5 files changed, 39 insertions(+), 24 deletions(-) diff --git a/Excel_userguide.rst b/Excel_userguide.rst index edc535349..5471236e1 100644 --- a/Excel_userguide.rst +++ b/Excel_userguide.rst @@ -189,27 +189,30 @@ Service sheet must contain 11 columns:: route id ; Source ; Destination ; TRX type ; Mode ; System: spacing ; System: input power (dBm) ; System: nb of channels ; routing: disjoint from ; routing: path ; routing: is loose? -- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash) +- **route id** is mandatory. It must be unique. It is the identifier of the request. It can be an integer or a string (do not use blank or dash or coma) - **Source** is mandatory. It is the name of the source node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries) - **Destination** is mandatory. It is the name of the destination node (as listed in Nodes sheet). Source MUST be a ROADM node. (TODO: relax this and accept trx entries) -- **TRX type and mode** are mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation. +- **TRX type ** is mandatory. They are the variety type and selected mode of the transceiver to be used for the propagation simulation. These modes MUST be defined in the equipment library. The format of the mode is used as the name of the mode. (TODO: maybe add another mode id on Transceiver library ?). In particular the mode selection defines the channel baudrate to be used for the propagation simulation. -- **System: spacing ; System: input power (dBm) ; System: nb of channels** are mandatory input defining the system parameters for the propagation simulation. +- **mode** is optional. If not specified, the program will search for the mode of the defined transponder with the highest baudrate fitting within the spacing value. + +- **System: spacing** is mandatory. Spacing is the channel spacing defined in GHz difined for the feasibility propagation simulation, assuming system full load. + +- **System: input power (dBm) ; System: nb of channels** are optional input defining the system parameters for the propagation simulation. - - spacing is the channel spacing defined in GHz - input power is the channel optical input power in dBm - nb of channels is the number of channels to be used for the simulation. - **routing: disjoint from ; routing: path ; routing: is loose?** are optional. - - disjoint from: (work not started) identifies the requests from which this request must be disjoint. It is not used yet - - path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. - - is loose? (in progress) 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable. + - disjoint from: identifies the requests from which this request must be disjoint. If filled it must contain request ids separated by ' | ' + - path: is the set of ROADM nodes that must be used by the path. It must contain the list of ROADM names that the path must cross. TODO : only ROADM nodes are accepted in this release. Relax this with any type of nodes. If filled it must contain ROADM ids separated by ' | '. Exact names are required. + - is loose? 'no' value means that the list of nodes should be strictly followed, while any other value means that the constraint may be relaxed if the node is not reachable. -# to be completed # +- ** path bandwidth** is optional. It is the amount of capacity required between source and destination in Gbit/s. Default value is 0.0 Gbit/s. convert_service_sheet.py ------------------------ diff --git a/README.rst b/README.rst index 6586ec05b..5279f43c4 100644 --- a/README.rst +++ b/README.rst @@ -255,6 +255,8 @@ The modes are defined as follows: +----------------------+-----------+-----------------------------------------+ | `tx_osnr` | (number) | In dB. OSNR out from transponder. | +----------------------+-----------+-----------------------------------------+ +| `cost` | (number) | Arbitrary unit | ++----------------------+-----------+-----------------------------------------+ Simulation parameters are defined as follows. diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 79d1799e8..40ed37e66 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -123,7 +123,6 @@ def disjunctions_from_json(json_data): params['node_diverse'] = snc['svec']['node-diverse'] params['disjunctions_req'] = snc['svec']['request-id-number'] disjunctions_list.append(Disjunction(**params)) - print(disjunctions_list) return disjunctions_list @@ -253,21 +252,21 @@ def correct_route_list(network, pathreqlist): \n\'{n_id}\', replaced with \'{new_n}\'') pathreq.nodes_list[i] = new_n else: - print(f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!') + print(f'\x1b[1;33;40m'+f'invalid route node specified \'{n_id}\', could not use it as constraint, skipped!'+'\x1b[0m') pathreq.nodes_list.remove(n_id) pathreq.loose_list.pop(i) else: - msg = f'could not find node : {n_id} in network topology. Strict constraint can not be applied.' + msg = f'\x1b[1;33;40m'+f'could not find node : {n_id} in network topology. Strict constraint can not be applied.'+'\x1b[0m' logger.critical(msg) raise ValueError(msg) if pathreq.source not in transponders: - msg = f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.' + msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder source : {pathreq.source}.'+'\x1b[0m' logger.critical(msg) print(f'{msg}\nComputation stopped.') exit() if pathreq.destination not in transponders: - msg = f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.' + msg = f'\x1b[1;31;40m'+f'Request: {pathreq.request_id}: could not find transponder destination : {pathreq.destination}.'+'\x1b[0m' logger.critical(msg) print(f'{msg}\nComputation stopped.') exit() @@ -297,6 +296,7 @@ def path_result_json(pathresult): args = parser.parse_args() basicConfig(level={2: DEBUG, 1: INFO, 0: CRITICAL}.get(args.verbose, DEBUG)) logger.info(f'Computing path requests {args.service_filename} into JSON format') + print('\x1b[1;34;40m'+f'Computing path requests {args.service_filename} into JSON format'+ '\x1b[0m') # for debug # print( args.eqpt_filename) data = load_requests(args.service_filename,args.eqpt_filename) @@ -328,24 +328,31 @@ def path_result_json(pathresult): # pths = compute_path(network, equipment, rqs) dsjn = disjunctions_from_json(data) - # print('ohohoho') - # print(dsjn) + + print('\x1b[1;34;40m'+f'List of disjunctions'+ '\x1b[0m') + print(dsjn) # need to warn or correct in case of wrong disjunction form # disjunction must not be repeated with same or different ids dsjn = correct_disjn(dsjn) # Aggregate demands with same exact constraints + print('\x1b[1;34;40m'+f'Aggregating similar requests'+ '\x1b[0m') + rqs,dsjn = requests_aggregation(rqs,dsjn) # TODO export novel set of aggregated demands in a json file - print('WARNING: The following services have been requested:') + print('\x1b[1;34;40m'+'The following services have been requested:'+ '\x1b[0m') print(rqs) + print('\x1b[1;34;40m'+f'Computing all paths with constraints'+ '\x1b[0m') pths = compute_path_dsjctn(network, equipment, rqs, dsjn) + + print('\x1b[1;34;40m'+f'Propagating on selected path'+ '\x1b[0m') propagatedpths = compute_path_with_disjunction(network, equipment, rqs, pths) end = time.time() print(f'computation time {end-start}') + print('\x1b[1;34;40m'+f'Result summary'+ '\x1b[0m') header = ['req id', ' demand',' snr@bandwidth',' snr@0.1nm',' Receiver minOSNR', ' mode', ' Gbit/s' , ' nb of tsp pairs'] data = [] @@ -381,4 +388,5 @@ def path_result_json(pathresult): f.write(dumps(path_result_json(result), indent=2, ensure_ascii=False)) with open(fnamecsv,"w", encoding='utf-8') as fcsv : jsontocsv(temp,equipment,fcsv) + print('\x1b[1;34;40m'+f'saving in {args.output} and {fnamecsv}'+ '\x1b[0m') diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index f27436d46..f092d9de2 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -81,19 +81,21 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): and (test_links == [] or test_links ==[''])\ and (test_eqpts == [] or test_eqpts ==['']) except AssertionError: - print(f'!names in Nodes and Links sheets do no match, check:\ + print(f'\x1b[1;31;40m'+ f'!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') + \n{test_eqpts} in Eqpt sheet'+ '\x1b[0m') exit(1) for city,link in links_by_city.items(): - if nodes_by_city[city].node_type.lower()=='ila' and len(link) != 2: + if (nodes_by_city[city].node_type.lower()=='ila' or nodes_by_city[city].node_type.lower()=='fused') 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') + msg = f'\x1b[1;33;40m'+ f'\n\tInvalid node type ({nodes_by_city[city].node_type}) '+\ + f'specified in {city}, replaced by ROADM'+ '\x1b[0m' + print(msg) + # TODO create a logger ? nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM') nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes] return nodes diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 56856a69b..c2f91bab5 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -292,7 +292,7 @@ def compute_constrained_path(network, req): try : total_path = dijkstra_path(network, source, destination) except NetworkXNoPath: - msg = f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology' + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path from {source.uid} to node : {destination.uid} in network topology'+ '\x1b[0m' logger.critical(msg) print(msg) total_path = [] @@ -310,11 +310,11 @@ def compute_constrained_path(network, req): total_path = candidate[0] else: if req.loose_list[1] == 'loose': - print(f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology') + print(f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list} in network topology'+ '\x1b[0m') print(f'constraint ignored') total_path = dijkstra_path(network, source, destination) else: - msg = f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed' + msg = f'\x1b[1;33;40m'+f'Request {req.request_id} could not find a path crossing {nodes_list}.\nNo path computed'+ '\x1b[0m' logger.critical(msg) print(msg) total_path = [] From 63f8139dbcf64bf1f3639c218639ed2c510e17f0 Mon Sep 17 00:00:00 2001 From: EstherLerouzic Date: Fri, 21 Dec 2018 10:49:08 +0000 Subject: [PATCH 082/108] Adding min_spacing parameter in transponder mode library with this feature, spacing is checked with respect to eqpt library instead of hard coded values in equipment + mode selection is based on the minspacing instead of a coef or margin on baudrate value some sanity check are introduced: - request spacing must be greater than min spacing - tsp baudrate must be smaller than min spacing Signed-off-by: EstherLerouzic --- examples/eqpt_config.json | 7 ++ examples/meshTopologyExampleV2.xls | Bin 14848 -> 15360 bytes examples/meshTopologyExampleV2_services.json | 87 ++++++++++++++++++- examples/path_requests_run.py | 57 ++++++------ gnpy/core/equipment.py | 13 ++- gnpy/core/request.py | 8 +- tests/data/eqpt_config.json | 11 +++ tests/test_automaticmodefeature.py | 2 +- 8 files changed, 148 insertions(+), 37 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 4654cddd3..056b4b9e3 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -102,6 +102,7 @@ "OSNR": 11, "bit_rate":100e9, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }], "Transceiver":[ @@ -120,6 +121,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }, { @@ -129,6 +131,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] @@ -147,6 +150,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 37.5e9, "cost":1 }, { @@ -156,6 +160,7 @@ "bit_rate": 300e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 62.5e9, "cost":1 }, { @@ -165,6 +170,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -174,6 +180,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] diff --git a/examples/meshTopologyExampleV2.xls b/examples/meshTopologyExampleV2.xls index ab72a957dea0dfc90573bc450b271184e07b7213..eb75594aeeae542470e058166f3cf6a1315e2e1e 100644 GIT binary patch delta 683 zcmZ9IJuC!46vzLwx4U*NX)m*p~hB`VQr6rvD~L?IH+o88N?Hrbi^zc=sw-hZ~TTkHERRt$wN3e=o-#T2`m0^j+3 zUjDU(bDiAX73I=s8?j`8hQX!jnblob*r@aa1G9^BUf{#)siAjeIXk|KGCYWB>nNPi zp6O!7)k@=MG#e;Ehgln-N5F(}7ru3^$r8MNDw zUccyUfRPFpl|rhls6wRrib^GFKPkyGh-c%5l$`_lqoP(C%Lf`B@J#iATwX$)Rte+* zAg9`2Aje-I$6p}-1^(mZWK-#|NB0q?2OKB>%>d900L=i<3;<0JDBs#H0Hs^spjqU- zVn`%Y^PGEc$mAsMacYc-$JEqW%S6a(>p(ZvA$=ViR>fjE9?@UKOXl#NiEXvny-J@e Q%oc0yJIa*fezxnOAF8x+B>(^b delta 300 zcmZpuXegOrz$?W71p*8V;u~$HnMD8n|NkF^C4mwkS{g)e44llkiN%1^#E%!Go|VBb zKP9!em4TO`Oavm}lbM$d7WfR(094^xSWpt*%D~TXZE_0lEOuqqn?PZo&8&QKtgQbT zxPVMu;V5xNuE`x*zCiMtmiy*N?E{RH)peKf)-iE0Z~)EFV$fq`nEXID*9auS2@_#p zWMTvI*nsj7adsfO2;Z<$Mh?VlN(udHXktE!o(;sxzwVW>6pgmUlwf40Lns1G5`Po diff --git a/examples/meshTopologyExampleV2_services.json b/examples/meshTopologyExampleV2_services.json index 3fef7f95d..cdce3cc0e 100644 --- a/examples/meshTopologyExampleV2_services.json +++ b/examples/meshTopologyExampleV2_services.json @@ -47,7 +47,7 @@ "spacing": 50000000000.0, "max-nb-of-channel": null, "output-power": 0.0012589254117941673, - "path_bandwidth": 0 + "path_bandwidth": 200000000000.0 } }, "optimizations": { @@ -161,7 +161,7 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": null, - "output-power": null, + "output-power": 0.0019952623149688794, "path_bandwidth": 150000000000.0 } }, @@ -188,13 +188,94 @@ ], "spacing": 75000000000.0, "max-nb-of-channel": 63, - "output-power": null, + "output-power": 0.0019952623149688794, "path_bandwidth": 20000000000.0 } }, "optimizations": { "explicit-route-include-objects": [] } + }, + { + "request-id": "6", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 76, + "output-power": 0.001, + "path_bandwidth": 300000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "7", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 50000000000.0, + "max-nb-of-channel": 76, + "output-power": 0.001, + "path_bandwidth": 400000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } + }, + { + "request-id": "7b", + "source": "Lannion_CAS", + "destination": "Lorient_KMA", + "src-tp-id": "trx Lannion_CAS", + "dst-tp-id": "trx Lorient_KMA", + "path-constraints": { + "te-bandwidth": { + "technology": "flexi-grid", + "trx_type": "Voyager", + "trx_mode": "mode 1", + "effective-freq-slot": [ + { + "n": "null", + "m": "null" + } + ], + "spacing": 75000000000.0, + "max-nb-of-channel": 50, + "output-power": 0.001, + "path_bandwidth": 400000000000.0 + } + }, + "optimizations": { + "explicit-route-include-objects": [] + } } ], "synchronization": [ diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 40ed37e66..e79338ae0 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -69,42 +69,19 @@ def requests_from_json(json_data,equipment): # 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['tx_osnr']) + # print(trx_params['min_spacing']) # optical power might be set differently in the request. if it is indicated then the # params['power'] is updated if req['path-constraints']['te-bandwidth']['output-power']: params['power'] = req['path-constraints']['te-bandwidth']['output-power'] # same process for nb-channel - fmin = trx_params['frequency']['min'] - fmax = trx_params['frequency']['max'] + fmin = params['frequency']['min'] + fmax = params['frequency']['max'] if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : - # check if requested nb_channels is consistant with baudrate and min-max frequencies - if trx_params['baud_rate'] is not None: - min_recommanded_spacing = automatic_spacing(trx_params['baud_rate']) - # needed for printing - else argument with quote are making errors in the print - temp = params['baud_rate']*1e-9 - else: - min_recommanded_spacing = params['spacing'] - temp = 'undetermined baudrate' - - max_recommanded_nb_channels = automatic_nch(fmin,fmax, - min_recommanded_spacing) - - if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] <= max_recommanded_nb_channels : - params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] - else: - msg = dedent(f''' - Requested channel number is not consistent with frequency range: - {fmin*1e-12} THz, {fmax*1e-12} THz and baud rate: {temp} GHz - min recommanded spacing is {min_recommanded_spacing} - max recommanded nb of channels is {max_recommanded_nb_channels} - Computation stopped.''') - logger.critical(msg) - exit() - - + params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] else : params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) + consitency_check(params) try : params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] except KeyError: @@ -112,6 +89,30 @@ def requests_from_json(json_data,equipment): requests_list.append(Path_request(**params)) return requests_list +def consitency_check(params): + fmin = params['frequency']['min'] + fmax = params['frequency']['max'] + max_recommanded_nb_channels = automatic_nch(fmin,fmax, + params['spacing']) + if params['baud_rate'] is not None: + #implicitely means that a mode is defined with min_spacing + if params['min_spacing']>params['spacing'] : + msg = f'Request {params["request_id"]} has spacing below transponder {params["trx_type"]}'+\ + f' {params["trx_mode"]} min spacing value {params["min_spacing"]*1e-9}GHz.\n'+\ + 'Computation stopped' + print(msg) + logger.critical(msg) + exit() + if params['nb_channel']>max_recommanded_nb_channels: + msg = dedent(f''' + Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz + is not consistent with frequency range {fmin*1e-12} THz, {fmax*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. + max recommanded nb of channels is {max_recommanded_nb_channels} + Computation stopped.''') + logger.critical(msg) + exit() + + def disjunctions_from_json(json_data): disjunctions_list = [] diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 5dfa34fe8..f5c17f62b 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db OSNR bit_rate tx_osnr cost') + power_dbm power_range_db OSNR bit_rate tx_osnr min_spacing cost') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -167,6 +167,13 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F if trx == trx_type_variety \ for mode in trxs[trx].mode \ if mode['format'] == trx_mode) + trx_params = {**mode_params} + # sanity check: spacing baudrate must be smaller than min spacing + if trx_params['baud_rate'] > trx_params['min_spacing'] : + msg = f'Inconsistency in equipment library:\n Transpoder "{trx_type_variety}" mode "{trx_params["format"]}" '+\ + f'has baud rate: {trx_params["baud_rate"]*1e-9} GHz greater than min_spacing {trx_params["min_spacing"]*1e-9}.' + print(msg) + exit() else: mode_params = {"format": "undetermined", "baud_rate": None, @@ -174,8 +181,9 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "bit_rate": None, "roll_off": None, "tx_osnr":None, + "min_spacing":None, "cost":None} - trx_params = {**mode_params} + trx_params = {**mode_params} trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency # TODO: novel automatic feature maybe unwanted if spacing is specified @@ -201,6 +209,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F trx_params['frequency']['max'], trx_params['spacing']) trx_params['tx_osnr'] = default_si_data.tx_osnr + trx_params['min_spacing'] = default_si_data.min_spacing nch = automatic_nch(trx_params['frequency']['min'], trx_params['frequency']['max'], trx_params['spacing']) diff --git a/gnpy/core/request.py b/gnpy/core/request.py index c2f91bab5..e4f8ce1a8 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -34,7 +34,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr cost path_bandwidth') +' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -57,6 +57,7 @@ def __init__(self, *args, **params): self.bit_rate = params.bit_rate self.roll_off = params.roll_off self.tx_osnr = params.tx_osnr + self.min_spacing = params.min_spacing self.cost = params.cost self.path_bandwidth = params.path_bandwidth @@ -402,7 +403,8 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1 # 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['baud_rate'])+12.5e9< req.spacing])) + if float(m['min_spacing'])<= req.spacing])) + # TODO be carefull on limits cases if min_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 @@ -425,7 +427,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if show : print(el) if path[-1].snr is not None: - if round(mean(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : + if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : found_a_feasible_mode = True return path, m else: diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index 1d232e877..eea1ba52a 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -79,6 +79,7 @@ "OSNR": 15, "bit_rate":100e9, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 }], "Transceiver":[ @@ -96,6 +97,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 }, { @@ -105,6 +107,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 75e9, "cost":1 }, { @@ -114,6 +117,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 }, { @@ -123,6 +127,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 75e9, "cost":1 } ] @@ -141,6 +146,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 100, + "min_spacing": 50e9, "cost":1 } ] @@ -159,6 +165,7 @@ "bit_rate": 100e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 50e9, "cost":1 }, { @@ -168,6 +175,7 @@ "bit_rate": 300e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 62.5e9, "cost":1 }, { @@ -177,6 +185,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -186,6 +195,7 @@ "bit_rate": 400e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 }, { @@ -195,6 +205,7 @@ "bit_rate": 200e9, "roll_off": 0.15, "tx_osnr": 45, + "min_spacing": 75e9, "cost":1 } ] diff --git a/tests/test_automaticmodefeature.py b/tests/test_automaticmodefeature.py index 0617df3fa..d5a2ec7ad 100644 --- a/tests/test_automaticmodefeature.py +++ b/tests/test_automaticmodefeature.py @@ -33,7 +33,7 @@ @pytest.mark.parametrize("net",[network_file_name]) @pytest.mark.parametrize("eqpt", [eqpt_library_name]) @pytest.mark.parametrize("serv",[service_file_name]) -@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 3', 'mode 1', 'mode 3', 'nok']]) +@pytest.mark.parametrize("expected_mode",[['16QAM', 'PS_SP64_1', 'PS_SP64_1', 'PS_SP64_1', 'mode 2 - fake', 'mode 2', 'PS_SP64_1', 'mode 3', 'PS_SP64_1', 'PS_SP64_1', '16QAM', 'mode 1', 'PS_SP64_1', 'PS_SP64_1', 'mode 1', 'mode 2', 'mode 1', 'mode 2', 'nok']]) def test_automaticmodefeature(net,eqpt,serv,expected_mode): data = load_requests(serv,eqpt) equipment = load_equipment(eqpt) From 4bd9a9cdda0ddde220ed34d5d88a972fb1b4d6c8 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Fri, 16 Nov 2018 12:12:47 +0100 Subject: [PATCH 083/108] final openroadm models implementation Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 15 ++++++++++++--- gnpy/core/elements.py | 5 ++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 056b4b9e3..29423cd27 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -17,14 +17,23 @@ "nf_max": 10, "out_voa_auto": false, "allowed_for_design": false - }, + }, { "type_variety": "low_noise", "type_def": "openroadm", "gain_flatmax": 27, - "gain_min": 11, + "gain_min": 12, + "p_max": 22, + "nf_coef": [-8.104e-4,-6.221e-2,-5.889e-1,37.62], + "allowed_for_design": false + }, + { + "type_variety": "standard", + "type_def": "openroadm", + "gain_flatmax": 27, + "gain_min": 12, "p_max": 22, - "nf_coef": [8.1e-4,6.142e-2,1.558,19.97], + "nf_coef": [-5.952e-4,-6.250e-2,-1.071,28.99], "allowed_for_design": false }, { diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index a969a14dc..adc8997a9 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -484,6 +484,7 @@ def interpol_params(self, frequencies, pin, baud_rates, pref): self.nch = frequencies.size self.pin_db = lin2db(sum(pin*1e3)) + """check power saturation and correct target_gain accordingly:""" if self.dp_db is not None: @@ -521,10 +522,8 @@ def _calc_nf(self, avg = False): nf_avg = self.params.nf_model.nf0 elif self.params.type_def == 'openroadm': pin_ch = self.pin_db - lin2db(self.nch) - # model NF = f(Pin) - nf_avg = polyval(self.params.nf_model.nf_coef, pin_ch) # model OSNR = f(Pin) - #nf_avg = pin_ch - nf_avg + 58 + nf_avg = pin_ch - polyval(self.params.nf_model.nf_coef, pin_ch) + 58 else: nf_avg = polyval(self.params.nf_fit_coeff, -dg) if avg: From c0cc5fa9fde0437a7e1da4c60a319489cd87a375 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 19 Nov 2018 20:32:03 +0100 Subject: [PATCH 084/108] Improve ROADM loss calculation in power mode Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 2 +- gnpy/core/elements.py | 21 +++++++++++++-------- gnpy/core/equipment.py | 2 +- gnpy/core/network.py | 11 +++++++++-- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 29423cd27..d26faf157 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -98,7 +98,7 @@ ], "Roadms":[{ "gain_mode_default_loss": 20, - "power_mode_pref": -20 + "power_mode_pout_target": -20 }], "SI":[{ "f_min": 191.3e12, diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index adc8997a9..59de38020 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -94,7 +94,8 @@ def __init__(self, *args, params=None, **kwargs): params = {'loss':None} super().__init__(*args, params=RoadmParams(**params), **kwargs) self.loss = self.params.loss - self.pch_out = None + self.pout_target = None #set in Networks.py by def set_roadm_loss + self.effective_loss = None #set in self.propagate self.passive = True @property @@ -112,11 +113,16 @@ def __repr__(self): def __str__(self): return '\n'.join([f'{type(self).__name__} {self.uid}', - f' loss (dB): {self.loss:.2f}', - f' pch out (dBm): {self.pch_out!r}']) + f' loss (dB): {self.effective_loss:.2f}', + f' pch out (dBm): {self.pout_target!r}']) - def propagate(self, *carriers): - attenuation = db2lin(self.loss) + 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_loss = pref.pi - self.pout_target + attenuation = db2lin(self.effective_loss) for carrier in carriers: pwr = carrier.power @@ -126,11 +132,10 @@ def propagate(self, *carriers): yield carrier._replace(power=pwr) def update_pref(self, pref): - self.pch_out = round(pref.pi - self.loss, 2) - return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) + return pref._replace(p_span0=pref.p0, p_spani=self.pout_target) def __call__(self, spectral_info): - carriers = tuple(self.propagate(*spectral_info.carriers)) + carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) pref = self.update_pref(spectral_info.pref) return spectral_info.update(carriers=carriers, pref=pref) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index f5c17f62b..06bbea555 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -25,7 +25,7 @@ Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ max_loss padding EOL con_in con_out') Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') -Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pref') +Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ power_dbm power_range_db OSNR bit_rate tx_osnr min_spacing cost') AmpBase = namedtuple( diff --git a/gnpy/core/network.py b/gnpy/core/network.py index dbd6523d3..f0d3bf6af 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -131,16 +131,23 @@ def select_edfa(gain_target, power_target, equipment): # =>chose the amp with the best NF among the acceptable ones: return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF +def set_roadm_loss(network, equipment): + roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] + power_mode = equipment['Spans']['default'].power_mode + roadm_loss = equipment['Roadms']['default'].default_loss + pout_target = equipment['Roadms']['default'].power_mode_pout_target + def set_roadm_loss(network, equipment, pref_ch_db): roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] power_mode = equipment['Spans']['default'].power_mode default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss - pref_roadm_db = equipment['Roadms']['default'].power_mode_pref - roadm_loss = pref_ch_db - pref_roadm_db + pout_target = equipment['Roadms']['default'].power_mode_pout_target + roadm_loss = pref_ch_db - pout_target for roadm in roadms: if power_mode: roadm.loss = roadm_loss + roadm.pout_target = pout_target elif roadm.loss == None: roadm.loss = default_roadm_loss From c22d1173af89059a1603165a3f01d91be2244700 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 19 Nov 2018 20:33:53 +0100 Subject: [PATCH 085/108] update test eqpt_config Signed-off-by: Jean-Luc Auge --- tests/data/eqpt_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index eea1ba52a..137ed2279 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -66,7 +66,7 @@ ], "Roadms":[{ "gain_mode_default_loss": 20, - "power_mode_pref": -20 + "power_mode_pout_target": -20 }], "SI":[{ "f_min": 191.3e12, From 697ac311fe5797fae9b908450ddcca6da44ec8d5 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 10 Dec 2018 16:39:19 +0100 Subject: [PATCH 086/108] Improve excel link sheet parser -implement hierarchical headers -can read headers in any order Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 151 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 131 insertions(+), 20 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index f092d9de2..8aca77613 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -40,24 +40,43 @@ def __new__(cls, city, state='', country='', region='', latitude=0, longitude=0, values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)] return super().__new__(cls, city, state, country, region, *values) -class Link(namedtuple('Link', 'from_city to_city \ - east_distance east_fiber east_lineic east_con_in east_con_out east_pmd east_cable \ - west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \ - distance_units')): - def __new__(cls, from_city, to_city, - east_distance, east_fiber='SSMF', east_lineic=0.2, - east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='', - west_distance='', west_fiber='', west_lineic='', - west_con_in='', west_con_out='', west_pmd='', west_cable='', - distance_units='km'): - east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out, - east_pmd, east_cable] - west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out, - west_pmd, west_cable] - default_values = [80,'SSMF',0.2,None,None,0.1,''] - east_values = [x[0] if x[0] != '' else x[1] for x in zip(east_values,default_values)] - west_values = [x[0] if x[0] != '' else x[1] for x in zip(west_values,east_values)] - return super().__new__(cls, from_city, to_city, *east_values, *west_values, distance_units) +class Link(object, ): + """attribtes from ingress parse_ept_headers dict + +node_a, node_z, ingress_fiber_con_in, egress_fiber_con_in + """ + def __init__(self, **kwargs): + super(Link, self).__init__() + print(kwargs) + self.update_attr(kwargs) + self.update_egress(kwargs) + self.distance_units = 'km' + + def update_attr(self, kwargs): + for k,v in kwargs.items(): + if not 'west' in k: + setattr(self, k, v if v!='' else self.default_values[k]) + + def update_egress(self, kwargs): + for k,v in kwargs.items(): + if 'west' in k: + if v=='': + print(k) + attribut = 'east' + k.split('west')[1] + setattr(self, k, getattr(self, attribut)) + else: + setattr(self, k, v) + + default_values = \ + { + 'east_distance': 80, + 'east_fiber': 'SSMF', + 'east_lineic': 0.2, + 'east_con_in': None, + 'east_con_out': None, + 'east_pmd': 0.1, + 'east_cable': '' + } + class Eqpt(namedtuple('Eqpt', 'from_city to_city \ egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\ @@ -72,6 +91,71 @@ def __new__(cls, from_city='', to_city='', values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)] return super().__new__(cls, *values) +def read_header(my_sheet, line, slice_): + """ return the list of headers !:= '' + header_i = [(header, header_column_index), ...] + in a {line, slice1_x, slice_y} range + """ + Param_header = namedtuple('Param_header', 'header colindex') + try: + header = [x.value.strip() for x in my_sheet.row_slice(line, slice_[0], slice_[1])] + header_i = [Param_header(header,i+slice_[0]) for i, header in enumerate(header) if header != ''] + except: + header_i = [] + if header_i != [] and header_i[-1].colindex != slice_[1]: + header_i.append(Param_header('',slice_[1])) + return header_i + +def read_slice(my_sheet, line, slice_, header): + """return the slice range of a given header + in a defined range {line, slice_x, slice_y}""" + header_i = read_header(my_sheet, line, slice_) + slice_range = (-1,-1) + if header_i != []: + try: + slice_range = next((h.colindex,header_i[i+1].colindex) \ + for i,h in enumerate(header_i) if header in h.header) + except: + pass + return slice_range + + +def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in): + """return a dict of header1_slice + key = column index + value = all_headers 3rd order value + = Ept_inputs attributs""" + + 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 + print(h0, iteration) + slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0) + iteration += 1 + if slice_out == (-1, -1): + print(f'critical missing header {h0}, abort parsing') + exit() + if 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) + return headers + +def parse_row(row, headers): + #print([label for label in ept.values()]) + #print([i for i in ept.keys()]) + #print(row[i for i in ept.keys()]) + return {f: r.value for f, r in \ + zip([label for label in headers.values()], [row[i] for i in headers])} + #if r.ctype != XL_CELL_EMPTY} + +def parse_sheet(my_sheet, input_headers_dict, header_line, start_line, column): + headers = parse_headers(my_sheet, input_headers_dict, {}, header_line, (0,column)) + for row in all_rows(my_sheet, start=start_line): + yield parse_row(row[0: column], headers) + def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): try : test_nodes = [n for n in nodes_by_city if not n in links_by_city] @@ -225,6 +309,29 @@ def convert_file(input_filename, filter_region=[]): return output_json_file_name 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' + } + } + with open_workbook(input_filename) as wb: nodes_sheet = wb.sheet_by_name('Nodes') links_sheet = wb.sheet_by_name('Links') @@ -261,8 +368,9 @@ def parse_excel(input_filename): raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}') """ links = [] - for row in all_rows(links_sheet, start=5): - links.append(Link(*(x.value for x in row[0:LINKS_COLUMN]))) + for link in parse_sheet(links_sheet, link_headers, LINKS_LINE, LINKS_LINE+2, LINKS_COLUMN): + links.append(Link(**link)) + print('\n', [l.__dict__ for l in links]) eqpts = [] if eqpt_sheet != None: @@ -380,8 +488,11 @@ def midpoint(city_a, city_b): #output_json_file_name = 'coronet_conus_example.json' #TODO get column size automatically from tupple size + NODES_COLUMN = 7 +NODES_LINE = 4 LINKS_COLUMN = 16 +LINKS_LINE = 3 EQPTS_COLUMN = 12 parser = ArgumentParser() parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls') From ac5171e95e40138669c1f1bd879a92015597a083 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 10 Dec 2018 17:33:00 +0100 Subject: [PATCH 087/108] Improve Excel Node parser -read headers in any order -possible hierarchical implementation Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 65 +++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 8aca77613..518fd2743 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -33,14 +33,28 @@ all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) -class Node(namedtuple('Node', 'city state country region latitude longitude node_type')): - def __new__(cls, city, state='', country='', region='', latitude=0, longitude=0, node_type='ILA'): - values = [latitude, longitude, node_type] - default_values = [0, 0, 'ILA'] - values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)] - return super().__new__(cls, city, state, country, region, *values) +class Node(object): + def __init__(self, **kwargs): + super(Node, self).__init__() + print(kwargs) + self.update_attr(kwargs) + + def update_attr(self, kwargs): + for k,v in kwargs.items(): + setattr(self, k, v if v!='' else self.default_values[k]) + + default_values = \ + { + 'city': '', + 'state': '', + 'country': '', + 'region': '', + 'latitude': 0, + 'longitude': 0, + 'node_type': 'ILA' + } -class Link(object, ): +class Link(object): """attribtes from ingress parse_ept_headers dict +node_a, node_z, ingress_fiber_con_in, egress_fiber_con_in """ @@ -331,6 +345,15 @@ def parse_excel(input_filename): 'Cable id': 'west_cable' } } + node_headers = \ + { 'City': 'city', + 'State': 'state', + 'Country': 'country', + 'Region': 'region', + 'Latitude': 'latitude', + 'Longitude': 'longitude', + 'Type': 'node_type' + } with open_workbook(input_filename) as wb: nodes_sheet = wb.sheet_by_name('Nodes') @@ -341,36 +364,22 @@ def parse_excel(input_filename): #eqpt_sheet is optional eqpt_sheet = None - - # sanity check - """ - header = [x.value.strip() for x in nodes_sheet.row(4)] - expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude'] - if header != expected: - raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}') - """ - nodes = [] - for row in all_rows(nodes_sheet, start=5): - nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN]))) + for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE, NODES_LINE+1, NODES_COLUMN): + nodes.append(Node(**node)) + #print('\n', [l.__dict__ for l in links]) + + #for row in all_rows(nodes_sheet, start=5): + # nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN]))) #check input expected_node_types = ('ROADM', 'ILA', 'FUSED') nodes = [n._replace(node_type='ILA') if not (n.node_type in expected_node_types) else n for n in nodes] - # sanity check - """ - header = [x.value.strip() for x in links_sheet.row(4)] - expected = ['Node A', 'Node Z', - 'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id', - 'Distance (km)', 'Fiber type', 'lineic att', 'Con_in', 'Con_out', 'PMD', 'Cable id'] - if header != expected: - raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}') - """ links = [] for link in parse_sheet(links_sheet, link_headers, LINKS_LINE, LINKS_LINE+2, LINKS_COLUMN): links.append(Link(**link)) - print('\n', [l.__dict__ for l in links]) + #print('\n', [l.__dict__ for l in links]) eqpts = [] if eqpt_sheet != None: From 5d92baf35e9a04ae27d57124c7fe93e0e9cd8b54 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 13 Dec 2018 17:17:12 +0100 Subject: [PATCH 088/108] Add link duplication check for the xls parser Signed-off-by: Jean-Luc Auge Check and remove duplicate links a warning is issued when a duplicate link is dicovered the execution is paused for the user ot see the warning the duplicate link is removed and the execution resumed Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 143 ++++++++++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 518fd2743..4fc4ae5be 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -30,13 +30,14 @@ from itertools import chain from json import dumps from pathlib import Path +from difflib import get_close_matches +import time all_rows = lambda sh, start=0: (sh.row(x) for x in range(start, sh.nrows)) class Node(object): def __init__(self, **kwargs): super(Node, self).__init__() - print(kwargs) self.update_attr(kwargs) def update_attr(self, kwargs): @@ -60,9 +61,9 @@ class Link(object): """ def __init__(self, **kwargs): super(Link, self).__init__() - print(kwargs) self.update_attr(kwargs) - self.update_egress(kwargs) + # need to update west after east, in case east is copied over west + self.update_west(kwargs) self.distance_units = 'km' def update_attr(self, kwargs): @@ -70,16 +71,19 @@ def update_attr(self, kwargs): if not 'west' in k: setattr(self, k, v if v!='' else self.default_values[k]) - def update_egress(self, kwargs): + def update_west(self, kwargs): for k,v in kwargs.items(): if 'west' in k: if v=='': - print(k) - attribut = 'east' + k.split('west')[1] + attribut = 'east' + k.split('west')[-1] setattr(self, k, getattr(self, attribut)) else: setattr(self, k, v) + def __eq__(self, link): + return (self.from_city == link.from_city and self.to_city == link.to_city) \ + or (self.from_city == link.to_city and self.to_city == link.from_city) + default_values = \ { 'east_distance': 80, @@ -92,18 +96,33 @@ def update_egress(self, kwargs): } -class Eqpt(namedtuple('Eqpt', 'from_city to_city \ - egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\ - ingress_amp_type ingress_att_in ingress_amp_gain ingress_amp_tilt ingress_amp_att_out')): - def __new__(cls, from_city='', to_city='', - egress_amp_type='', egress_att_in=0, egress_amp_gain=0, egress_amp_tilt=0, egress_amp_att_out=0, - ingress_amp_type='', ingress_att_in=0, ingress_amp_gain=0, ingress_amp_tilt=0, ingress_amp_att_out=0): - values = [from_city, to_city, - egress_amp_type, egress_att_in, egress_amp_gain, egress_amp_tilt, egress_amp_att_out, - ingress_amp_type, ingress_att_in, ingress_amp_gain, ingress_amp_tilt, ingress_amp_att_out] - default_values = ['','','',0,0,0,0,'',0,0,0,0] - values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)] - return super().__new__(cls, *values) +class Eqpt(object): + def __init__(self, **kwargs): + super(Eqpt, self).__init__() + self.update_attr(kwargs) + + def update_attr(self, kwargs): + for k,v in kwargs.items(): + if v=='': + # remove east/west prefix to map default values that are east/west agnostic + attribut = k.split('west_')[-1] + attribut = attribut.split('east_')[-1] + print(attribut) + setattr(self, k, self.default_values[attribut]) + else: + setattr(self, k, v) + + default_values = \ + { + 'from_city': '', + 'to_city': '', + 'amp_type': '', + 'att_in': 0, + 'amp_gain': 0, + 'tilt': 0, + 'att_out': 0 + } + def read_header(my_sheet, line, slice_): """ return the list of headers !:= '' @@ -145,7 +164,7 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in): iteration = 1 while slice_out == (-1,-1) and iteration < 10: #try next lines - print(h0, iteration) + #print(h0, iteration) slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0) iteration += 1 if slice_out == (-1, -1): @@ -170,7 +189,21 @@ def parse_sheet(my_sheet, input_headers_dict, header_line, start_line, column): for row in all_rows(my_sheet, start=start_line): yield parse_row(row[0: column], headers) -def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): +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) + if duplicate_links != []: + time.sleep(3) + for l in duplicate_links: + links.remove(l) + try : test_nodes = [n for n in nodes_by_city if not n in links_by_city] test_links = [n for n in links_by_city if not n in nodes_by_city] @@ -179,24 +212,24 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): and (test_links == [] or test_links ==[''])\ and (test_eqpts == [] or test_eqpts ==['']) except AssertionError: - print(f'\x1b[1;31;40m'+ f'!names in Nodes and Links sheets do no match, check:\ + print(f'CRITICAL error: \nNames 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'+ '\x1b[0m') + \n{test_eqpts} in Eqpt sheet') exit(1) for city,link in links_by_city.items(): - if (nodes_by_city[city].node_type.lower()=='ila' or nodes_by_city[city].node_type.lower()=='fused') and len(link) != 2: + 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 - msg = f'\x1b[1;33;40m'+ f'\n\tInvalid node type ({nodes_by_city[city].node_type}) '+\ - f'specified in {city}, replaced by ROADM'+ '\x1b[0m' - print(msg) - # TODO create a logger ? - nodes_by_city[city] = nodes_by_city[city]._replace(node_type='ROADM') - nodes = [n._replace(node_type='ROADM') if n.city==city else n for n in nodes] - return nodes + 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 def convert_file(input_filename, filter_region=[]): nodes, links, eqpts = parse_excel(input_filename) @@ -223,7 +256,7 @@ def convert_file(input_filename, filter_region=[]): for eqpt in eqpts: eqpts_by_city[eqpt.from_city].append(eqpt) - nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city) + nodes, links = sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city) data = { 'elements': @@ -354,6 +387,24 @@ def parse_excel(input_filename): 'Longitude': 'longitude', 'Type': 'node_type' } + eqpt_headers = \ + { 'Node A': 'from_city', + 'Node Z': 'to_city', + 'egress':{ + 'amp type': 'east_amp_type', + 'att_in': 'east_att_in', + 'amp gain': 'east_amp_gain', + 'tilt': 'east_tilt', + 'att_out': 'east_att_out' + }, + 'ingress':{ + 'amp type': 'west_amp_type', + 'att_in': 'west_att_in', + 'amp gain': 'west_amp_gain', + 'tilt': 'west_tilt', + 'att_out': 'west_att_out' + } + } with open_workbook(input_filename) as wb: nodes_sheet = wb.sheet_by_name('Nodes') @@ -367,14 +418,10 @@ def parse_excel(input_filename): nodes = [] for node in parse_sheet(nodes_sheet, node_headers, NODES_LINE, NODES_LINE+1, NODES_COLUMN): nodes.append(Node(**node)) - #print('\n', [l.__dict__ for l in links]) - - #for row in all_rows(nodes_sheet, start=5): - # nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN]))) - #check input expected_node_types = ('ROADM', 'ILA', 'FUSED') - nodes = [n._replace(node_type='ILA') - if not (n.node_type in expected_node_types) else n for n in nodes] + for n in nodes: + if not (n.node_type 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): @@ -382,9 +429,9 @@ def parse_excel(input_filename): #print('\n', [l.__dict__ for l in links]) eqpts = [] - if eqpt_sheet != None: - for row in all_rows(eqpt_sheet, start=5): - eqpts.append(Eqpt(*(x.value for x in row[0:EQPTS_COLUMN]))) + if eqpt_sheet != 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) @@ -404,15 +451,10 @@ def eqpt_connection_by_city(city_name): # Then len(other_cities) == 2 direction = ['ingress', 'egress'] for i in range(2): - try: - from_ = fiber_link(other_cities[i], city_name) - in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) - to_ = fiber_link(city_name, other_cities[1-i]) - subdata += connect_eqpt(from_, in_, to_) - except IndexError: - msg = f'In {__name__} eqpt_connection_by_city:\n\t{city_name} is not properly connected' - print(msg) - exit(1) + from_ = fiber_link(other_cities[i], city_name) + in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) + to_ = fiber_link(city_name, other_cities[1-i]) + subdata += connect_eqpt(from_, in_, to_) elif nodes_by_city[city_name].node_type.lower() == 'roadm': for other_city in other_cities: from_ = f'roadm {city_name}' @@ -502,6 +544,7 @@ def midpoint(city_a, city_b): NODES_LINE = 4 LINKS_COLUMN = 16 LINKS_LINE = 3 +EQPTS_LINE = 3 EQPTS_COLUMN = 12 parser = ArgumentParser() parser.add_argument('workbook', nargs='?', type=Path , default='meshTopologyExampleV2.xls') From 4d84a4f5283d5e64997f2c72e866c5190ac9e8f4 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 17 Dec 2018 09:08:17 +0100 Subject: [PATCH 089/108] replace egress/ingress by east/west denomination xls parser Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 4fc4ae5be..e3c6fc610 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -56,8 +56,8 @@ def update_attr(self, kwargs): } class Link(object): - """attribtes from ingress parse_ept_headers dict - +node_a, node_z, ingress_fiber_con_in, egress_fiber_con_in + """attribtes from west parse_ept_headers dict + +node_a, node_z, west_fiber_con_in, east_fiber_con_in """ def __init__(self, **kwargs): super(Link, self).__init__() @@ -75,6 +75,7 @@ def update_west(self, kwargs): for k,v in kwargs.items(): if 'west' in k: if v=='': + # copy east attributes attribut = 'east' + k.split('west')[-1] setattr(self, k, getattr(self, attribut)) else: @@ -107,7 +108,6 @@ def update_attr(self, kwargs): # remove east/west prefix to map default values that are east/west agnostic attribut = k.split('west_')[-1] attribut = attribut.split('east_')[-1] - print(attribut) setattr(self, k, self.default_values[attribut]) else: setattr(self, k, v) @@ -274,14 +274,14 @@ def convert_file(input_filename, filter_region=[]): 'longitude': x.longitude}}, 'type': 'Roadm'} for x in nodes_by_city.values() if x.node_type.lower() == 'roadm'] + - [{'uid': f'ingress fused spans in {x.city}', + [{'uid': f'west fused spans in {x.city}', 'metadata': {'location': {'city': x.city, 'region': x.region, 'latitude': x.latitude, 'longitude': x.longitude}}, 'type': 'Fused'} for x in nodes_by_city.values() if x.node_type.lower() == 'fused'] + - [{'uid': f'egress fused spans in {x.city}', + [{'uid': f'east fused spans in {x.city}', 'metadata': {'location': {'city': x.city, 'region': x.region, 'latitude': x.latitude, @@ -312,28 +312,30 @@ def convert_file(input_filename, filter_region=[]): 'con_out':x.west_con_out} } # missing ILA construction for x in links] + - [{'uid': f'egress edfa in {e.from_city} to {e.to_city}', + [{'uid': f'east edfa in {e.from_city} to {e.to_city}', 'metadata': {'location': {'city': nodes_by_city[e.from_city].city, 'region': nodes_by_city[e.from_city].region, 'latitude': nodes_by_city[e.from_city].latitude, 'longitude': nodes_by_city[e.from_city].longitude}}, 'type': 'Edfa', - 'type_variety': e.egress_amp_type, - 'operational': {'gain_target': e.egress_amp_gain, - 'tilt_target': e.egress_amp_tilt} + 'type_variety': e.east_amp_type, + 'operational': {'gain_target': e.east_amp_gain, + 'tilt_target': e.east_tilt, + 'out_voa' : e.east_att_out} } - for e in eqpts if e.egress_amp_type.lower() != ''] + - [{'uid': f'ingress edfa in {e.from_city} to {e.to_city}', + for e in eqpts if e.east_amp_type.lower() != ''] + + [{'uid': f'west edfa in {e.from_city} to {e.to_city}', 'metadata': {'location': {'city': nodes_by_city[e.from_city].city, 'region': nodes_by_city[e.from_city].region, 'latitude': nodes_by_city[e.from_city].latitude, 'longitude': nodes_by_city[e.from_city].longitude}}, 'type': 'Edfa', - 'type_variety': e.ingress_amp_type, - 'operational': {'gain_target': e.ingress_amp_gain, - 'tilt_target': e.ingress_amp_tilt} + 'type_variety': e.west_amp_type, + 'operational': {'gain_target': e.west_amp_gain, + 'tilt_target': e.west_tilt, + 'out_voa' : e.west_att_out} } - for e in eqpts if e.ingress_amp_type.lower() != ''], + for e in eqpts if e.west_amp_type.lower() != ''], 'connections': list(chain.from_iterable([eqpt_connection_by_city(n.city) for n in nodes])) @@ -390,14 +392,14 @@ def parse_excel(input_filename): eqpt_headers = \ { 'Node A': 'from_city', 'Node Z': 'to_city', - 'egress':{ + 'east':{ 'amp type': 'east_amp_type', 'att_in': 'east_att_in', 'amp gain': 'east_amp_gain', 'tilt': 'east_tilt', 'att_out': 'east_att_out' }, - 'ingress':{ + 'west':{ 'amp type': 'west_amp_type', 'att_in': 'west_att_in', 'amp gain': 'west_amp_gain', @@ -449,7 +451,7 @@ def eqpt_connection_by_city(city_name): subdata = [] if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'): # Then len(other_cities) == 2 - direction = ['ingress', 'egress'] + direction = ['west', 'east'] for i in range(2): from_ = fiber_link(other_cities[i], city_name) in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) @@ -463,7 +465,7 @@ def eqpt_connection_by_city(city_name): subdata += connect_eqpt(from_, in_, to_) from_ = fiber_link(other_city, city_name) - in_ = eqpt_in_city_to_city(city_name, other_city, "ingress") + in_ = eqpt_in_city_to_city(city_name, other_city, "west") to_ = f'roadm {city_name}' subdata += connect_eqpt(from_, in_, to_) return subdata @@ -479,8 +481,8 @@ def connect_eqpt(from_, in_, to_): return connections -def eqpt_in_city_to_city(in_city, to_city, direction='egress'): - rev_direction = 'ingress' if direction == 'egress' else 'egress' +def eqpt_in_city_to_city(in_city, to_city, direction='east'): + rev_direction = 'west' if direction == 'east' else 'east' amp_direction = f'{direction}_amp_type' amp_rev_direction = f'{rev_direction}_amp_type' return_eqpt = '' From a08ce9ecb7787e5a467a9f770c80a80d5f5858f3 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 29 Nov 2018 12:13:45 +0100 Subject: [PATCH 090/108] gain mode fix Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 41 ++++++++++++++++++++++++----------------- gnpy/core/network.py | 7 +------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 59de38020..24f722990 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -94,7 +94,8 @@ def __init__(self, *args, params=None, **kwargs): params = {'loss':None} super().__init__(*args, params=RoadmParams(**params), **kwargs) self.loss = self.params.loss - self.pout_target = None #set in Networks.py by def set_roadm_loss + self.target_pch_out_db = None #set in Networks.py by def set_roadm_loss + self.effective_pch_out_db = None self.effective_loss = None #set in self.propagate self.passive = True @@ -114,14 +115,18 @@ def __repr__(self): def __str__(self): return '\n'.join([f'{type(self).__name__} {self.uid}', f' loss (dB): {self.effective_loss:.2f}', - f' pch out (dBm): {self.pout_target!r}']) + f' pch out (dBm): {self.effective_pch_out_db!r}']) 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_loss = pref.pi - self.pout_target + if self.target_pch_out_db: + self.effective_loss = pref.pi - self.target_pch_out_db + else: + self.effective_loss = self.loss + self.effective_pch_out_db = pref.pi - self.effective_loss attenuation = db2lin(self.effective_loss) for carrier in carriers: @@ -132,7 +137,7 @@ def propagate(self, pref, *carriers): yield carrier._replace(power=pwr) def update_pref(self, pref): - return pref._replace(p_span0=pref.p0, p_spani=self.pout_target) + return pref._replace(p_span0=pref.p0, p_spani=self.effective_pch_out_db) def __call__(self, spectral_info): carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) @@ -211,7 +216,7 @@ def __init__(self, *args, params=None, **kwargs): self.con_out = self.params.con_out self.dispersion = self.params.dispersion # s/m/m self.gamma = self.params.gamma # 1/W/m - self.pch_out = None + self.pch_out_db = None # TODO|jla: discuss factor 2 in the linear lineic attenuation @property @@ -244,7 +249,8 @@ def __str__(self): f' pad att_in (dB): {self.att_in:.2f}', f' total loss (dB): {self.loss:.2f}', f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})', - f' (conn loss out includes EOL margin defined in eqpt_config.json)']) + f' (conn loss out includes EOL margin defined in eqpt_config.json)', + f' pch out (dBm): {self.pch_out_db!r}']) @property def fiber_loss(self): @@ -360,8 +366,8 @@ def propagate(self, *carriers): yield carrier._replace(power=pwr) def update_pref(self, pref): - self.pch_out = round(pref.pi - self.loss, 2) - return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) + self.pch_out_db = round(pref.pi - self.loss, 2) + return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db) def __call__(self, spectral_info): carriers = tuple(self.propagate(*spectral_info.carriers)) @@ -424,8 +430,8 @@ def __init__(self, *args, params={}, operational={}, **kwargs): self.nch = None self.pout_db = None self.dp_db = None #delta P with Pref (power swwep) in power mode - self.target_pch_db = None - self.effective_pch_db = None + self.target_pch_out_db = None + self.effective_pch_out_db = None self.passive = False self.effective_gain = self.operational.gain_target self.att_in = None @@ -471,8 +477,8 @@ def __str__(self): f' Power In (dBm): {self.pin_db:.2f}', f' Power Out (dBm): {self.pout_db:.2f}', f' Delta_P (dB): {self.dp_db!r}', - f' target pch (dBm): {self.target_pch_db!r}', - f' effective pch (dBm): {self.effective_pch_db!r}', + f' target pch (dBm): {self.target_pch_out_db!r}', + f' effective pch (dBm): {self.effective_pch_out_db!r}', f' output VOA (dB): {self.operational.out_voa:.2f}']) def interpol_params(self, frequencies, pin, baud_rates, pref): @@ -490,16 +496,17 @@ def interpol_params(self, frequencies, pin, baud_rates, pref): self.nch = frequencies.size self.pin_db = lin2db(sum(pin*1e3)) - """check power saturation and correct target_gain accordingly:""" - + """in power mode: dp_db is defined and can be used to calculate the power target + This power target is used calculate the amplifier gain""" if self.dp_db is not None: - self.target_pch_db = round(self.dp_db + pref.p0, 2) - self.effective_gain = self.target_pch_db - pref.pi + self.target_pch_out_db = round(self.dp_db + pref.p0, 2) + self.effective_gain = self.target_pch_out_db - pref.pi else: self.effective_gain = self.operational.gain_target + """check power saturation and correct target_gain accordingly:""" self.effective_gain = min(self.effective_gain, self.params.p_max - self.pin_db) - self.effective_pch_db = round(pref.pi + self.effective_gain, 2) + self.effective_pch_out_db = round(pref.pi + self.effective_gain, 2) self.nf = self._calc_nf() self.gprofile = self._gain_profile(pin) diff --git a/gnpy/core/network.py b/gnpy/core/network.py index f0d3bf6af..bd53a5357 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -131,11 +131,6 @@ def select_edfa(gain_target, power_target, equipment): # =>chose the amp with the best NF among the acceptable ones: return min(acceptable_power_list, key=itemgetter(3)).variety #filter on NF -def set_roadm_loss(network, equipment): - roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] - power_mode = equipment['Spans']['default'].power_mode - roadm_loss = equipment['Roadms']['default'].default_loss - pout_target = equipment['Roadms']['default'].power_mode_pout_target def set_roadm_loss(network, equipment, pref_ch_db): roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] @@ -147,7 +142,7 @@ def set_roadm_loss(network, equipment, pref_ch_db): for roadm in roadms: if power_mode: roadm.loss = roadm_loss - roadm.pout_target = pout_target + roadm.target_pch_out_db = pout_target elif roadm.loss == None: roadm.loss = default_roadm_loss From 771af4991c3a1d3a96489b2fa709eb5b8ea6cb26 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 10 Jan 2019 17:57:10 +0100 Subject: [PATCH 091/108] carrier probe point for Edfa and Fiber elements save and be able to retrieve the carrier information processed by Edfa and Fiber elements: @ input & output of the element - channel power: ase, nli, signal and total Signed-off-by: Jean-Luc Auge --- examples/transmission_main_example.py | 6 ++++ gnpy/core/elements.py | 46 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index ce091441f..ba38607c9 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -98,6 +98,12 @@ def main(network, equipment, source, destination, req = None): propagate(path, req, equipment, show=len(power_range)==1) print(f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :') print(destination) + + #print(f'\n !!!!!!!!!!!!!!!!! TEST POINT !!!!!!!!!!!!!!!!!!!!!') + #print(f'carriers ase output of {path[1]} =\n {list(path[1].carriers("out", "nli"))}') + # => use "in" or "out" parameter + # => use "nli" or "ase" or "signal" or "total" parameter + simulation_data.append({ 'Pch_dBm' : pref_ch_db + dp_db, 'OSNR_ASE_0.1nm' : round(mean(destination.osnr_ase_01nm),2), diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 24f722990..8dd364cfc 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -217,6 +217,8 @@ def __init__(self, *args, params=None, **kwargs): self.dispersion = self.params.dispersion # s/m/m self.gamma = self.params.gamma # 1/W/m self.pch_out_db = None + self.carriers_in = None + self.carriers_out = None # TODO|jla: discuss factor 2 in the linear lineic attenuation @property @@ -282,6 +284,25 @@ def asymptotic_length(self): aleff = 1 / (2 * alpha) return aleff + def carriers(self, loc, attr): + """retrieve carriers information + loc = (in, out) of the class element + attr = (ase, nli, signal, total) power information""" + if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): + yield None + return + power_dict = { + 'nli': 'nonlinear_interference', + 'ase': 'amplified_spontaneous_emission' + } + attr = power_dict.get(attr, attr) + loc_attr = 'carriers_'+loc + for c in getattr(self, loc_attr) : + if attr == 'total': + yield c.power.ase+c.power.nli+c.power.signal + else: + yield c.power._asdict().get(attr, None) + def beta2(self, ref_wavelength=None): """ Returns beta2 from dispersion parameter. Dispersion is entered in ps/nm/km. @@ -370,8 +391,10 @@ def update_pref(self, pref): return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db) def __call__(self, spectral_info): + self.carriers_in = spectral_info.carriers carriers = tuple(self.propagate(*spectral_info.carriers)) pref = self.update_pref(spectral_info.pref) + self.carriers_out = carriers return spectral_info.update(carriers=carriers, pref=pref) class EdfaParams: @@ -435,6 +458,8 @@ def __init__(self, *args, params={}, operational={}, **kwargs): self.passive = False self.effective_gain = self.operational.gain_target self.att_in = None + self.carriers_in = None + self.carriers_out = None @property def to_json(self): @@ -481,6 +506,25 @@ def __str__(self): f' effective pch (dBm): {self.effective_pch_out_db!r}', f' output VOA (dB): {self.operational.out_voa:.2f}']) + def carriers(self, loc, attr): + """retrieve carriers information + loc = (in, out) of the class element + attr = (ase, nli, signal, total) power information""" + if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')): + yield None + return + power_dict = { + 'nli': 'nonlinear_interference', + 'ase': 'amplified_spontaneous_emission' + } + attr = power_dict.get(attr, attr) + loc_attr = 'carriers_'+loc + for c in getattr(self, loc_attr) : + if attr == 'total': + yield c.power.ase+c.power.nli+c.power.signal + else: + yield c.power._asdict().get(attr, None) + def interpol_params(self, frequencies, pin, baud_rates, pref): """interpolate SI channel frequencies with the edfa dgt and gain_ripple frquencies from json set the edfa class __init__ None parameters : @@ -722,6 +766,8 @@ def update_pref(self, pref): p_spani=pref.pi + self.effective_gain - self.operational.out_voa) def __call__(self, spectral_info): + self.carriers_in = spectral_info.carriers carriers = tuple(self.propagate(spectral_info.pref, *spectral_info.carriers)) pref = self.update_pref(spectral_info.pref) + self.carriers_out = carriers return spectral_info.update(carriers=carriers, pref=pref) From ec7b14da8c8ae404ed070bda475685f0b1ddb546 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 14 Jan 2019 17:11:07 +0100 Subject: [PATCH 092/108] convert.py xls to json parser class enhancement improve default values handling update xls examples with east/west headers Signed-off-by: Jean-Luc Auge --- examples/CORONET_Global_Topology.xls | Bin 49664 -> 30720 bytes examples/meshTopologyExampleV2.xls | Bin 15360 -> 15360 bytes gnpy/core/convert.py | 73 ++++++++++++++------------- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/examples/CORONET_Global_Topology.xls b/examples/CORONET_Global_Topology.xls index 90b7e83acd35e9930b05ab2c67a63881e1c7b051..6dea7f16581b02d2c15d9f087f245b58cba949a5 100644 GIT binary patch literal 30720 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 bRPc8m>)iPLb5w=?{l%Yy|7)1PhyT9-MU{z0 literal 49664 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 delta 797 zcmaJ;J#W)c6g~ck<2rT|qfiB9;3*N2i!!8zif%v=Of(W78v_c|Ad5O~lS-|KP>a+q zu<<6C8DOjMz=9AfgSP?$EKH#Ofxra!xx;%|>NlNp?>#=}-kq(Tt^ET_Gtw{C9qarp z5A+q#O;*C;9z2YyN5u80|0pN^)Q^a(>uVc#_fW=nzT&;og7lyFQ@?l`ww@2O9t$51 z!r_qh;x!gL7$h-K8wqCvc(B%RGs7s$?aJ4Kl0N zg>>Y~dJ)jF^jYKXlUPKX* zlE8*NQJ=!p35*p1Cy)4{`P<~}&P>Utd1Bzh=gXZ2{}ue$9^0oT4l(v5&JQsyvG7}~ zNG$xmcO@>+hMSJW;!u`?h5zTup~?ReYRW<=00vdL00+)Qz!FLrDFSYuZ};q*0)HLM yq)VNT8t<6yyZL*Da{-qqEY5wd^VJ1rTZjB``Q?GepPKFT)54;m-B1567LEUhwuJQn diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index e3c6fc610..a2952e555 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -41,8 +41,10 @@ def __init__(self, **kwargs): self.update_attr(kwargs) def update_attr(self, kwargs): - for k,v in kwargs.items(): - setattr(self, k, v if v!='' else self.default_values[k]) + clean_kwargs = {k:v for k,v in kwargs.items() if v !=''} + for k,v in self.default_values.items(): + v = clean_kwargs.get(k,v) + setattr(self, k, v) default_values = \ { @@ -62,24 +64,16 @@ class Link(object): def __init__(self, **kwargs): super(Link, self).__init__() self.update_attr(kwargs) - # need to update west after east, in case east is copied over west - self.update_west(kwargs) self.distance_units = 'km' def update_attr(self, kwargs): - for k,v in kwargs.items(): - if not 'west' in k: - setattr(self, k, v if v!='' else self.default_values[k]) - - def update_west(self, kwargs): - for k,v in kwargs.items(): - if 'west' in k: - if v=='': - # copy east attributes - attribut = 'east' + k.split('west')[-1] - setattr(self, k, getattr(self, attribut)) - else: - setattr(self, k, v) + clean_kwargs = {k:v for k,v in kwargs.items() if v !=''} + for k,v in self.default_values.items(): + v = clean_kwargs.get(k,v) + setattr(self, k, v) + k = 'west' + k.split('east')[-1] + v = clean_kwargs.get(k,v) + setattr(self, k, v) def __eq__(self, link): return (self.from_city == link.from_city and self.to_city == link.to_city) \ @@ -87,6 +81,8 @@ def __eq__(self, link): default_values = \ { + 'from_city': '', + 'to_city': '', 'east_distance': 80, 'east_fiber': 'SSMF', 'east_lineic': 0.2, @@ -103,24 +99,23 @@ def __init__(self, **kwargs): self.update_attr(kwargs) def update_attr(self, kwargs): - for k,v in kwargs.items(): - if v=='': - # remove east/west prefix to map default values that are east/west agnostic - attribut = k.split('west_')[-1] - attribut = attribut.split('east_')[-1] - setattr(self, k, self.default_values[attribut]) - else: - setattr(self, k, v) + clean_kwargs = {k:v for k,v in kwargs.items() if v !=''} + for k,v in self.default_values.items(): + v_east = clean_kwargs.get(k,v) + setattr(self, k, v_east) + k = 'west' + k.split('east')[-1] + v_west = clean_kwargs.get(k,v) + setattr(self, k, v_west) default_values = \ { 'from_city': '', 'to_city': '', - 'amp_type': '', - 'att_in': 0, - 'amp_gain': 0, - 'tilt': 0, - 'att_out': 0 + 'east_amp_type': '', + 'east_att_in': 0, + 'east_amp_gain': 0, + 'east_tilt': 0, + 'east_att_out': 0 } @@ -154,10 +149,10 @@ def read_slice(my_sheet, line, slice_, header): def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in): - """return a dict of header1_slice + """return a dict of header_slice key = column index - value = all_headers 3rd order value - = Ept_inputs attributs""" + value = header name""" + for h0 in input_headers_dict: slice_out = read_slice(my_sheet, start_line, slice_in, h0) @@ -168,12 +163,18 @@ def parse_headers(my_sheet, input_headers_dict, headers, start_line, slice_in): slice_out = read_slice(my_sheet, start_line+iteration, slice_in, h0) iteration += 1 if slice_out == (-1, -1): - print(f'critical missing header {h0}, abort parsing') - exit() - if not isinstance(input_headers_dict[h0], dict): + if h0 == 'east': + print(f'\x1b[1;31;40m'+f'CRITICAL: missing _east_ header above other headers (hierarchical) _ ABORT'+ '\x1b[0m') + exit() + 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'\x1b[1;31;40m'+f'CRITICAL ERROR: could not find any header to read _ ABORT'+ '\x1b[0m') + exit() return headers def parse_row(row, headers): From 4c2d61bb9bfed9281acb4e60d40c72aa9cb9d32a Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 14 Jan 2019 17:14:17 +0100 Subject: [PATCH 093/108] update test files Signed-off-by: Jean-Luc Auge --- tests/data/CORONET_Global_Topology.xls | Bin 48128 -> 30720 bytes .../CORONET_Global_Topology_expected.json | 4106 ++++++++++++----- tests/data/excelTestFile.xls | Bin 14336 -> 14336 bytes tests/data/excelTestFile_expected.json | 152 +- tests/data/meshTopologyExampleV2.xls | Bin 14336 -> 14336 bytes tests/data/meshTopologyExampleV2Eqpt.xls | Bin 14336 -> 14336 bytes .../meshTopologyExampleV2Eqpt_expected.json | 162 +- .../data/meshTopologyExampleV2_expected.json | 144 +- tests/test_parser.py | 2 +- 9 files changed, 3266 insertions(+), 1300 deletions(-) diff --git a/tests/data/CORONET_Global_Topology.xls b/tests/data/CORONET_Global_Topology.xls index 952d008b8a367d546300226d3a60024fb1dbd838..6dea7f16581b02d2c15d9f087f245b58cba949a5 100644 GIT binary patch literal 30720 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 bRPc8m>)iPLb5w=?{l%Yy|7)1PhyT9-MU{z0 literal 48128 zcmeHw2Ygjkw)ehignB5Uw@W9~kkBEd($h#n49C>J`$GK91$)qHMAE|2D$@27ijse0={o(z^ry%4WEJ3_WFNF1MQ=s zA)_M2r2&6v_mXgkC}`x5M3{IW;w-!i@iC+VQ7t+Ok?M2Fo_x8+?e#hT+h3_9gX};V z>OiJMM+8V0CXPbtBKneS-RZptz28rqPttp$=q|nyuZc~wH;tbTn5tdAR*V6k? zdhY`74)G|Jd50*I#31n=@JEa5u(QL>+d0yNEZo8+iiHo}|4(u_K#nI-0smn+DjW_m z-GWi2)hFpiaA8R;MwS<(Pe0jyad%aEK&}KCF^D#N^9x z2;gYGe>g<@%VKo63`UsfeHjh`ce$J#4cnXQL4zU&h)B4H7&?6DuyMnN#TCZRDj2P0 z3StLE3<2*y!1VpohS�yb~qKt8B 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 diff --git a/tests/data/CORONET_Global_Topology_expected.json b/tests/data/CORONET_Global_Topology_expected.json index 4dd8106b5..02657f9ad 100644 --- a/tests/data/CORONET_Global_Topology_expected.json +++ b/tests/data/CORONET_Global_Topology_expected.json @@ -1,5 +1,29 @@ { "elements": [ + { + "uid": "trx Abilene", + "metadata": { + "location": { + "city": "Abilene", + "region": "CONUS", + "latitude": 32.45, + "longitude": -99.739998 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Albany", + "metadata": { + "location": { + "city": "Albany", + "region": "CONUS", + "latitude": 42.6699982, + "longitude": -73.8000029 + } + }, + "type": "Transceiver" + }, { "uid": "trx Albuquerque", "metadata": { @@ -24,6 +48,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Austin", + "metadata": { + "location": { + "city": "Austin", + "region": "CONUS", + "latitude": 30.3099988, + "longitude": -97.7500018 + } + }, + "type": "Transceiver" + }, { "uid": "trx Baltimore", "metadata": { @@ -36,6 +72,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Baton_Rouge", + "metadata": { + "location": { + "city": "Baton_Rouge", + "region": "CONUS", + "latitude": 30.4499996, + "longitude": -91.1299968 + } + }, + "type": "Transceiver" + }, { "uid": "trx Billings", "metadata": { @@ -60,6 +108,66 @@ }, "type": "Transceiver" }, + { + "uid": "trx Bismarck", + "metadata": { + "location": { + "city": "Bismarck", + "region": "CONUS", + "latitude": 46.81000154, + "longitude": -100.7699965 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Boston", + "metadata": { + "location": { + "city": "Boston", + "region": "CONUS", + "latitude": 42.3400005, + "longitude": -71.0199959 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Buffalo", + "metadata": { + "location": { + "city": "Buffalo", + "region": "CONUS", + "latitude": 42.8899993, + "longitude": -78.860001 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Charleston", + "metadata": { + "location": { + "city": "Charleston", + "region": "CONUS", + "latitude": 32.7900008, + "longitude": -79.9899982 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Charlotte", + "metadata": { + "location": { + "city": "Charlotte", + "region": "CONUS", + "latitude": 35.2, + "longitude": -80.83 + } + }, + "type": "Transceiver" + }, { "uid": "trx Chicago", "metadata": { @@ -132,6 +240,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Detroit", + "metadata": { + "location": { + "city": "Detroit", + "region": "CONUS", + "latitude": 42.3800019, + "longitude": -83.0999998 + } + }, + "type": "Transceiver" + }, { "uid": "trx El_Paso", "metadata": { @@ -168,6 +288,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Hartford", + "metadata": { + "location": { + "city": "Hartford", + "region": "CONUS", + "latitude": 41.7700004, + "longitude": -72.6800003 + } + }, + "type": "Transceiver" + }, { "uid": "trx Houston", "metadata": { @@ -216,6 +348,30 @@ }, "type": "Transceiver" }, + { + "uid": "trx Little_Rock", + "metadata": { + "location": { + "city": "Little_Rock", + "region": "CONUS", + "latitude": 34.72, + "longitude": -92.35 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Long_Island", + "metadata": { + "location": { + "city": "Long_Island", + "region": "CONUS", + "latitude": 40.5899999, + "longitude": -73.6699993 + } + }, + "type": "Transceiver" + }, { "uid": "trx Los_Angeles", "metadata": { @@ -240,6 +396,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Memphis", + "metadata": { + "location": { + "city": "Memphis", + "region": "CONUS", + "latitude": 35.110001, + "longitude": -90.010004 + } + }, + "type": "Transceiver" + }, { "uid": "trx Miami", "metadata": { @@ -252,6 +420,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Milwaukee", + "metadata": { + "location": { + "city": "Milwaukee", + "region": "CONUS", + "latitude": 43.0600013, + "longitude": -87.9700005 + } + }, + "type": "Transceiver" + }, { "uid": "trx Minneapolis", "metadata": { @@ -300,6 +480,30 @@ }, "type": "Transceiver" }, + { + "uid": "trx Newark", + "metadata": { + "location": { + "city": "Newark", + "region": "CONUS", + "latitude": 40.7200012, + "longitude": -74.1699986 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Norfolk", + "metadata": { + "location": { + "city": "Norfolk", + "region": "CONUS", + "latitude": 36.9199982, + "longitude": -76.2399978 + } + }, + "type": "Transceiver" + }, { "uid": "trx Oakland", "metadata": { @@ -312,6 +516,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Oklahoma_City", + "metadata": { + "location": { + "city": "Oklahoma_City", + "region": "CONUS", + "latitude": 35.4700015, + "longitude": -97.5100028 + } + }, + "type": "Transceiver" + }, { "uid": "trx Omaha", "metadata": { @@ -324,6 +540,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Orlando", + "metadata": { + "location": { + "city": "Orlando", + "region": "CONUS", + "latitude": 28.4999994, + "longitude": -81.370003 + } + }, + "type": "Transceiver" + }, { "uid": "trx Philadelphia", "metadata": { @@ -372,6 +600,18 @@ }, "type": "Transceiver" }, + { + "uid": "trx Providence", + "metadata": { + "location": { + "city": "Providence", + "region": "CONUS", + "latitude": 41.82, + "longitude": -71.42 + } + }, + "type": "Transceiver" + }, { "uid": "trx Raleigh", "metadata": { @@ -385,181 +625,493 @@ "type": "Transceiver" }, { - "uid": "trx Salt_Lake_City", + "uid": "trx Richmond", "metadata": { "location": { - "city": "Salt_Lake_City", + "city": "Richmond", "region": "CONUS", - "latitude": 40.77999863, - "longitude": -111.9300007 + "latitude": 37.5299986, + "longitude": -77.4700015 } }, "type": "Transceiver" }, { - "uid": "trx Scranton", + "uid": "trx Rochester", "metadata": { "location": { - "city": "Scranton", + "city": "Rochester", "region": "CONUS", - "latitude": 41.4, - "longitude": -75.67 + "latitude": 43.1699985, + "longitude": -77.620003 } }, "type": "Transceiver" }, { - "uid": "trx St_Louis", + "uid": "trx Sacramento", "metadata": { "location": { - "city": "St_Louis", + "city": "Sacramento", "region": "CONUS", - "latitude": 38.64, - "longitude": -90.24 + "latitude": 38.56999946, + "longitude": -121.4700016 } }, "type": "Transceiver" }, { - "uid": "trx Syracuse", + "uid": "trx Salt_Lake_City", "metadata": { "location": { - "city": "Syracuse", + "city": "Salt_Lake_City", "region": "CONUS", - "latitude": 43.040001, - "longitude": -76.1399993 + "latitude": 40.77999863, + "longitude": -111.9300007 } }, "type": "Transceiver" }, { - "uid": "trx Washington_DC", + "uid": "trx San_Antonio", "metadata": { "location": { - "city": "Washington_DC", + "city": "San_Antonio", "region": "CONUS", - "latitude": 38.9100003, - "longitude": -77.0199965 + "latitude": 29.459997, + "longitude": -98.510002 } }, "type": "Transceiver" }, { - "uid": "trx Amsterdam", + "uid": "trx San_Diego", "metadata": { "location": { - "city": "Amsterdam", - "region": "Europe", - "latitude": 52.3699996, - "longitude": 4.88999915 + "city": "San_Diego", + "region": "CONUS", + "latitude": 32.8100017, + "longitude": -117.139999 } }, "type": "Transceiver" }, { - "uid": "trx Istanbul", + "uid": "trx San_Francisco", "metadata": { "location": { - "city": "Istanbul", - "region": "Europe", - "latitude": 41.1, - "longitude": 29.0 + "city": "San_Francisco", + "region": "CONUS", + "latitude": 37.65999942, + "longitude": -122.4199987 } }, "type": "Transceiver" }, { - "uid": "trx London", + "uid": "trx San_Jose", "metadata": { "location": { - "city": "London", - "region": "Europe", - "latitude": 51.5200005, - "longitude": -0.100000296 + "city": "San_Jose", + "region": "CONUS", + "latitude": 37.29999947, + "longitude": -121.8499985 } }, "type": "Transceiver" }, { - "uid": "trx Paris", + "uid": "trx Santa_Barbara", "metadata": { "location": { - "city": "Paris", - "region": "Europe", - "latitude": 48.86, - "longitude": 2.3399995 + "city": "Santa_Barbara", + "region": "CONUS", + "latitude": 34.43000021, + "longitude": -119.7200014 } }, "type": "Transceiver" }, { - "uid": "trx Rome", + "uid": "trx Scranton", "metadata": { "location": { - "city": "Rome", - "region": "Europe", - "latitude": 41.8899996, - "longitude": 12.5000004 + "city": "Scranton", + "region": "CONUS", + "latitude": 41.4, + "longitude": -75.67 } }, "type": "Transceiver" }, { - "uid": "trx Vienna", + "uid": "trx Seattle", "metadata": { "location": { - "city": "Vienna", - "region": "Europe", - "latitude": 48.2200024, - "longitude": 16.3700005 + "city": "Seattle", + "region": "CONUS", + "latitude": 47.61999916, + "longitude": -122.3499985 } }, "type": "Transceiver" }, { - "uid": "trx Warsaw", + "uid": "trx Spokane", "metadata": { "location": { - "city": "Warsaw", - "region": "Europe", - "latitude": 52.2599987, - "longitude": 21.0200005 + "city": "Spokane", + "region": "CONUS", + "latitude": 47.66999805, + "longitude": -117.4100038 } }, "type": "Transceiver" }, { - "uid": "trx Delhi", + "uid": "trx Springfield", "metadata": { "location": { - "city": "Delhi", - "region": "Asia", - "latitude": 28.6700003, - "longitude": 77.2099989 + "city": "Springfield", + "region": "CONUS", + "latitude": 39.5, + "longitude": -89.4 } }, "type": "Transceiver" }, { - "uid": "trx Hong_Kong", + "uid": "trx St_Louis", "metadata": { "location": { - "city": "Hong_Kong", - "region": "Asia", - "latitude": 22.267, - "longitude": 114.14 + "city": "St_Louis", + "region": "CONUS", + "latitude": 38.64, + "longitude": -90.24 } }, "type": "Transceiver" }, { - "uid": "trx Honolulu", + "uid": "trx Syracuse", "metadata": { "location": { - "city": "Honolulu", - "region": "Asia", - "latitude": 21.3199996, - "longitude": -157.800003 + "city": "Syracuse", + "region": "CONUS", + "latitude": 43.040001, + "longitude": -76.1399993 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tallahassee", + "metadata": { + "location": { + "city": "Tallahassee", + "region": "CONUS", + "latitude": 30.46, + "longitude": -84.28 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tampa", + "metadata": { + "location": { + "city": "Tampa", + "region": "CONUS", + "latitude": 27.9599988, + "longitude": -82.4800035 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Toledo", + "metadata": { + "location": { + "city": "Toledo", + "region": "CONUS", + "latitude": 41.659997, + "longitude": -83.58 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tucson", + "metadata": { + "location": { + "city": "Tucson", + "region": "CONUS", + "latitude": 32.2, + "longitude": -110.89 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tulsa", + "metadata": { + "location": { + "city": "Tulsa", + "region": "CONUS", + "latitude": 36.13, + "longitude": -95.92 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Washington_DC", + "metadata": { + "location": { + "city": "Washington_DC", + "region": "CONUS", + "latitude": 38.9100003, + "longitude": -77.0199965 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx West_Palm_Beach", + "metadata": { + "location": { + "city": "West_Palm_Beach", + "region": "CONUS", + "latitude": 26.7499997, + "longitude": -80.1299975 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Wilmington", + "metadata": { + "location": { + "city": "Wilmington", + "region": "CONUS", + "latitude": 39.7400018, + "longitude": -75.5299989 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Amsterdam", + "metadata": { + "location": { + "city": "Amsterdam", + "region": "Europe", + "latitude": 52.3699996, + "longitude": 4.88999915 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Berlin", + "metadata": { + "location": { + "city": "Berlin", + "region": "Europe", + "latitude": 52.520002, + "longitude": 13.379995 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Brussels", + "metadata": { + "location": { + "city": "Brussels", + "region": "Europe", + "latitude": 50.830002, + "longitude": 4.330002 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Bucharest", + "metadata": { + "location": { + "city": "Bucharest", + "region": "Europe", + "latitude": 44.44, + "longitude": 26.1 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Frankfurt", + "metadata": { + "location": { + "city": "Frankfurt", + "region": "Europe", + "latitude": 50.1199992, + "longitude": 8.68000104 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Istanbul", + "metadata": { + "location": { + "city": "Istanbul", + "region": "Europe", + "latitude": 41.1, + "longitude": 29.0 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx London", + "metadata": { + "location": { + "city": "London", + "region": "Europe", + "latitude": 51.5200005, + "longitude": -0.100000296 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Madrid", + "metadata": { + "location": { + "city": "Madrid", + "region": "Europe", + "latitude": 40.419998, + "longitude": -3.7100002 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Paris", + "metadata": { + "location": { + "city": "Paris", + "region": "Europe", + "latitude": 48.86, + "longitude": 2.3399995 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Rome", + "metadata": { + "location": { + "city": "Rome", + "region": "Europe", + "latitude": 41.8899996, + "longitude": 12.5000004 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Vienna", + "metadata": { + "location": { + "city": "Vienna", + "region": "Europe", + "latitude": 48.2200024, + "longitude": 16.3700005 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Warsaw", + "metadata": { + "location": { + "city": "Warsaw", + "region": "Europe", + "latitude": 52.2599987, + "longitude": 21.0200005 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Zurich", + "metadata": { + "location": { + "city": "Zurich", + "region": "Europe", + "latitude": 47.3800015, + "longitude": 8.5399996 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Bangkok", + "metadata": { + "location": { + "city": "Bangkok", + "region": "Asia", + "latitude": 13.73, + "longitude": 100.5 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Beijing", + "metadata": { + "location": { + "city": "Beijing", + "region": "Asia", + "latitude": 39.92999979, + "longitude": 116.4000013 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Delhi", + "metadata": { + "location": { + "city": "Delhi", + "region": "Asia", + "latitude": 28.6700003, + "longitude": 77.2099989 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Hong_Kong", + "metadata": { + "location": { + "city": "Hong_Kong", + "region": "Asia", + "latitude": 22.267, + "longitude": 114.14 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Honolulu", + "metadata": { + "location": { + "city": "Honolulu", + "region": "Asia", + "latitude": 21.3199996, + "longitude": -157.800003 } }, "type": "Transceiver" @@ -568,502 +1120,1042 @@ "uid": "trx Mumbai", "metadata": { "location": { - "city": "Mumbai", - "region": "Asia", - "latitude": 18.9599987, - "longitude": 72.8199999 + "city": "Mumbai", + "region": "Asia", + "latitude": 18.9599987, + "longitude": 72.8199999 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Seoul", + "metadata": { + "location": { + "city": "Seoul", + "region": "Asia", + "latitude": 37.56000108, + "longitude": 126.9899988 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Shanghai", + "metadata": { + "location": { + "city": "Shanghai", + "region": "Asia", + "latitude": 31.23, + "longitude": 121.47 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Singapore", + "metadata": { + "location": { + "city": "Singapore", + "region": "Asia", + "latitude": 1.299999907, + "longitude": 103.8499992 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Sydney", + "metadata": { + "location": { + "city": "Sydney", + "region": "Asia", + "latitude": -33.86999896, + "longitude": 151.2100066 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Taipei", + "metadata": { + "location": { + "city": "Taipei", + "region": "Asia", + "latitude": 25.0200005, + "longitude": 121.449997 + } + }, + "type": "Transceiver" + }, + { + "uid": "trx Tokyo", + "metadata": { + "location": { + "city": "Tokyo", + "region": "Asia", + "latitude": 35.6699986, + "longitude": 139.770004 + } + }, + "type": "Transceiver" + }, + { + "uid": "roadm Abilene", + "metadata": { + "location": { + "city": "Abilene", + "region": "CONUS", + "latitude": 32.45, + "longitude": -99.739998 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Albany", + "metadata": { + "location": { + "city": "Albany", + "region": "CONUS", + "latitude": 42.6699982, + "longitude": -73.8000029 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Albuquerque", + "metadata": { + "location": { + "city": "Albuquerque", + "region": "CONUS", + "latitude": 35.119977, + "longitude": -106.61997 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Atlanta", + "metadata": { + "location": { + "city": "Atlanta", + "region": "CONUS", + "latitude": 33.7599982, + "longitude": -84.4199987 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Austin", + "metadata": { + "location": { + "city": "Austin", + "region": "CONUS", + "latitude": 30.3099988, + "longitude": -97.7500018 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Baltimore", + "metadata": { + "location": { + "city": "Baltimore", + "region": "CONUS", + "latitude": 39.2999992, + "longitude": -76.6100008 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Baton_Rouge", + "metadata": { + "location": { + "city": "Baton_Rouge", + "region": "CONUS", + "latitude": 30.4499996, + "longitude": -91.1299968 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Billings", + "metadata": { + "location": { + "city": "Billings", + "region": "CONUS", + "latitude": 45.79000104, + "longitude": -108.5400006 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Birmingham", + "metadata": { + "location": { + "city": "Birmingham", + "region": "CONUS", + "latitude": 33.5299985, + "longitude": -86.8000029 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Bismarck", + "metadata": { + "location": { + "city": "Bismarck", + "region": "CONUS", + "latitude": 46.81000154, + "longitude": -100.7699965 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Boston", + "metadata": { + "location": { + "city": "Boston", + "region": "CONUS", + "latitude": 42.3400005, + "longitude": -71.0199959 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Buffalo", + "metadata": { + "location": { + "city": "Buffalo", + "region": "CONUS", + "latitude": 42.8899993, + "longitude": -78.860001 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Charleston", + "metadata": { + "location": { + "city": "Charleston", + "region": "CONUS", + "latitude": 32.7900008, + "longitude": -79.9899982 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Charlotte", + "metadata": { + "location": { + "city": "Charlotte", + "region": "CONUS", + "latitude": 35.2, + "longitude": -80.83 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Chicago", + "metadata": { + "location": { + "city": "Chicago", + "region": "CONUS", + "latitude": 41.839997, + "longitude": -87.680001 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Cincinnati", + "metadata": { + "location": { + "city": "Cincinnati", + "region": "CONUS", + "latitude": 39.1399991, + "longitude": -84.5100027 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Cleveland", + "metadata": { + "location": { + "city": "Cleveland", + "region": "CONUS", + "latitude": 41.4799992, + "longitude": -81.6800014 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Columbus", + "metadata": { + "location": { + "city": "Columbus", + "region": "CONUS", + "latitude": 39.990002, + "longitude": -82.989997 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Dallas", + "metadata": { + "location": { + "city": "Dallas", + "region": "CONUS", + "latitude": 32.79, + "longitude": -96.77 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Denver", + "metadata": { + "location": { + "city": "Denver", + "region": "CONUS", + "latitude": 39.77000271, + "longitude": -104.8700036 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Detroit", + "metadata": { + "location": { + "city": "Detroit", + "region": "CONUS", + "latitude": 42.3800019, + "longitude": -83.0999998 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm El_Paso", + "metadata": { + "location": { + "city": "El_Paso", + "region": "CONUS", + "latitude": 31.84981, + "longitude": -106.4396 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Fresno", + "metadata": { + "location": { + "city": "Fresno", + "region": "CONUS", + "latitude": 36.7800007, + "longitude": -119.790002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Greensboro", + "metadata": { + "location": { + "city": "Greensboro", + "region": "CONUS", + "latitude": 36.0800024, + "longitude": -79.8300018 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Hartford", + "metadata": { + "location": { + "city": "Hartford", + "region": "CONUS", + "latitude": 41.7700004, + "longitude": -72.6800003 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Houston", + "metadata": { + "location": { + "city": "Houston", + "region": "CONUS", + "latitude": 29.77, + "longitude": -95.39 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Jacksonville", + "metadata": { + "location": { + "city": "Jacksonville", + "region": "CONUS", + "latitude": 30.330003, + "longitude": -81.660002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Kansas_City", + "metadata": { + "location": { + "city": "Kansas_City", + "region": "CONUS", + "latitude": 39.1199992, + "longitude": -94.7300038 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Las_Vegas", + "metadata": { + "location": { + "city": "Las_Vegas", + "region": "CONUS", + "latitude": 36.20999, + "longitude": -115.2199 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Little_Rock", + "metadata": { + "location": { + "city": "Little_Rock", + "region": "CONUS", + "latitude": 34.72, + "longitude": -92.35 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Long_Island", + "metadata": { + "location": { + "city": "Long_Island", + "region": "CONUS", + "latitude": 40.5899999, + "longitude": -73.6699993 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Los_Angeles", + "metadata": { + "location": { + "city": "Los_Angeles", + "region": "CONUS", + "latitude": 34.110001, + "longitude": -118.410002 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Louisville", + "metadata": { + "location": { + "city": "Louisville", + "region": "CONUS", + "latitude": 38.2200009, + "longitude": -85.7399979 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Memphis", + "metadata": { + "location": { + "city": "Memphis", + "region": "CONUS", + "latitude": 35.110001, + "longitude": -90.010004 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Sydney", + "uid": "roadm Miami", "metadata": { "location": { - "city": "Sydney", - "region": "Asia", - "latitude": -33.86999896, - "longitude": 151.2100066 + "city": "Miami", + "region": "CONUS", + "latitude": 25.7800006, + "longitude": -80.2099997 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Taipei", + "uid": "roadm Milwaukee", "metadata": { "location": { - "city": "Taipei", - "region": "Asia", - "latitude": 25.0200005, - "longitude": 121.449997 + "city": "Milwaukee", + "region": "CONUS", + "latitude": 43.0600013, + "longitude": -87.9700005 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "trx Tokyo", + "uid": "roadm Minneapolis", "metadata": { "location": { - "city": "Tokyo", - "region": "Asia", - "latitude": 35.6699986, - "longitude": 139.770004 + "city": "Minneapolis", + "region": "CONUS", + "latitude": 44.9599988, + "longitude": -93.2699973 } }, - "type": "Transceiver" + "type": "Roadm" }, { - "uid": "roadm Albuquerque", + "uid": "roadm Nashville", "metadata": { "location": { - "city": "Albuquerque", + "city": "Nashville", "region": "CONUS", - "latitude": 35.119977, - "longitude": -106.61997 + "latitude": 36.1699984, + "longitude": -86.7799989 } }, "type": "Roadm" }, { - "uid": "roadm Atlanta", + "uid": "roadm New_Orleans", "metadata": { "location": { - "city": "Atlanta", + "city": "New_Orleans", "region": "CONUS", - "latitude": 33.7599982, - "longitude": -84.4199987 + "latitude": 30.07, + "longitude": -89.93 } }, "type": "Roadm" }, { - "uid": "roadm Baltimore", + "uid": "roadm New_York", "metadata": { "location": { - "city": "Baltimore", + "city": "New_York", "region": "CONUS", - "latitude": 39.2999992, - "longitude": -76.6100008 + "latitude": 40.6699983, + "longitude": -73.9400035 } }, "type": "Roadm" }, { - "uid": "roadm Billings", + "uid": "roadm Newark", "metadata": { "location": { - "city": "Billings", + "city": "Newark", "region": "CONUS", - "latitude": 45.79000104, - "longitude": -108.5400006 + "latitude": 40.7200012, + "longitude": -74.1699986 } }, "type": "Roadm" }, { - "uid": "roadm Birmingham", + "uid": "roadm Norfolk", "metadata": { "location": { - "city": "Birmingham", + "city": "Norfolk", "region": "CONUS", - "latitude": 33.5299985, - "longitude": -86.8000029 + "latitude": 36.9199982, + "longitude": -76.2399978 } }, "type": "Roadm" }, { - "uid": "roadm Chicago", + "uid": "roadm Oakland", "metadata": { "location": { - "city": "Chicago", + "city": "Oakland", "region": "CONUS", - "latitude": 41.839997, - "longitude": -87.680001 + "latitude": 37.77000071, + "longitude": -122.2200016 } }, "type": "Roadm" }, { - "uid": "roadm Cincinnati", + "uid": "roadm Oklahoma_City", "metadata": { "location": { - "city": "Cincinnati", + "city": "Oklahoma_City", "region": "CONUS", - "latitude": 39.1399991, - "longitude": -84.5100027 + "latitude": 35.4700015, + "longitude": -97.5100028 } }, "type": "Roadm" }, { - "uid": "roadm Cleveland", + "uid": "roadm Omaha", "metadata": { "location": { - "city": "Cleveland", + "city": "Omaha", "region": "CONUS", - "latitude": 41.4799992, - "longitude": -81.6800014 + "latitude": 41.2599984, + "longitude": -96.0100022 } }, "type": "Roadm" }, { - "uid": "roadm Columbus", + "uid": "roadm Orlando", "metadata": { "location": { - "city": "Columbus", + "city": "Orlando", "region": "CONUS", - "latitude": 39.990002, - "longitude": -82.989997 + "latitude": 28.4999994, + "longitude": -81.370003 } }, "type": "Roadm" }, { - "uid": "roadm Dallas", + "uid": "roadm Philadelphia", "metadata": { "location": { - "city": "Dallas", + "city": "Philadelphia", "region": "CONUS", - "latitude": 32.79, - "longitude": -96.77 + "latitude": 40.0099985, + "longitude": -75.1299964 } }, "type": "Roadm" }, { - "uid": "roadm Denver", + "uid": "roadm Phoenix", "metadata": { "location": { - "city": "Denver", + "city": "Phoenix", "region": "CONUS", - "latitude": 39.77000271, - "longitude": -104.8700036 + "latitude": 33.54000058, + "longitude": -112.0699996 } }, "type": "Roadm" }, { - "uid": "roadm El_Paso", + "uid": "roadm Pittsburgh", "metadata": { "location": { - "city": "El_Paso", + "city": "Pittsburgh", "region": "CONUS", - "latitude": 31.84981, - "longitude": -106.4396 + "latitude": 40.3, + "longitude": -80.13 } }, "type": "Roadm" }, { - "uid": "roadm Fresno", + "uid": "roadm Portland", "metadata": { "location": { - "city": "Fresno", + "city": "Portland", "region": "CONUS", - "latitude": 36.7800007, - "longitude": -119.790002 + "latitude": 45.54000072, + "longitude": -122.6600035 } }, "type": "Roadm" }, { - "uid": "roadm Greensboro", + "uid": "roadm Providence", "metadata": { "location": { - "city": "Greensboro", + "city": "Providence", "region": "CONUS", - "latitude": 36.0800024, - "longitude": -79.8300018 + "latitude": 41.82, + "longitude": -71.42 } }, "type": "Roadm" }, { - "uid": "roadm Houston", + "uid": "roadm Raleigh", "metadata": { "location": { - "city": "Houston", + "city": "Raleigh", "region": "CONUS", - "latitude": 29.77, - "longitude": -95.39 + "latitude": 35.8199995, + "longitude": -78.6600034 } }, "type": "Roadm" }, { - "uid": "roadm Jacksonville", + "uid": "roadm Richmond", "metadata": { "location": { - "city": "Jacksonville", + "city": "Richmond", "region": "CONUS", - "latitude": 30.330003, - "longitude": -81.660002 + "latitude": 37.5299986, + "longitude": -77.4700015 } }, "type": "Roadm" }, { - "uid": "roadm Kansas_City", + "uid": "roadm Rochester", "metadata": { "location": { - "city": "Kansas_City", + "city": "Rochester", "region": "CONUS", - "latitude": 39.1199992, - "longitude": -94.7300038 + "latitude": 43.1699985, + "longitude": -77.620003 } }, "type": "Roadm" }, { - "uid": "roadm Las_Vegas", + "uid": "roadm Sacramento", "metadata": { "location": { - "city": "Las_Vegas", + "city": "Sacramento", "region": "CONUS", - "latitude": 36.20999, - "longitude": -115.2199 + "latitude": 38.56999946, + "longitude": -121.4700016 } }, "type": "Roadm" }, { - "uid": "roadm Los_Angeles", + "uid": "roadm Salt_Lake_City", "metadata": { "location": { - "city": "Los_Angeles", + "city": "Salt_Lake_City", "region": "CONUS", - "latitude": 34.110001, - "longitude": -118.410002 + "latitude": 40.77999863, + "longitude": -111.9300007 } }, "type": "Roadm" }, { - "uid": "roadm Louisville", + "uid": "roadm San_Antonio", "metadata": { "location": { - "city": "Louisville", + "city": "San_Antonio", "region": "CONUS", - "latitude": 38.2200009, - "longitude": -85.7399979 + "latitude": 29.459997, + "longitude": -98.510002 } }, "type": "Roadm" }, { - "uid": "roadm Miami", + "uid": "roadm San_Diego", "metadata": { "location": { - "city": "Miami", + "city": "San_Diego", "region": "CONUS", - "latitude": 25.7800006, - "longitude": -80.2099997 + "latitude": 32.8100017, + "longitude": -117.139999 } }, "type": "Roadm" }, { - "uid": "roadm Minneapolis", + "uid": "roadm San_Francisco", "metadata": { "location": { - "city": "Minneapolis", + "city": "San_Francisco", "region": "CONUS", - "latitude": 44.9599988, - "longitude": -93.2699973 + "latitude": 37.65999942, + "longitude": -122.4199987 } }, "type": "Roadm" }, { - "uid": "roadm Nashville", + "uid": "roadm San_Jose", "metadata": { "location": { - "city": "Nashville", + "city": "San_Jose", "region": "CONUS", - "latitude": 36.1699984, - "longitude": -86.7799989 + "latitude": 37.29999947, + "longitude": -121.8499985 } }, "type": "Roadm" }, { - "uid": "roadm New_Orleans", + "uid": "roadm Santa_Barbara", "metadata": { "location": { - "city": "New_Orleans", + "city": "Santa_Barbara", "region": "CONUS", - "latitude": 30.07, - "longitude": -89.93 + "latitude": 34.43000021, + "longitude": -119.7200014 } }, "type": "Roadm" }, { - "uid": "roadm New_York", + "uid": "roadm Scranton", "metadata": { "location": { - "city": "New_York", + "city": "Scranton", "region": "CONUS", - "latitude": 40.6699983, - "longitude": -73.9400035 + "latitude": 41.4, + "longitude": -75.67 } }, "type": "Roadm" }, { - "uid": "roadm Oakland", + "uid": "roadm Seattle", "metadata": { "location": { - "city": "Oakland", + "city": "Seattle", "region": "CONUS", - "latitude": 37.77000071, - "longitude": -122.2200016 + "latitude": 47.61999916, + "longitude": -122.3499985 } }, "type": "Roadm" }, { - "uid": "roadm Omaha", + "uid": "roadm Spokane", "metadata": { "location": { - "city": "Omaha", + "city": "Spokane", "region": "CONUS", - "latitude": 41.2599984, - "longitude": -96.0100022 + "latitude": 47.66999805, + "longitude": -117.4100038 } }, "type": "Roadm" }, { - "uid": "roadm Philadelphia", + "uid": "roadm Springfield", "metadata": { "location": { - "city": "Philadelphia", + "city": "Springfield", "region": "CONUS", - "latitude": 40.0099985, - "longitude": -75.1299964 + "latitude": 39.5, + "longitude": -89.4 } }, "type": "Roadm" }, { - "uid": "roadm Phoenix", + "uid": "roadm St_Louis", "metadata": { "location": { - "city": "Phoenix", + "city": "St_Louis", "region": "CONUS", - "latitude": 33.54000058, - "longitude": -112.0699996 + "latitude": 38.64, + "longitude": -90.24 } }, "type": "Roadm" }, { - "uid": "roadm Pittsburgh", + "uid": "roadm Syracuse", "metadata": { "location": { - "city": "Pittsburgh", + "city": "Syracuse", "region": "CONUS", - "latitude": 40.3, - "longitude": -80.13 + "latitude": 43.040001, + "longitude": -76.1399993 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tallahassee", + "metadata": { + "location": { + "city": "Tallahassee", + "region": "CONUS", + "latitude": 30.46, + "longitude": -84.28 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tampa", + "metadata": { + "location": { + "city": "Tampa", + "region": "CONUS", + "latitude": 27.9599988, + "longitude": -82.4800035 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Toledo", + "metadata": { + "location": { + "city": "Toledo", + "region": "CONUS", + "latitude": 41.659997, + "longitude": -83.58 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tucson", + "metadata": { + "location": { + "city": "Tucson", + "region": "CONUS", + "latitude": 32.2, + "longitude": -110.89 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Tulsa", + "metadata": { + "location": { + "city": "Tulsa", + "region": "CONUS", + "latitude": 36.13, + "longitude": -95.92 } }, "type": "Roadm" }, { - "uid": "roadm Portland", + "uid": "roadm Washington_DC", "metadata": { "location": { - "city": "Portland", + "city": "Washington_DC", "region": "CONUS", - "latitude": 45.54000072, - "longitude": -122.6600035 + "latitude": 38.9100003, + "longitude": -77.0199965 } }, "type": "Roadm" }, { - "uid": "roadm Raleigh", + "uid": "roadm West_Palm_Beach", "metadata": { "location": { - "city": "Raleigh", + "city": "West_Palm_Beach", "region": "CONUS", - "latitude": 35.8199995, - "longitude": -78.6600034 + "latitude": 26.7499997, + "longitude": -80.1299975 } }, "type": "Roadm" }, { - "uid": "roadm Salt_Lake_City", + "uid": "roadm Wilmington", "metadata": { "location": { - "city": "Salt_Lake_City", + "city": "Wilmington", "region": "CONUS", - "latitude": 40.77999863, - "longitude": -111.9300007 + "latitude": 39.7400018, + "longitude": -75.5299989 } }, "type": "Roadm" }, { - "uid": "roadm Scranton", + "uid": "roadm Amsterdam", "metadata": { "location": { - "city": "Scranton", - "region": "CONUS", - "latitude": 41.4, - "longitude": -75.67 + "city": "Amsterdam", + "region": "Europe", + "latitude": 52.3699996, + "longitude": 4.88999915 } }, "type": "Roadm" }, { - "uid": "roadm St_Louis", + "uid": "roadm Berlin", "metadata": { "location": { - "city": "St_Louis", - "region": "CONUS", - "latitude": 38.64, - "longitude": -90.24 + "city": "Berlin", + "region": "Europe", + "latitude": 52.520002, + "longitude": 13.379995 } }, "type": "Roadm" }, { - "uid": "roadm Syracuse", + "uid": "roadm Brussels", "metadata": { "location": { - "city": "Syracuse", - "region": "CONUS", - "latitude": 43.040001, - "longitude": -76.1399993 + "city": "Brussels", + "region": "Europe", + "latitude": 50.830002, + "longitude": 4.330002 } }, "type": "Roadm" }, { - "uid": "roadm Washington_DC", + "uid": "roadm Bucharest", "metadata": { "location": { - "city": "Washington_DC", - "region": "CONUS", - "latitude": 38.9100003, - "longitude": -77.0199965 + "city": "Bucharest", + "region": "Europe", + "latitude": 44.44, + "longitude": 26.1 } }, "type": "Roadm" }, { - "uid": "roadm Amsterdam", + "uid": "roadm Frankfurt", "metadata": { "location": { - "city": "Amsterdam", + "city": "Frankfurt", "region": "Europe", - "latitude": 52.3699996, - "longitude": 4.88999915 + "latitude": 50.1199992, + "longitude": 8.68000104 } }, "type": "Roadm" @@ -1092,6 +2184,18 @@ }, "type": "Roadm" }, + { + "uid": "roadm Madrid", + "metadata": { + "location": { + "city": "Madrid", + "region": "Europe", + "latitude": 40.419998, + "longitude": -3.7100002 + } + }, + "type": "Roadm" + }, { "uid": "roadm Paris", "metadata": { @@ -1140,6 +2244,42 @@ }, "type": "Roadm" }, + { + "uid": "roadm Zurich", + "metadata": { + "location": { + "city": "Zurich", + "region": "Europe", + "latitude": 47.3800015, + "longitude": 8.5399996 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Bangkok", + "metadata": { + "location": { + "city": "Bangkok", + "region": "Asia", + "latitude": 13.73, + "longitude": 100.5 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Beijing", + "metadata": { + "location": { + "city": "Beijing", + "region": "Asia", + "latitude": 39.92999979, + "longitude": 116.4000013 + } + }, + "type": "Roadm" + }, { "uid": "roadm Delhi", "metadata": { @@ -1188,6 +2328,42 @@ }, "type": "Roadm" }, + { + "uid": "roadm Seoul", + "metadata": { + "location": { + "city": "Seoul", + "region": "Asia", + "latitude": 37.56000108, + "longitude": 126.9899988 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Shanghai", + "metadata": { + "location": { + "city": "Shanghai", + "region": "Asia", + "latitude": 31.23, + "longitude": 121.47 + } + }, + "type": "Roadm" + }, + { + "uid": "roadm Singapore", + "metadata": { + "location": { + "city": "Singapore", + "region": "Asia", + "latitude": 1.299999907, + "longitude": 103.8499992 + } + }, + "type": "Roadm" + }, { "uid": "roadm Sydney", "metadata": { @@ -1225,7 +2401,7 @@ "type": "Roadm" }, { - "uid": "fiber (Abilene \u2192 Dallas)-", + "uid": "fiber (Abilene → Dallas)-", "metadata": { "location": { "latitude": 32.620000000000005, @@ -1243,7 +2419,7 @@ } }, { - "uid": "fiber (Abilene \u2192 El_Paso)-", + "uid": "fiber (Abilene → El_Paso)-", "metadata": { "location": { "latitude": 32.149905000000004, @@ -1261,7 +2437,7 @@ } }, { - "uid": "fiber (Albany \u2192 Boston)-", + "uid": "fiber (Albany → Boston)-", "metadata": { "location": { "latitude": 42.504999350000006, @@ -1279,7 +2455,7 @@ } }, { - "uid": "fiber (Albany \u2192 Syracuse)-", + "uid": "fiber (Albany → Syracuse)-", "metadata": { "location": { "latitude": 42.8549996, @@ -1297,7 +2473,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Dallas)-", + "uid": "fiber (Albuquerque → Dallas)-", "metadata": { "location": { "latitude": 33.9549885, @@ -1315,7 +2491,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Denver)-", + "uid": "fiber (Albuquerque → Denver)-", "metadata": { "location": { "latitude": 37.444989855, @@ -1333,7 +2509,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 El_Paso)-", + "uid": "fiber (Albuquerque → El_Paso)-", "metadata": { "location": { "latitude": 33.4848935, @@ -1351,7 +2527,7 @@ } }, { - "uid": "fiber (Albuquerque \u2192 Las_Vegas)-", + "uid": "fiber (Albuquerque → Las_Vegas)-", "metadata": { "location": { "latitude": 35.6649835, @@ -1369,7 +2545,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Berlin)-", + "uid": "fiber (Amsterdam → Berlin)-", "metadata": { "location": { "latitude": 52.4450008, @@ -1387,7 +2563,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Brussels)-", + "uid": "fiber (Amsterdam → Brussels)-", "metadata": { "location": { "latitude": 51.600000800000004, @@ -1405,7 +2581,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 Frankfurt)-", + "uid": "fiber (Amsterdam → Frankfurt)-", "metadata": { "location": { "latitude": 51.2449994, @@ -1423,7 +2599,7 @@ } }, { - "uid": "fiber (Amsterdam \u2192 New_York)-", + "uid": "fiber (Amsterdam → New_York)-", "metadata": { "location": { "latitude": 46.51999895, @@ -1441,7 +2617,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Birmingham)-", + "uid": "fiber (Atlanta → Birmingham)-", "metadata": { "location": { "latitude": 33.644998349999995, @@ -1459,7 +2635,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Charlotte)-", + "uid": "fiber (Atlanta → Charlotte)-", "metadata": { "location": { "latitude": 34.4799991, @@ -1477,7 +2653,7 @@ } }, { - "uid": "fiber (Atlanta \u2192 Jacksonville)-", + "uid": "fiber (Atlanta → Jacksonville)-", "metadata": { "location": { "latitude": 32.0450006, @@ -1495,7 +2671,7 @@ } }, { - "uid": "fiber (Austin \u2192 Houston)-", + "uid": "fiber (Austin → Houston)-", "metadata": { "location": { "latitude": 30.0399994, @@ -1513,7 +2689,7 @@ } }, { - "uid": "fiber (Austin \u2192 San_Antonio)-", + "uid": "fiber (Austin → San_Antonio)-", "metadata": { "location": { "latitude": 29.884997900000002, @@ -1531,7 +2707,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Philadelphia)-", + "uid": "fiber (Baltimore → Philadelphia)-", "metadata": { "location": { "latitude": 39.65499885, @@ -1549,7 +2725,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Pittsburgh)-", + "uid": "fiber (Baltimore → Pittsburgh)-", "metadata": { "location": { "latitude": 39.7999996, @@ -1567,7 +2743,7 @@ } }, { - "uid": "fiber (Baltimore \u2192 Washington_DC)-", + "uid": "fiber (Baltimore → Washington_DC)-", "metadata": { "location": { "latitude": 39.104999750000005, @@ -1585,7 +2761,7 @@ } }, { - "uid": "fiber (Bangkok \u2192 Delhi)-", + "uid": "fiber (Bangkok → Delhi)-", "metadata": { "location": { "latitude": 21.20000015, @@ -1603,7 +2779,7 @@ } }, { - "uid": "fiber (Bangkok \u2192 Hong_Kong)-", + "uid": "fiber (Bangkok → Hong_Kong)-", "metadata": { "location": { "latitude": 17.9985, @@ -1621,7 +2797,7 @@ } }, { - "uid": "fiber (Baton_Rouge \u2192 Houston)-", + "uid": "fiber (Baton_Rouge → Houston)-", "metadata": { "location": { "latitude": 30.1099998, @@ -1639,7 +2815,7 @@ } }, { - "uid": "fiber (Baton_Rouge \u2192 New_Orleans)-", + "uid": "fiber (Baton_Rouge → New_Orleans)-", "metadata": { "location": { "latitude": 30.259999800000003, @@ -1657,7 +2833,7 @@ } }, { - "uid": "fiber (Beijing \u2192 Seoul)-", + "uid": "fiber (Beijing → Seoul)-", "metadata": { "location": { "latitude": 38.745000434999994, @@ -1675,7 +2851,7 @@ } }, { - "uid": "fiber (Beijing \u2192 Shanghai)-", + "uid": "fiber (Beijing → Shanghai)-", "metadata": { "location": { "latitude": 35.579999895, @@ -1693,7 +2869,7 @@ } }, { - "uid": "fiber (Berlin \u2192 Warsaw)-", + "uid": "fiber (Berlin → Warsaw)-", "metadata": { "location": { "latitude": 52.390000349999994, @@ -1711,7 +2887,7 @@ } }, { - "uid": "fiber (Billings \u2192 Bismarck)-", + "uid": "fiber (Billings → Bismarck)-", "metadata": { "location": { "latitude": 46.30000129, @@ -1729,7 +2905,7 @@ } }, { - "uid": "fiber (Billings \u2192 Denver)-", + "uid": "fiber (Billings → Denver)-", "metadata": { "location": { "latitude": 42.780001874999996, @@ -1747,7 +2923,7 @@ } }, { - "uid": "fiber (Billings \u2192 Spokane)-", + "uid": "fiber (Billings → Spokane)-", "metadata": { "location": { "latitude": 46.729999545, @@ -1765,7 +2941,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 Nashville)-", + "uid": "fiber (Birmingham → Nashville)-", "metadata": { "location": { "latitude": 34.84999845, @@ -1783,7 +2959,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 New_Orleans)-", + "uid": "fiber (Birmingham → New_Orleans)-", "metadata": { "location": { "latitude": 31.79999925, @@ -1801,7 +2977,7 @@ } }, { - "uid": "fiber (Bismarck \u2192 Minneapolis)-", + "uid": "fiber (Bismarck → Minneapolis)-", "metadata": { "location": { "latitude": 45.88500017, @@ -1819,7 +2995,7 @@ } }, { - "uid": "fiber (Boston \u2192 Providence)-", + "uid": "fiber (Boston → Providence)-", "metadata": { "location": { "latitude": 42.08000025, @@ -1837,7 +3013,7 @@ } }, { - "uid": "fiber (Brussels \u2192 London)-", + "uid": "fiber (Brussels → London)-", "metadata": { "location": { "latitude": 51.17500125, @@ -1855,7 +3031,7 @@ } }, { - "uid": "fiber (Bucharest \u2192 Istanbul)-", + "uid": "fiber (Bucharest → Istanbul)-", "metadata": { "location": { "latitude": 42.769999999999996, @@ -1873,7 +3049,7 @@ } }, { - "uid": "fiber (Bucharest \u2192 Warsaw)-", + "uid": "fiber (Bucharest → Warsaw)-", "metadata": { "location": { "latitude": 48.34999935, @@ -1891,7 +3067,7 @@ } }, { - "uid": "fiber (Buffalo \u2192 Cleveland)-", + "uid": "fiber (Buffalo → Cleveland)-", "metadata": { "location": { "latitude": 42.184999250000004, @@ -1909,7 +3085,7 @@ } }, { - "uid": "fiber (Buffalo \u2192 Rochester)-", + "uid": "fiber (Buffalo → Rochester)-", "metadata": { "location": { "latitude": 43.029998899999995, @@ -1927,7 +3103,7 @@ } }, { - "uid": "fiber (Charleston \u2192 Jacksonville)-", + "uid": "fiber (Charleston → Jacksonville)-", "metadata": { "location": { "latitude": 31.560001900000003, @@ -1945,7 +3121,7 @@ } }, { - "uid": "fiber (Charleston \u2192 Raleigh)-", + "uid": "fiber (Charleston → Raleigh)-", "metadata": { "location": { "latitude": 34.30500015, @@ -1963,7 +3139,7 @@ } }, { - "uid": "fiber (Charlotte \u2192 Greensboro)-", + "uid": "fiber (Charlotte → Greensboro)-", "metadata": { "location": { "latitude": 35.6400012, @@ -1981,7 +3157,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Detroit)-", + "uid": "fiber (Chicago → Detroit)-", "metadata": { "location": { "latitude": 42.109999450000004, @@ -1999,7 +3175,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Milwaukee)-", + "uid": "fiber (Chicago → Milwaukee)-", "metadata": { "location": { "latitude": 42.44999915, @@ -2017,7 +3193,7 @@ } }, { - "uid": "fiber (Chicago \u2192 Springfield)-", + "uid": "fiber (Chicago → Springfield)-", "metadata": { "location": { "latitude": 40.6699985, @@ -2035,7 +3211,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Columbus)-", + "uid": "fiber (Cincinnati → Columbus)-", "metadata": { "location": { "latitude": 39.56500054999999, @@ -2053,7 +3229,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Louisville)-", + "uid": "fiber (Cincinnati → Louisville)-", "metadata": { "location": { "latitude": 38.68, @@ -2071,7 +3247,7 @@ } }, { - "uid": "fiber (Cincinnati \u2192 Washington_DC)-", + "uid": "fiber (Cincinnati → Washington_DC)-", "metadata": { "location": { "latitude": 39.024999699999995, @@ -2089,7 +3265,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Columbus)-", + "uid": "fiber (Cleveland → Columbus)-", "metadata": { "location": { "latitude": 40.7350006, @@ -2107,7 +3283,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Toledo)-", + "uid": "fiber (Cleveland → Toledo)-", "metadata": { "location": { "latitude": 41.5699981, @@ -2125,7 +3301,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Pittsburgh)-", + "uid": "fiber (Columbus → Pittsburgh)-", "metadata": { "location": { "latitude": 40.14500099999999, @@ -2143,7 +3319,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Houston)-", + "uid": "fiber (Dallas → Houston)-", "metadata": { "location": { "latitude": 31.28, @@ -2161,7 +3337,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Little_Rock)-", + "uid": "fiber (Dallas → Little_Rock)-", "metadata": { "location": { "latitude": 33.754999999999995, @@ -2179,7 +3355,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Oklahoma_City)-", + "uid": "fiber (Dallas → Oklahoma_City)-", "metadata": { "location": { "latitude": 34.13000075, @@ -2197,7 +3373,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Istanbul)-", + "uid": "fiber (Delhi → Istanbul)-", "metadata": { "location": { "latitude": 34.88500015, @@ -2215,7 +3391,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Mumbai)-", + "uid": "fiber (Delhi → Mumbai)-", "metadata": { "location": { "latitude": 23.8149995, @@ -2233,7 +3409,7 @@ } }, { - "uid": "fiber (Denver \u2192 Omaha)-", + "uid": "fiber (Denver → Omaha)-", "metadata": { "location": { "latitude": 40.515000555, @@ -2251,7 +3427,7 @@ } }, { - "uid": "fiber (Denver \u2192 Salt_Lake_City)-", + "uid": "fiber (Denver → Salt_Lake_City)-", "metadata": { "location": { "latitude": 40.27500067, @@ -2269,7 +3445,7 @@ } }, { - "uid": "fiber (Detroit \u2192 Toledo)-", + "uid": "fiber (Detroit → Toledo)-", "metadata": { "location": { "latitude": 42.01999945, @@ -2287,7 +3463,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 San_Antonio)-", + "uid": "fiber (El_Paso → San_Antonio)-", "metadata": { "location": { "latitude": 30.654903500000003, @@ -2305,7 +3481,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Tucson)-", + "uid": "fiber (El_Paso → Tucson)-", "metadata": { "location": { "latitude": 32.024905000000004, @@ -2323,7 +3499,7 @@ } }, { - "uid": "fiber (Frankfurt \u2192 Vienna)-", + "uid": "fiber (Frankfurt → Vienna)-", "metadata": { "location": { "latitude": 49.1700008, @@ -2341,7 +3517,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Las_Vegas)-", + "uid": "fiber (Fresno → Las_Vegas)-", "metadata": { "location": { "latitude": 36.494995349999996, @@ -2359,7 +3535,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Los_Angeles)-", + "uid": "fiber (Fresno → Los_Angeles)-", "metadata": { "location": { "latitude": 35.44500085, @@ -2377,7 +3553,7 @@ } }, { - "uid": "fiber (Fresno \u2192 Oakland)-", + "uid": "fiber (Fresno → Oakland)-", "metadata": { "location": { "latitude": 37.275000705, @@ -2395,7 +3571,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Louisville)-", + "uid": "fiber (Greensboro → Louisville)-", "metadata": { "location": { "latitude": 37.15000165, @@ -2413,7 +3589,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Raleigh)-", + "uid": "fiber (Greensboro → Raleigh)-", "metadata": { "location": { "latitude": 35.95000095, @@ -2431,7 +3607,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Richmond)-", + "uid": "fiber (Greensboro → Richmond)-", "metadata": { "location": { "latitude": 36.8050005, @@ -2449,7 +3625,7 @@ } }, { - "uid": "fiber (Hartford \u2192 Long_Island)-", + "uid": "fiber (Hartford → Long_Island)-", "metadata": { "location": { "latitude": 41.18000015, @@ -2467,7 +3643,7 @@ } }, { - "uid": "fiber (Hartford \u2192 Providence)-", + "uid": "fiber (Hartford → Providence)-", "metadata": { "location": { "latitude": 41.795000200000004, @@ -2485,7 +3661,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Shanghai)-", + "uid": "fiber (Hong_Kong → Shanghai)-", "metadata": { "location": { "latitude": 26.7485, @@ -2503,7 +3679,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Sydney)-", + "uid": "fiber (Hong_Kong → Sydney)-", "metadata": { "location": { "latitude": -5.801499479999999, @@ -2521,7 +3697,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Taipei)-", + "uid": "fiber (Hong_Kong → Taipei)-", "metadata": { "location": { "latitude": 23.64350025, @@ -2539,7 +3715,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Los_Angeles)-", + "uid": "fiber (Honolulu → Los_Angeles)-", "metadata": { "location": { "latitude": 27.7150003, @@ -2557,7 +3733,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Sydney)-", + "uid": "fiber (Honolulu → Sydney)-", "metadata": { "location": { "latitude": -6.274999679999999, @@ -2575,7 +3751,7 @@ } }, { - "uid": "fiber (Honolulu \u2192 Taipei)-", + "uid": "fiber (Honolulu → Taipei)-", "metadata": { "location": { "latitude": 23.17000005, @@ -2593,7 +3769,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Rome)-", + "uid": "fiber (Istanbul → Rome)-", "metadata": { "location": { "latitude": 41.4949998, @@ -2611,7 +3787,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Orlando)-", + "uid": "fiber (Jacksonville → Orlando)-", "metadata": { "location": { "latitude": 29.4150012, @@ -2629,7 +3805,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 Omaha)-", + "uid": "fiber (Kansas_City → Omaha)-", "metadata": { "location": { "latitude": 40.1899988, @@ -2647,7 +3823,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 St_Louis)-", + "uid": "fiber (Kansas_City → St_Louis)-", "metadata": { "location": { "latitude": 38.879999600000005, @@ -2665,7 +3841,7 @@ } }, { - "uid": "fiber (Kansas_City \u2192 Tulsa)-", + "uid": "fiber (Kansas_City → Tulsa)-", "metadata": { "location": { "latitude": 37.6249996, @@ -2683,7 +3859,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Phoenix)-", + "uid": "fiber (Las_Vegas → Phoenix)-", "metadata": { "location": { "latitude": 34.87499529, @@ -2701,7 +3877,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Salt_Lake_City)-", + "uid": "fiber (Las_Vegas → Salt_Lake_City)-", "metadata": { "location": { "latitude": 38.494994315, @@ -2719,7 +3895,7 @@ } }, { - "uid": "fiber (Little_Rock \u2192 Memphis)-", + "uid": "fiber (Little_Rock → Memphis)-", "metadata": { "location": { "latitude": 34.9150005, @@ -2737,7 +3913,7 @@ } }, { - "uid": "fiber (London \u2192 Paris)-", + "uid": "fiber (London → Paris)-", "metadata": { "location": { "latitude": 50.19000025, @@ -2755,7 +3931,7 @@ } }, { - "uid": "fiber (London \u2192 Washington_DC)-", + "uid": "fiber (London → Washington_DC)-", "metadata": { "location": { "latitude": 45.2150004, @@ -2773,7 +3949,7 @@ } }, { - "uid": "fiber (Long_Island \u2192 New_York)-", + "uid": "fiber (Long_Island → New_York)-", "metadata": { "location": { "latitude": 40.629999100000006, @@ -2791,7 +3967,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 San_Diego)-", + "uid": "fiber (Los_Angeles → San_Diego)-", "metadata": { "location": { "latitude": 33.46000135, @@ -2809,7 +3985,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Santa_Barbara)-", + "uid": "fiber (Los_Angeles → Santa_Barbara)-", "metadata": { "location": { "latitude": 34.270000605, @@ -2827,7 +4003,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Nashville)-", + "uid": "fiber (Louisville → Nashville)-", "metadata": { "location": { "latitude": 37.19499965, @@ -2845,7 +4021,7 @@ } }, { - "uid": "fiber (Louisville \u2192 St_Louis)-", + "uid": "fiber (Louisville → St_Louis)-", "metadata": { "location": { "latitude": 38.43000045, @@ -2863,7 +4039,7 @@ } }, { - "uid": "fiber (Madrid \u2192 Paris)-", + "uid": "fiber (Madrid → Paris)-", "metadata": { "location": { "latitude": 44.639999, @@ -2881,7 +4057,7 @@ } }, { - "uid": "fiber (Madrid \u2192 Zurich)-", + "uid": "fiber (Madrid → Zurich)-", "metadata": { "location": { "latitude": 43.89999975, @@ -2899,7 +4075,7 @@ } }, { - "uid": "fiber (Memphis \u2192 Nashville)-", + "uid": "fiber (Memphis → Nashville)-", "metadata": { "location": { "latitude": 35.6399997, @@ -2917,7 +4093,7 @@ } }, { - "uid": "fiber (Miami \u2192 Paris)-", + "uid": "fiber (Miami → Paris)-", "metadata": { "location": { "latitude": 37.320000300000004, @@ -2935,7 +4111,7 @@ } }, { - "uid": "fiber (Miami \u2192 Tampa)-", + "uid": "fiber (Miami → Tampa)-", "metadata": { "location": { "latitude": 26.8699997, @@ -2953,7 +4129,7 @@ } }, { - "uid": "fiber (Miami \u2192 West_Palm_Beach)-", + "uid": "fiber (Miami → West_Palm_Beach)-", "metadata": { "location": { "latitude": 26.26500015, @@ -2971,7 +4147,7 @@ } }, { - "uid": "fiber (Milwaukee \u2192 Minneapolis)-", + "uid": "fiber (Milwaukee → Minneapolis)-", "metadata": { "location": { "latitude": 44.01000005, @@ -2989,7 +4165,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Omaha)-", + "uid": "fiber (Minneapolis → Omaha)-", "metadata": { "location": { "latitude": 43.1099986, @@ -3007,7 +4183,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Rome)-", + "uid": "fiber (Mumbai → Rome)-", "metadata": { "location": { "latitude": 30.42499915, @@ -3025,7 +4201,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Singapore)-", + "uid": "fiber (Mumbai → Singapore)-", "metadata": { "location": { "latitude": 10.1299993035, @@ -3043,7 +4219,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Tallahassee)-", + "uid": "fiber (New_Orleans → Tallahassee)-", "metadata": { "location": { "latitude": 30.265, @@ -3061,7 +4237,7 @@ } }, { - "uid": "fiber (New_York \u2192 Newark)-", + "uid": "fiber (New_York → Newark)-", "metadata": { "location": { "latitude": 40.69499975, @@ -3079,7 +4255,7 @@ } }, { - "uid": "fiber (New_York \u2192 Scranton)-", + "uid": "fiber (New_York → Scranton)-", "metadata": { "location": { "latitude": 41.034999150000004, @@ -3097,7 +4273,7 @@ } }, { - "uid": "fiber (New_York \u2192 Wilmington)-", + "uid": "fiber (New_York → Wilmington)-", "metadata": { "location": { "latitude": 40.20500005, @@ -3115,7 +4291,7 @@ } }, { - "uid": "fiber (Newark \u2192 Philadelphia)-", + "uid": "fiber (Newark → Philadelphia)-", "metadata": { "location": { "latitude": 40.364999850000004, @@ -3133,7 +4309,7 @@ } }, { - "uid": "fiber (Norfolk \u2192 Raleigh)-", + "uid": "fiber (Norfolk → Raleigh)-", "metadata": { "location": { "latitude": 36.36999885, @@ -3151,7 +4327,7 @@ } }, { - "uid": "fiber (Norfolk \u2192 Wilmington)-", + "uid": "fiber (Norfolk → Wilmington)-", "metadata": { "location": { "latitude": 38.33, @@ -3169,7 +4345,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Sacramento)-", + "uid": "fiber (Oakland → Sacramento)-", "metadata": { "location": { "latitude": 38.170000085, @@ -3187,7 +4363,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Salt_Lake_City)-", + "uid": "fiber (Oakland → Salt_Lake_City)-", "metadata": { "location": { "latitude": 39.27499967, @@ -3205,7 +4381,7 @@ } }, { - "uid": "fiber (Oakland \u2192 San_Francisco)-", + "uid": "fiber (Oakland → San_Francisco)-", "metadata": { "location": { "latitude": 37.715000065, @@ -3223,7 +4399,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Taipei)-", + "uid": "fiber (Oakland → Taipei)-", "metadata": { "location": { "latitude": 31.395000605, @@ -3241,7 +4417,7 @@ } }, { - "uid": "fiber (Oklahoma_City \u2192 Tulsa)-", + "uid": "fiber (Oklahoma_City → Tulsa)-", "metadata": { "location": { "latitude": 35.80000075, @@ -3259,7 +4435,7 @@ } }, { - "uid": "fiber (Orlando \u2192 West_Palm_Beach)-", + "uid": "fiber (Orlando → West_Palm_Beach)-", "metadata": { "location": { "latitude": 27.62499955, @@ -3277,7 +4453,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Scranton)-", + "uid": "fiber (Philadelphia → Scranton)-", "metadata": { "location": { "latitude": 40.70499925, @@ -3295,7 +4471,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 San_Diego)-", + "uid": "fiber (Phoenix → San_Diego)-", "metadata": { "location": { "latitude": 33.17500114, @@ -3313,7 +4489,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 Tucson)-", + "uid": "fiber (Phoenix → Tucson)-", "metadata": { "location": { "latitude": 32.87000029, @@ -3331,7 +4507,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Scranton)-", + "uid": "fiber (Pittsburgh → Scranton)-", "metadata": { "location": { "latitude": 40.849999999999994, @@ -3349,7 +4525,7 @@ } }, { - "uid": "fiber (Portland \u2192 Sacramento)-", + "uid": "fiber (Portland → Sacramento)-", "metadata": { "location": { "latitude": 42.05500009, @@ -3367,7 +4543,7 @@ } }, { - "uid": "fiber (Portland \u2192 Salt_Lake_City)-", + "uid": "fiber (Portland → Salt_Lake_City)-", "metadata": { "location": { "latitude": 43.159999675, @@ -3385,7 +4561,7 @@ } }, { - "uid": "fiber (Portland \u2192 Seattle)-", + "uid": "fiber (Portland → Seattle)-", "metadata": { "location": { "latitude": 46.57999994, @@ -3403,7 +4579,7 @@ } }, { - "uid": "fiber (Portland \u2192 Tokyo)-", + "uid": "fiber (Portland → Tokyo)-", "metadata": { "location": { "latitude": 40.604999660000004, @@ -3421,7 +4597,7 @@ } }, { - "uid": "fiber (Richmond \u2192 Washington_DC)-", + "uid": "fiber (Richmond → Washington_DC)-", "metadata": { "location": { "latitude": 38.21999945, @@ -3439,7 +4615,7 @@ } }, { - "uid": "fiber (Rochester \u2192 Syracuse)-", + "uid": "fiber (Rochester → Syracuse)-", "metadata": { "location": { "latitude": 43.10499975, @@ -3457,7 +4633,7 @@ } }, { - "uid": "fiber (Rome \u2192 Vienna)-", + "uid": "fiber (Rome → Vienna)-", "metadata": { "location": { "latitude": 45.055001000000004, @@ -3475,7 +4651,7 @@ } }, { - "uid": "fiber (Rome \u2192 Zurich)-", + "uid": "fiber (Rome → Zurich)-", "metadata": { "location": { "latitude": 44.63500055, @@ -3493,7 +4669,7 @@ } }, { - "uid": "fiber (San_Francisco \u2192 San_Jose)-", + "uid": "fiber (San_Francisco → San_Jose)-", "metadata": { "location": { "latitude": 37.479999445000004, @@ -3511,7 +4687,7 @@ } }, { - "uid": "fiber (San_Jose \u2192 Santa_Barbara)-", + "uid": "fiber (San_Jose → Santa_Barbara)-", "metadata": { "location": { "latitude": 35.86499984, @@ -3529,7 +4705,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Syracuse)-", + "uid": "fiber (Scranton → Syracuse)-", "metadata": { "location": { "latitude": 42.2200005, @@ -3547,7 +4723,7 @@ } }, { - "uid": "fiber (Seattle \u2192 Spokane)-", + "uid": "fiber (Seattle → Spokane)-", "metadata": { "location": { "latitude": 47.644998605, @@ -3565,7 +4741,7 @@ } }, { - "uid": "fiber (Seoul \u2192 Tokyo)-", + "uid": "fiber (Seoul → Tokyo)-", "metadata": { "location": { "latitude": 36.614999839999996, @@ -3583,7 +4759,7 @@ } }, { - "uid": "fiber (Singapore \u2192 Sydney)-", + "uid": "fiber (Singapore → Sydney)-", "metadata": { "location": { "latitude": -16.2849995265, @@ -3601,7 +4777,7 @@ } }, { - "uid": "fiber (Springfield \u2192 St_Louis)-", + "uid": "fiber (Springfield → St_Louis)-", "metadata": { "location": { "latitude": 39.07, @@ -3619,7 +4795,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Tokyo)-", + "uid": "fiber (Taipei → Tokyo)-", "metadata": { "location": { "latitude": 30.344999549999997, @@ -3637,7 +4813,7 @@ } }, { - "uid": "fiber (Tallahassee \u2192 Tampa)-", + "uid": "fiber (Tallahassee → Tampa)-", "metadata": { "location": { "latitude": 29.2099994, @@ -3655,7 +4831,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Warsaw)-", + "uid": "fiber (Vienna → Warsaw)-", "metadata": { "location": { "latitude": 50.24000055, @@ -3673,7 +4849,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Abilene)-", + "uid": "fiber (Dallas → Abilene)-", "metadata": { "location": { "latitude": 32.620000000000005, @@ -3691,7 +4867,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Abilene)-", + "uid": "fiber (El_Paso → Abilene)-", "metadata": { "location": { "latitude": 32.149905000000004, @@ -3709,7 +4885,7 @@ } }, { - "uid": "fiber (Boston \u2192 Albany)-", + "uid": "fiber (Boston → Albany)-", "metadata": { "location": { "latitude": 42.504999350000006, @@ -3727,7 +4903,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Albany)-", + "uid": "fiber (Syracuse → Albany)-", "metadata": { "location": { "latitude": 42.8549996, @@ -3745,7 +4921,7 @@ } }, { - "uid": "fiber (Dallas \u2192 Albuquerque)-", + "uid": "fiber (Dallas → Albuquerque)-", "metadata": { "location": { "latitude": 33.9549885, @@ -3763,7 +4939,7 @@ } }, { - "uid": "fiber (Denver \u2192 Albuquerque)-", + "uid": "fiber (Denver → Albuquerque)-", "metadata": { "location": { "latitude": 37.444989855, @@ -3781,7 +4957,7 @@ } }, { - "uid": "fiber (El_Paso \u2192 Albuquerque)-", + "uid": "fiber (El_Paso → Albuquerque)-", "metadata": { "location": { "latitude": 33.4848935, @@ -3799,7 +4975,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Albuquerque)-", + "uid": "fiber (Las_Vegas → Albuquerque)-", "metadata": { "location": { "latitude": 35.6649835, @@ -3817,7 +4993,7 @@ } }, { - "uid": "fiber (Berlin \u2192 Amsterdam)-", + "uid": "fiber (Berlin → Amsterdam)-", "metadata": { "location": { "latitude": 52.4450008, @@ -3835,7 +5011,7 @@ } }, { - "uid": "fiber (Brussels \u2192 Amsterdam)-", + "uid": "fiber (Brussels → Amsterdam)-", "metadata": { "location": { "latitude": 51.600000800000004, @@ -3853,7 +5029,7 @@ } }, { - "uid": "fiber (Frankfurt \u2192 Amsterdam)-", + "uid": "fiber (Frankfurt → Amsterdam)-", "metadata": { "location": { "latitude": 51.2449994, @@ -3871,7 +5047,7 @@ } }, { - "uid": "fiber (New_York \u2192 Amsterdam)-", + "uid": "fiber (New_York → Amsterdam)-", "metadata": { "location": { "latitude": 46.51999895, @@ -3889,7 +5065,7 @@ } }, { - "uid": "fiber (Birmingham \u2192 Atlanta)-", + "uid": "fiber (Birmingham → Atlanta)-", "metadata": { "location": { "latitude": 33.644998349999995, @@ -3907,7 +5083,7 @@ } }, { - "uid": "fiber (Charlotte \u2192 Atlanta)-", + "uid": "fiber (Charlotte → Atlanta)-", "metadata": { "location": { "latitude": 34.4799991, @@ -3925,7 +5101,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Atlanta)-", + "uid": "fiber (Jacksonville → Atlanta)-", "metadata": { "location": { "latitude": 32.0450006, @@ -3943,7 +5119,7 @@ } }, { - "uid": "fiber (Houston \u2192 Austin)-", + "uid": "fiber (Houston → Austin)-", "metadata": { "location": { "latitude": 30.0399994, @@ -3961,7 +5137,7 @@ } }, { - "uid": "fiber (San_Antonio \u2192 Austin)-", + "uid": "fiber (San_Antonio → Austin)-", "metadata": { "location": { "latitude": 29.884997900000002, @@ -3979,7 +5155,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Baltimore)-", + "uid": "fiber (Philadelphia → Baltimore)-", "metadata": { "location": { "latitude": 39.65499885, @@ -3997,7 +5173,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Baltimore)-", + "uid": "fiber (Pittsburgh → Baltimore)-", "metadata": { "location": { "latitude": 39.7999996, @@ -4015,7 +5191,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Baltimore)-", + "uid": "fiber (Washington_DC → Baltimore)-", "metadata": { "location": { "latitude": 39.104999750000005, @@ -4033,7 +5209,7 @@ } }, { - "uid": "fiber (Delhi \u2192 Bangkok)-", + "uid": "fiber (Delhi → Bangkok)-", "metadata": { "location": { "latitude": 21.20000015, @@ -4051,7 +5227,7 @@ } }, { - "uid": "fiber (Hong_Kong \u2192 Bangkok)-", + "uid": "fiber (Hong_Kong → Bangkok)-", "metadata": { "location": { "latitude": 17.9985, @@ -4069,7 +5245,7 @@ } }, { - "uid": "fiber (Houston \u2192 Baton_Rouge)-", + "uid": "fiber (Houston → Baton_Rouge)-", "metadata": { "location": { "latitude": 30.1099998, @@ -4087,7 +5263,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Baton_Rouge)-", + "uid": "fiber (New_Orleans → Baton_Rouge)-", "metadata": { "location": { "latitude": 30.259999800000003, @@ -4105,7 +5281,7 @@ } }, { - "uid": "fiber (Seoul \u2192 Beijing)-", + "uid": "fiber (Seoul → Beijing)-", "metadata": { "location": { "latitude": 38.745000434999994, @@ -4123,7 +5299,7 @@ } }, { - "uid": "fiber (Shanghai \u2192 Beijing)-", + "uid": "fiber (Shanghai → Beijing)-", "metadata": { "location": { "latitude": 35.579999895, @@ -4141,7 +5317,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Berlin)-", + "uid": "fiber (Warsaw → Berlin)-", "metadata": { "location": { "latitude": 52.390000349999994, @@ -4159,7 +5335,7 @@ } }, { - "uid": "fiber (Bismarck \u2192 Billings)-", + "uid": "fiber (Bismarck → Billings)-", "metadata": { "location": { "latitude": 46.30000129, @@ -4177,7 +5353,7 @@ } }, { - "uid": "fiber (Denver \u2192 Billings)-", + "uid": "fiber (Denver → Billings)-", "metadata": { "location": { "latitude": 42.780001874999996, @@ -4195,7 +5371,7 @@ } }, { - "uid": "fiber (Spokane \u2192 Billings)-", + "uid": "fiber (Spokane → Billings)-", "metadata": { "location": { "latitude": 46.729999545, @@ -4213,7 +5389,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Birmingham)-", + "uid": "fiber (Nashville → Birmingham)-", "metadata": { "location": { "latitude": 34.84999845, @@ -4231,7 +5407,7 @@ } }, { - "uid": "fiber (New_Orleans \u2192 Birmingham)-", + "uid": "fiber (New_Orleans → Birmingham)-", "metadata": { "location": { "latitude": 31.79999925, @@ -4249,7 +5425,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Bismarck)-", + "uid": "fiber (Minneapolis → Bismarck)-", "metadata": { "location": { "latitude": 45.88500017, @@ -4267,7 +5443,7 @@ } }, { - "uid": "fiber (Providence \u2192 Boston)-", + "uid": "fiber (Providence → Boston)-", "metadata": { "location": { "latitude": 42.08000025, @@ -4285,7 +5461,7 @@ } }, { - "uid": "fiber (London \u2192 Brussels)-", + "uid": "fiber (London → Brussels)-", "metadata": { "location": { "latitude": 51.17500125, @@ -4303,7 +5479,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Bucharest)-", + "uid": "fiber (Istanbul → Bucharest)-", "metadata": { "location": { "latitude": 42.769999999999996, @@ -4321,7 +5497,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Bucharest)-", + "uid": "fiber (Warsaw → Bucharest)-", "metadata": { "location": { "latitude": 48.34999935, @@ -4339,7 +5515,7 @@ } }, { - "uid": "fiber (Cleveland \u2192 Buffalo)-", + "uid": "fiber (Cleveland → Buffalo)-", "metadata": { "location": { "latitude": 42.184999250000004, @@ -4357,7 +5533,7 @@ } }, { - "uid": "fiber (Rochester \u2192 Buffalo)-", + "uid": "fiber (Rochester → Buffalo)-", "metadata": { "location": { "latitude": 43.029998899999995, @@ -4375,7 +5551,7 @@ } }, { - "uid": "fiber (Jacksonville \u2192 Charleston)-", + "uid": "fiber (Jacksonville → Charleston)-", "metadata": { "location": { "latitude": 31.560001900000003, @@ -4393,7 +5569,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Charleston)-", + "uid": "fiber (Raleigh → Charleston)-", "metadata": { "location": { "latitude": 34.30500015, @@ -4411,7 +5587,7 @@ } }, { - "uid": "fiber (Greensboro \u2192 Charlotte)-", + "uid": "fiber (Greensboro → Charlotte)-", "metadata": { "location": { "latitude": 35.6400012, @@ -4429,7 +5605,7 @@ } }, { - "uid": "fiber (Detroit \u2192 Chicago)-", + "uid": "fiber (Detroit → Chicago)-", "metadata": { "location": { "latitude": 42.109999450000004, @@ -4447,7 +5623,7 @@ } }, { - "uid": "fiber (Milwaukee \u2192 Chicago)-", + "uid": "fiber (Milwaukee → Chicago)-", "metadata": { "location": { "latitude": 42.44999915, @@ -4465,7 +5641,7 @@ } }, { - "uid": "fiber (Springfield \u2192 Chicago)-", + "uid": "fiber (Springfield → Chicago)-", "metadata": { "location": { "latitude": 40.6699985, @@ -4483,7 +5659,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Cincinnati)-", + "uid": "fiber (Columbus → Cincinnati)-", "metadata": { "location": { "latitude": 39.56500054999999, @@ -4501,7 +5677,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Cincinnati)-", + "uid": "fiber (Louisville → Cincinnati)-", "metadata": { "location": { "latitude": 38.68, @@ -4519,7 +5695,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Cincinnati)-", + "uid": "fiber (Washington_DC → Cincinnati)-", "metadata": { "location": { "latitude": 39.024999699999995, @@ -4537,7 +5713,7 @@ } }, { - "uid": "fiber (Columbus \u2192 Cleveland)-", + "uid": "fiber (Columbus → Cleveland)-", "metadata": { "location": { "latitude": 40.7350006, @@ -4555,7 +5731,7 @@ } }, { - "uid": "fiber (Toledo \u2192 Cleveland)-", + "uid": "fiber (Toledo → Cleveland)-", "metadata": { "location": { "latitude": 41.5699981, @@ -4573,7 +5749,7 @@ } }, { - "uid": "fiber (Pittsburgh \u2192 Columbus)-", + "uid": "fiber (Pittsburgh → Columbus)-", "metadata": { "location": { "latitude": 40.14500099999999, @@ -4591,7 +5767,7 @@ } }, { - "uid": "fiber (Houston \u2192 Dallas)-", + "uid": "fiber (Houston → Dallas)-", "metadata": { "location": { "latitude": 31.28, @@ -4609,7 +5785,7 @@ } }, { - "uid": "fiber (Little_Rock \u2192 Dallas)-", + "uid": "fiber (Little_Rock → Dallas)-", "metadata": { "location": { "latitude": 33.754999999999995, @@ -4627,7 +5803,7 @@ } }, { - "uid": "fiber (Oklahoma_City \u2192 Dallas)-", + "uid": "fiber (Oklahoma_City → Dallas)-", "metadata": { "location": { "latitude": 34.13000075, @@ -4645,7 +5821,7 @@ } }, { - "uid": "fiber (Istanbul \u2192 Delhi)-", + "uid": "fiber (Istanbul → Delhi)-", "metadata": { "location": { "latitude": 34.88500015, @@ -4663,7 +5839,7 @@ } }, { - "uid": "fiber (Mumbai \u2192 Delhi)-", + "uid": "fiber (Mumbai → Delhi)-", "metadata": { "location": { "latitude": 23.8149995, @@ -4681,7 +5857,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Denver)-", + "uid": "fiber (Omaha → Denver)-", "metadata": { "location": { "latitude": 40.515000555, @@ -4699,7 +5875,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Denver)-", + "uid": "fiber (Salt_Lake_City → Denver)-", "metadata": { "location": { "latitude": 40.27500067, @@ -4717,7 +5893,7 @@ } }, { - "uid": "fiber (Toledo \u2192 Detroit)-", + "uid": "fiber (Toledo → Detroit)-", "metadata": { "location": { "latitude": 42.01999945, @@ -4735,7 +5911,7 @@ } }, { - "uid": "fiber (San_Antonio \u2192 El_Paso)-", + "uid": "fiber (San_Antonio → El_Paso)-", "metadata": { "location": { "latitude": 30.654903500000003, @@ -4753,7 +5929,7 @@ } }, { - "uid": "fiber (Tucson \u2192 El_Paso)-", + "uid": "fiber (Tucson → El_Paso)-", "metadata": { "location": { "latitude": 32.024905000000004, @@ -4771,7 +5947,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Frankfurt)-", + "uid": "fiber (Vienna → Frankfurt)-", "metadata": { "location": { "latitude": 49.1700008, @@ -4789,7 +5965,7 @@ } }, { - "uid": "fiber (Las_Vegas \u2192 Fresno)-", + "uid": "fiber (Las_Vegas → Fresno)-", "metadata": { "location": { "latitude": 36.494995349999996, @@ -4807,7 +5983,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Fresno)-", + "uid": "fiber (Los_Angeles → Fresno)-", "metadata": { "location": { "latitude": 35.44500085, @@ -4825,7 +6001,7 @@ } }, { - "uid": "fiber (Oakland \u2192 Fresno)-", + "uid": "fiber (Oakland → Fresno)-", "metadata": { "location": { "latitude": 37.275000705, @@ -4843,7 +6019,7 @@ } }, { - "uid": "fiber (Louisville \u2192 Greensboro)-", + "uid": "fiber (Louisville → Greensboro)-", "metadata": { "location": { "latitude": 37.15000165, @@ -4861,7 +6037,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Greensboro)-", + "uid": "fiber (Raleigh → Greensboro)-", "metadata": { "location": { "latitude": 35.95000095, @@ -4879,7 +6055,7 @@ } }, { - "uid": "fiber (Richmond \u2192 Greensboro)-", + "uid": "fiber (Richmond → Greensboro)-", "metadata": { "location": { "latitude": 36.8050005, @@ -4897,7 +6073,7 @@ } }, { - "uid": "fiber (Long_Island \u2192 Hartford)-", + "uid": "fiber (Long_Island → Hartford)-", "metadata": { "location": { "latitude": 41.18000015, @@ -4915,7 +6091,7 @@ } }, { - "uid": "fiber (Providence \u2192 Hartford)-", + "uid": "fiber (Providence → Hartford)-", "metadata": { "location": { "latitude": 41.795000200000004, @@ -4933,7 +6109,7 @@ } }, { - "uid": "fiber (Shanghai \u2192 Hong_Kong)-", + "uid": "fiber (Shanghai → Hong_Kong)-", "metadata": { "location": { "latitude": 26.7485, @@ -4951,7 +6127,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Hong_Kong)-", + "uid": "fiber (Sydney → Hong_Kong)-", "metadata": { "location": { "latitude": -5.801499479999999, @@ -4969,7 +6145,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Hong_Kong)-", + "uid": "fiber (Taipei → Hong_Kong)-", "metadata": { "location": { "latitude": 23.64350025, @@ -4987,7 +6163,7 @@ } }, { - "uid": "fiber (Los_Angeles \u2192 Honolulu)-", + "uid": "fiber (Los_Angeles → Honolulu)-", "metadata": { "location": { "latitude": 27.7150003, @@ -5005,7 +6181,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Honolulu)-", + "uid": "fiber (Sydney → Honolulu)-", "metadata": { "location": { "latitude": -6.274999679999999, @@ -5023,7 +6199,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Honolulu)-", + "uid": "fiber (Taipei → Honolulu)-", "metadata": { "location": { "latitude": 23.17000005, @@ -5041,7 +6217,7 @@ } }, { - "uid": "fiber (Rome \u2192 Istanbul)-", + "uid": "fiber (Rome → Istanbul)-", "metadata": { "location": { "latitude": 41.4949998, @@ -5059,7 +6235,7 @@ } }, { - "uid": "fiber (Orlando \u2192 Jacksonville)-", + "uid": "fiber (Orlando → Jacksonville)-", "metadata": { "location": { "latitude": 29.4150012, @@ -5077,7 +6253,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Kansas_City)-", + "uid": "fiber (Omaha → Kansas_City)-", "metadata": { "location": { "latitude": 40.1899988, @@ -5095,7 +6271,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Kansas_City)-", + "uid": "fiber (St_Louis → Kansas_City)-", "metadata": { "location": { "latitude": 38.879999600000005, @@ -5113,7 +6289,7 @@ } }, { - "uid": "fiber (Tulsa \u2192 Kansas_City)-", + "uid": "fiber (Tulsa → Kansas_City)-", "metadata": { "location": { "latitude": 37.6249996, @@ -5131,7 +6307,7 @@ } }, { - "uid": "fiber (Phoenix \u2192 Las_Vegas)-", + "uid": "fiber (Phoenix → Las_Vegas)-", "metadata": { "location": { "latitude": 34.87499529, @@ -5149,7 +6325,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Las_Vegas)-", + "uid": "fiber (Salt_Lake_City → Las_Vegas)-", "metadata": { "location": { "latitude": 38.494994315, @@ -5167,7 +6343,7 @@ } }, { - "uid": "fiber (Memphis \u2192 Little_Rock)-", + "uid": "fiber (Memphis → Little_Rock)-", "metadata": { "location": { "latitude": 34.9150005, @@ -5185,7 +6361,7 @@ } }, { - "uid": "fiber (Paris \u2192 London)-", + "uid": "fiber (Paris → London)-", "metadata": { "location": { "latitude": 50.19000025, @@ -5203,7 +6379,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 London)-", + "uid": "fiber (Washington_DC → London)-", "metadata": { "location": { "latitude": 45.2150004, @@ -5221,7 +6397,7 @@ } }, { - "uid": "fiber (New_York \u2192 Long_Island)-", + "uid": "fiber (New_York → Long_Island)-", "metadata": { "location": { "latitude": 40.629999100000006, @@ -5239,7 +6415,7 @@ } }, { - "uid": "fiber (San_Diego \u2192 Los_Angeles)-", + "uid": "fiber (San_Diego → Los_Angeles)-", "metadata": { "location": { "latitude": 33.46000135, @@ -5257,7 +6433,7 @@ } }, { - "uid": "fiber (Santa_Barbara \u2192 Los_Angeles)-", + "uid": "fiber (Santa_Barbara → Los_Angeles)-", "metadata": { "location": { "latitude": 34.270000605, @@ -5275,7 +6451,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Louisville)-", + "uid": "fiber (Nashville → Louisville)-", "metadata": { "location": { "latitude": 37.19499965, @@ -5293,7 +6469,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Louisville)-", + "uid": "fiber (St_Louis → Louisville)-", "metadata": { "location": { "latitude": 38.43000045, @@ -5311,7 +6487,7 @@ } }, { - "uid": "fiber (Paris \u2192 Madrid)-", + "uid": "fiber (Paris → Madrid)-", "metadata": { "location": { "latitude": 44.639999, @@ -5329,7 +6505,7 @@ } }, { - "uid": "fiber (Zurich \u2192 Madrid)-", + "uid": "fiber (Zurich → Madrid)-", "metadata": { "location": { "latitude": 43.89999975, @@ -5347,7 +6523,7 @@ } }, { - "uid": "fiber (Nashville \u2192 Memphis)-", + "uid": "fiber (Nashville → Memphis)-", "metadata": { "location": { "latitude": 35.6399997, @@ -5365,7 +6541,7 @@ } }, { - "uid": "fiber (Paris \u2192 Miami)-", + "uid": "fiber (Paris → Miami)-", "metadata": { "location": { "latitude": 37.320000300000004, @@ -5383,7 +6559,7 @@ } }, { - "uid": "fiber (Tampa \u2192 Miami)-", + "uid": "fiber (Tampa → Miami)-", "metadata": { "location": { "latitude": 26.8699997, @@ -5401,7 +6577,7 @@ } }, { - "uid": "fiber (West_Palm_Beach \u2192 Miami)-", + "uid": "fiber (West_Palm_Beach → Miami)-", "metadata": { "location": { "latitude": 26.26500015, @@ -5419,7 +6595,7 @@ } }, { - "uid": "fiber (Minneapolis \u2192 Milwaukee)-", + "uid": "fiber (Minneapolis → Milwaukee)-", "metadata": { "location": { "latitude": 44.01000005, @@ -5437,7 +6613,7 @@ } }, { - "uid": "fiber (Omaha \u2192 Minneapolis)-", + "uid": "fiber (Omaha → Minneapolis)-", "metadata": { "location": { "latitude": 43.1099986, @@ -5455,7 +6631,7 @@ } }, { - "uid": "fiber (Rome \u2192 Mumbai)-", + "uid": "fiber (Rome → Mumbai)-", "metadata": { "location": { "latitude": 30.42499915, @@ -5473,7 +6649,7 @@ } }, { - "uid": "fiber (Singapore \u2192 Mumbai)-", + "uid": "fiber (Singapore → Mumbai)-", "metadata": { "location": { "latitude": 10.1299993035, @@ -5491,7 +6667,7 @@ } }, { - "uid": "fiber (Tallahassee \u2192 New_Orleans)-", + "uid": "fiber (Tallahassee → New_Orleans)-", "metadata": { "location": { "latitude": 30.265, @@ -5509,7 +6685,7 @@ } }, { - "uid": "fiber (Newark \u2192 New_York)-", + "uid": "fiber (Newark → New_York)-", "metadata": { "location": { "latitude": 40.69499975, @@ -5527,7 +6703,7 @@ } }, { - "uid": "fiber (Scranton \u2192 New_York)-", + "uid": "fiber (Scranton → New_York)-", "metadata": { "location": { "latitude": 41.034999150000004, @@ -5545,7 +6721,7 @@ } }, { - "uid": "fiber (Wilmington \u2192 New_York)-", + "uid": "fiber (Wilmington → New_York)-", "metadata": { "location": { "latitude": 40.20500005, @@ -5563,7 +6739,7 @@ } }, { - "uid": "fiber (Philadelphia \u2192 Newark)-", + "uid": "fiber (Philadelphia → Newark)-", "metadata": { "location": { "latitude": 40.364999850000004, @@ -5581,7 +6757,7 @@ } }, { - "uid": "fiber (Raleigh \u2192 Norfolk)-", + "uid": "fiber (Raleigh → Norfolk)-", "metadata": { "location": { "latitude": 36.36999885, @@ -5599,7 +6775,7 @@ } }, { - "uid": "fiber (Wilmington \u2192 Norfolk)-", + "uid": "fiber (Wilmington → Norfolk)-", "metadata": { "location": { "latitude": 38.33, @@ -5617,7 +6793,7 @@ } }, { - "uid": "fiber (Sacramento \u2192 Oakland)-", + "uid": "fiber (Sacramento → Oakland)-", "metadata": { "location": { "latitude": 38.170000085, @@ -5635,7 +6811,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Oakland)-", + "uid": "fiber (Salt_Lake_City → Oakland)-", "metadata": { "location": { "latitude": 39.27499967, @@ -5653,7 +6829,7 @@ } }, { - "uid": "fiber (San_Francisco \u2192 Oakland)-", + "uid": "fiber (San_Francisco → Oakland)-", "metadata": { "location": { "latitude": 37.715000065, @@ -5671,7 +6847,7 @@ } }, { - "uid": "fiber (Taipei \u2192 Oakland)-", + "uid": "fiber (Taipei → Oakland)-", "metadata": { "location": { "latitude": 31.395000605, @@ -5689,7 +6865,7 @@ } }, { - "uid": "fiber (Tulsa \u2192 Oklahoma_City)-", + "uid": "fiber (Tulsa → Oklahoma_City)-", "metadata": { "location": { "latitude": 35.80000075, @@ -5707,7 +6883,7 @@ } }, { - "uid": "fiber (West_Palm_Beach \u2192 Orlando)-", + "uid": "fiber (West_Palm_Beach → Orlando)-", "metadata": { "location": { "latitude": 27.62499955, @@ -5725,7 +6901,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Philadelphia)-", + "uid": "fiber (Scranton → Philadelphia)-", "metadata": { "location": { "latitude": 40.70499925, @@ -5743,7 +6919,7 @@ } }, { - "uid": "fiber (San_Diego \u2192 Phoenix)-", + "uid": "fiber (San_Diego → Phoenix)-", "metadata": { "location": { "latitude": 33.17500114, @@ -5761,7 +6937,7 @@ } }, { - "uid": "fiber (Tucson \u2192 Phoenix)-", + "uid": "fiber (Tucson → Phoenix)-", "metadata": { "location": { "latitude": 32.87000029, @@ -5779,7 +6955,7 @@ } }, { - "uid": "fiber (Scranton \u2192 Pittsburgh)-", + "uid": "fiber (Scranton → Pittsburgh)-", "metadata": { "location": { "latitude": 40.849999999999994, @@ -5797,7 +6973,7 @@ } }, { - "uid": "fiber (Sacramento \u2192 Portland)-", + "uid": "fiber (Sacramento → Portland)-", "metadata": { "location": { "latitude": 42.05500009, @@ -5815,7 +6991,7 @@ } }, { - "uid": "fiber (Salt_Lake_City \u2192 Portland)-", + "uid": "fiber (Salt_Lake_City → Portland)-", "metadata": { "location": { "latitude": 43.159999675, @@ -5833,7 +7009,7 @@ } }, { - "uid": "fiber (Seattle \u2192 Portland)-", + "uid": "fiber (Seattle → Portland)-", "metadata": { "location": { "latitude": 46.57999994, @@ -5851,7 +7027,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Portland)-", + "uid": "fiber (Tokyo → Portland)-", "metadata": { "location": { "latitude": 40.604999660000004, @@ -5869,7 +7045,7 @@ } }, { - "uid": "fiber (Washington_DC \u2192 Richmond)-", + "uid": "fiber (Washington_DC → Richmond)-", "metadata": { "location": { "latitude": 38.21999945, @@ -5887,7 +7063,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Rochester)-", + "uid": "fiber (Syracuse → Rochester)-", "metadata": { "location": { "latitude": 43.10499975, @@ -5905,7 +7081,7 @@ } }, { - "uid": "fiber (Vienna \u2192 Rome)-", + "uid": "fiber (Vienna → Rome)-", "metadata": { "location": { "latitude": 45.055001000000004, @@ -5923,7 +7099,7 @@ } }, { - "uid": "fiber (Zurich \u2192 Rome)-", + "uid": "fiber (Zurich → Rome)-", "metadata": { "location": { "latitude": 44.63500055, @@ -5941,7 +7117,7 @@ } }, { - "uid": "fiber (San_Jose \u2192 San_Francisco)-", + "uid": "fiber (San_Jose → San_Francisco)-", "metadata": { "location": { "latitude": 37.479999445000004, @@ -5959,7 +7135,7 @@ } }, { - "uid": "fiber (Santa_Barbara \u2192 San_Jose)-", + "uid": "fiber (Santa_Barbara → San_Jose)-", "metadata": { "location": { "latitude": 35.86499984, @@ -5977,7 +7153,7 @@ } }, { - "uid": "fiber (Syracuse \u2192 Scranton)-", + "uid": "fiber (Syracuse → Scranton)-", "metadata": { "location": { "latitude": 42.2200005, @@ -5995,7 +7171,7 @@ } }, { - "uid": "fiber (Spokane \u2192 Seattle)-", + "uid": "fiber (Spokane → Seattle)-", "metadata": { "location": { "latitude": 47.644998605, @@ -6013,7 +7189,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Seoul)-", + "uid": "fiber (Tokyo → Seoul)-", "metadata": { "location": { "latitude": 36.614999839999996, @@ -6031,7 +7207,7 @@ } }, { - "uid": "fiber (Sydney \u2192 Singapore)-", + "uid": "fiber (Sydney → Singapore)-", "metadata": { "location": { "latitude": -16.2849995265, @@ -6049,7 +7225,7 @@ } }, { - "uid": "fiber (St_Louis \u2192 Springfield)-", + "uid": "fiber (St_Louis → Springfield)-", "metadata": { "location": { "latitude": 39.07, @@ -6067,7 +7243,7 @@ } }, { - "uid": "fiber (Tokyo \u2192 Taipei)-", + "uid": "fiber (Tokyo → Taipei)-", "metadata": { "location": { "latitude": 30.344999549999997, @@ -6085,7 +7261,7 @@ } }, { - "uid": "fiber (Tampa \u2192 Tallahassee)-", + "uid": "fiber (Tampa → Tallahassee)-", "metadata": { "location": { "latitude": 29.2099994, @@ -6103,7 +7279,7 @@ } }, { - "uid": "fiber (Warsaw \u2192 Vienna)-", + "uid": "fiber (Warsaw → Vienna)-", "metadata": { "location": { "latitude": 50.24000055, @@ -6123,1789 +7299,2197 @@ ], "connections": [ { - "from_node": "fiber (Dallas \u2192 Abilene)-", - "to_node": "fiber (Abilene \u2192 El_Paso)-" + "from_node": "roadm Abilene", + "to_node": "fiber (Abilene → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Abilene)-", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Abilene", + "to_node": "fiber (Abilene → El_Paso)-" + }, + { + "from_node": "fiber (El_Paso → Abilene)-", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Albany", + "to_node": "fiber (Albany → Boston)-" }, { - "from_node": "fiber (El_Paso \u2192 Abilene)-", - "to_node": "fiber (Abilene \u2192 Dallas)-" + "from_node": "fiber (Boston → Albany)-", + "to_node": "roadm Albany" }, { - "from_node": "fiber (Boston \u2192 Albany)-", - "to_node": "fiber (Albany \u2192 Syracuse)-" + "from_node": "roadm Albany", + "to_node": "fiber (Albany → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Albany)-", - "to_node": "fiber (Albany \u2192 Boston)-" + "from_node": "fiber (Syracuse → Albany)-", + "to_node": "roadm Albany" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Dallas)-" + "to_node": "fiber (Albuquerque → Dallas)-" }, { - "from_node": "fiber (Dallas \u2192 Albuquerque)-", + "from_node": "fiber (Dallas → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Denver)-" + "to_node": "fiber (Albuquerque → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Albuquerque)-", + "from_node": "fiber (Denver → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 El_Paso)-" + "to_node": "fiber (Albuquerque → El_Paso)-" }, { - "from_node": "fiber (El_Paso \u2192 Albuquerque)-", + "from_node": "fiber (El_Paso → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Albuquerque", - "to_node": "fiber (Albuquerque \u2192 Las_Vegas)-" + "to_node": "fiber (Albuquerque → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Albuquerque)-", + "from_node": "fiber (Las_Vegas → Albuquerque)-", "to_node": "roadm Albuquerque" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Birmingham)-" + "to_node": "fiber (Atlanta → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 Atlanta)-", + "from_node": "fiber (Birmingham → Atlanta)-", "to_node": "roadm Atlanta" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Charlotte)-" + "to_node": "fiber (Atlanta → Charlotte)-" }, { - "from_node": "fiber (Charlotte \u2192 Atlanta)-", + "from_node": "fiber (Charlotte → Atlanta)-", "to_node": "roadm Atlanta" }, { "from_node": "roadm Atlanta", - "to_node": "fiber (Atlanta \u2192 Jacksonville)-" + "to_node": "fiber (Atlanta → Jacksonville)-" }, { - "from_node": "fiber (Jacksonville \u2192 Atlanta)-", + "from_node": "fiber (Jacksonville → Atlanta)-", "to_node": "roadm Atlanta" }, { - "from_node": "fiber (Houston \u2192 Austin)-", - "to_node": "fiber (Austin \u2192 San_Antonio)-" + "from_node": "roadm Austin", + "to_node": "fiber (Austin → Houston)-" }, { - "from_node": "fiber (San_Antonio \u2192 Austin)-", - "to_node": "fiber (Austin \u2192 Houston)-" + "from_node": "fiber (Houston → Austin)-", + "to_node": "roadm Austin" + }, + { + "from_node": "roadm Austin", + "to_node": "fiber (Austin → San_Antonio)-" + }, + { + "from_node": "fiber (San_Antonio → Austin)-", + "to_node": "roadm Austin" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Philadelphia)-" + "to_node": "fiber (Baltimore → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Baltimore)-", + "from_node": "fiber (Philadelphia → Baltimore)-", "to_node": "roadm Baltimore" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Pittsburgh)-" + "to_node": "fiber (Baltimore → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Baltimore)-", + "from_node": "fiber (Pittsburgh → Baltimore)-", "to_node": "roadm Baltimore" }, { "from_node": "roadm Baltimore", - "to_node": "fiber (Baltimore \u2192 Washington_DC)-" + "to_node": "fiber (Baltimore → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 Baltimore)-", + "from_node": "fiber (Washington_DC → Baltimore)-", "to_node": "roadm Baltimore" }, { - "from_node": "fiber (Houston \u2192 Baton_Rouge)-", - "to_node": "fiber (Baton_Rouge \u2192 New_Orleans)-" + "from_node": "roadm Baton_Rouge", + "to_node": "fiber (Baton_Rouge → Houston)-" + }, + { + "from_node": "fiber (Houston → Baton_Rouge)-", + "to_node": "roadm Baton_Rouge" + }, + { + "from_node": "roadm Baton_Rouge", + "to_node": "fiber (Baton_Rouge → New_Orleans)-" }, { - "from_node": "fiber (New_Orleans \u2192 Baton_Rouge)-", - "to_node": "fiber (Baton_Rouge \u2192 Houston)-" + "from_node": "fiber (New_Orleans → Baton_Rouge)-", + "to_node": "roadm Baton_Rouge" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Bismarck)-" + "to_node": "fiber (Billings → Bismarck)-" }, { - "from_node": "fiber (Bismarck \u2192 Billings)-", + "from_node": "fiber (Bismarck → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Denver)-" + "to_node": "fiber (Billings → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Billings)-", + "from_node": "fiber (Denver → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Billings", - "to_node": "fiber (Billings \u2192 Spokane)-" + "to_node": "fiber (Billings → Spokane)-" }, { - "from_node": "fiber (Spokane \u2192 Billings)-", + "from_node": "fiber (Spokane → Billings)-", "to_node": "roadm Billings" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 Atlanta)-" + "to_node": "fiber (Birmingham → Atlanta)-" }, { - "from_node": "fiber (Atlanta \u2192 Birmingham)-", + "from_node": "fiber (Atlanta → Birmingham)-", "to_node": "roadm Birmingham" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 Nashville)-" + "to_node": "fiber (Birmingham → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Birmingham)-", + "from_node": "fiber (Nashville → Birmingham)-", "to_node": "roadm Birmingham" }, { "from_node": "roadm Birmingham", - "to_node": "fiber (Birmingham \u2192 New_Orleans)-" + "to_node": "fiber (Birmingham → New_Orleans)-" }, { - "from_node": "fiber (New_Orleans \u2192 Birmingham)-", + "from_node": "fiber (New_Orleans → Birmingham)-", "to_node": "roadm Birmingham" }, { - "from_node": "fiber (Billings \u2192 Bismarck)-", - "to_node": "fiber (Bismarck \u2192 Minneapolis)-" + "from_node": "roadm Bismarck", + "to_node": "fiber (Bismarck → Billings)-" + }, + { + "from_node": "fiber (Billings → Bismarck)-", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Bismarck", + "to_node": "fiber (Bismarck → Minneapolis)-" + }, + { + "from_node": "fiber (Minneapolis → Bismarck)-", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Boston", + "to_node": "fiber (Boston → Albany)-" }, { - "from_node": "fiber (Minneapolis \u2192 Bismarck)-", - "to_node": "fiber (Bismarck \u2192 Billings)-" + "from_node": "fiber (Albany → Boston)-", + "to_node": "roadm Boston" }, { - "from_node": "fiber (Albany \u2192 Boston)-", - "to_node": "fiber (Boston \u2192 Providence)-" + "from_node": "roadm Boston", + "to_node": "fiber (Boston → Providence)-" }, { - "from_node": "fiber (Providence \u2192 Boston)-", - "to_node": "fiber (Boston \u2192 Albany)-" + "from_node": "fiber (Providence → Boston)-", + "to_node": "roadm Boston" }, { - "from_node": "fiber (Cleveland \u2192 Buffalo)-", - "to_node": "fiber (Buffalo \u2192 Rochester)-" + "from_node": "roadm Buffalo", + "to_node": "fiber (Buffalo → Cleveland)-" }, { - "from_node": "fiber (Rochester \u2192 Buffalo)-", - "to_node": "fiber (Buffalo \u2192 Cleveland)-" + "from_node": "fiber (Cleveland → Buffalo)-", + "to_node": "roadm Buffalo" }, { - "from_node": "fiber (Jacksonville \u2192 Charleston)-", - "to_node": "fiber (Charleston \u2192 Raleigh)-" + "from_node": "roadm Buffalo", + "to_node": "fiber (Buffalo → Rochester)-" }, { - "from_node": "fiber (Raleigh \u2192 Charleston)-", - "to_node": "fiber (Charleston \u2192 Jacksonville)-" + "from_node": "fiber (Rochester → Buffalo)-", + "to_node": "roadm Buffalo" }, { - "from_node": "fiber (Atlanta \u2192 Charlotte)-", - "to_node": "fiber (Charlotte \u2192 Greensboro)-" + "from_node": "roadm Charleston", + "to_node": "fiber (Charleston → Jacksonville)-" }, { - "from_node": "fiber (Greensboro \u2192 Charlotte)-", - "to_node": "fiber (Charlotte \u2192 Atlanta)-" + "from_node": "fiber (Jacksonville → Charleston)-", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charleston", + "to_node": "fiber (Charleston → Raleigh)-" + }, + { + "from_node": "fiber (Raleigh → Charleston)-", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charlotte", + "to_node": "fiber (Charlotte → Atlanta)-" + }, + { + "from_node": "fiber (Atlanta → Charlotte)-", + "to_node": "roadm Charlotte" + }, + { + "from_node": "roadm Charlotte", + "to_node": "fiber (Charlotte → Greensboro)-" + }, + { + "from_node": "fiber (Greensboro → Charlotte)-", + "to_node": "roadm Charlotte" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Detroit)-" + "to_node": "fiber (Chicago → Detroit)-" }, { - "from_node": "fiber (Detroit \u2192 Chicago)-", + "from_node": "fiber (Detroit → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Milwaukee)-" + "to_node": "fiber (Chicago → Milwaukee)-" }, { - "from_node": "fiber (Milwaukee \u2192 Chicago)-", + "from_node": "fiber (Milwaukee → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Chicago", - "to_node": "fiber (Chicago \u2192 Springfield)-" + "to_node": "fiber (Chicago → Springfield)-" }, { - "from_node": "fiber (Springfield \u2192 Chicago)-", + "from_node": "fiber (Springfield → Chicago)-", "to_node": "roadm Chicago" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Columbus)-" + "to_node": "fiber (Cincinnati → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Cincinnati)-", + "from_node": "fiber (Columbus → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Louisville)-" + "to_node": "fiber (Cincinnati → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Cincinnati)-", + "from_node": "fiber (Louisville → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cincinnati", - "to_node": "fiber (Cincinnati \u2192 Washington_DC)-" + "to_node": "fiber (Cincinnati → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 Cincinnati)-", + "from_node": "fiber (Washington_DC → Cincinnati)-", "to_node": "roadm Cincinnati" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Buffalo)-" + "to_node": "fiber (Cleveland → Buffalo)-" }, { - "from_node": "fiber (Buffalo \u2192 Cleveland)-", + "from_node": "fiber (Buffalo → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Columbus)-" + "to_node": "fiber (Cleveland → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Cleveland)-", + "from_node": "fiber (Columbus → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Cleveland", - "to_node": "fiber (Cleveland \u2192 Toledo)-" + "to_node": "fiber (Cleveland → Toledo)-" }, { - "from_node": "fiber (Toledo \u2192 Cleveland)-", + "from_node": "fiber (Toledo → Cleveland)-", "to_node": "roadm Cleveland" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Cincinnati)-" + "to_node": "fiber (Columbus → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Columbus)-", + "from_node": "fiber (Cincinnati → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Cleveland)-" + "to_node": "fiber (Columbus → Cleveland)-" }, { - "from_node": "fiber (Cleveland \u2192 Columbus)-", + "from_node": "fiber (Cleveland → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Columbus", - "to_node": "fiber (Columbus \u2192 Pittsburgh)-" + "to_node": "fiber (Columbus → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Columbus)-", + "from_node": "fiber (Pittsburgh → Columbus)-", "to_node": "roadm Columbus" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Abilene)-" + "to_node": "fiber (Dallas → Abilene)-" }, { - "from_node": "fiber (Abilene \u2192 Dallas)-", + "from_node": "fiber (Abilene → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Albuquerque)-" + "to_node": "fiber (Dallas → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Dallas)-", + "from_node": "fiber (Albuquerque → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Houston)-" + "to_node": "fiber (Dallas → Houston)-" }, { - "from_node": "fiber (Houston \u2192 Dallas)-", + "from_node": "fiber (Houston → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Little_Rock)-" + "to_node": "fiber (Dallas → Little_Rock)-" }, { - "from_node": "fiber (Little_Rock \u2192 Dallas)-", + "from_node": "fiber (Little_Rock → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Dallas", - "to_node": "fiber (Dallas \u2192 Oklahoma_City)-" + "to_node": "fiber (Dallas → Oklahoma_City)-" }, { - "from_node": "fiber (Oklahoma_City \u2192 Dallas)-", + "from_node": "fiber (Oklahoma_City → Dallas)-", "to_node": "roadm Dallas" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Albuquerque)-" + "to_node": "fiber (Denver → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Denver)-", + "from_node": "fiber (Albuquerque → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Billings)-" + "to_node": "fiber (Denver → Billings)-" }, { - "from_node": "fiber (Billings \u2192 Denver)-", + "from_node": "fiber (Billings → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Omaha)-" + "to_node": "fiber (Denver → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Denver)-", + "from_node": "fiber (Omaha → Denver)-", "to_node": "roadm Denver" }, { "from_node": "roadm Denver", - "to_node": "fiber (Denver \u2192 Salt_Lake_City)-" + "to_node": "fiber (Denver → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Denver)-", + "from_node": "fiber (Salt_Lake_City → Denver)-", "to_node": "roadm Denver" }, { - "from_node": "fiber (Chicago \u2192 Detroit)-", - "to_node": "fiber (Detroit \u2192 Toledo)-" + "from_node": "roadm Detroit", + "to_node": "fiber (Detroit → Chicago)-" + }, + { + "from_node": "fiber (Chicago → Detroit)-", + "to_node": "roadm Detroit" + }, + { + "from_node": "roadm Detroit", + "to_node": "fiber (Detroit → Toledo)-" }, { - "from_node": "fiber (Toledo \u2192 Detroit)-", - "to_node": "fiber (Detroit \u2192 Chicago)-" + "from_node": "fiber (Toledo → Detroit)-", + "to_node": "roadm Detroit" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Abilene)-" + "to_node": "fiber (El_Paso → Abilene)-" }, { - "from_node": "fiber (Abilene \u2192 El_Paso)-", + "from_node": "fiber (Abilene → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Albuquerque)-" + "to_node": "fiber (El_Paso → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 El_Paso)-", + "from_node": "fiber (Albuquerque → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 San_Antonio)-" + "to_node": "fiber (El_Paso → San_Antonio)-" }, { - "from_node": "fiber (San_Antonio \u2192 El_Paso)-", + "from_node": "fiber (San_Antonio → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm El_Paso", - "to_node": "fiber (El_Paso \u2192 Tucson)-" + "to_node": "fiber (El_Paso → Tucson)-" }, { - "from_node": "fiber (Tucson \u2192 El_Paso)-", + "from_node": "fiber (Tucson → El_Paso)-", "to_node": "roadm El_Paso" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Las_Vegas)-" + "to_node": "fiber (Fresno → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Fresno)-", + "from_node": "fiber (Las_Vegas → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Los_Angeles)-" + "to_node": "fiber (Fresno → Los_Angeles)-" }, { - "from_node": "fiber (Los_Angeles \u2192 Fresno)-", + "from_node": "fiber (Los_Angeles → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Fresno", - "to_node": "fiber (Fresno \u2192 Oakland)-" + "to_node": "fiber (Fresno → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Fresno)-", + "from_node": "fiber (Oakland → Fresno)-", "to_node": "roadm Fresno" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Charlotte)-" + "to_node": "fiber (Greensboro → Charlotte)-" }, { - "from_node": "fiber (Charlotte \u2192 Greensboro)-", + "from_node": "fiber (Charlotte → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Louisville)-" + "to_node": "fiber (Greensboro → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Greensboro)-", + "from_node": "fiber (Louisville → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Raleigh)-" + "to_node": "fiber (Greensboro → Raleigh)-" }, { - "from_node": "fiber (Raleigh \u2192 Greensboro)-", + "from_node": "fiber (Raleigh → Greensboro)-", "to_node": "roadm Greensboro" }, { "from_node": "roadm Greensboro", - "to_node": "fiber (Greensboro \u2192 Richmond)-" + "to_node": "fiber (Greensboro → Richmond)-" }, { - "from_node": "fiber (Richmond \u2192 Greensboro)-", + "from_node": "fiber (Richmond → Greensboro)-", "to_node": "roadm Greensboro" }, { - "from_node": "fiber (Long_Island \u2192 Hartford)-", - "to_node": "fiber (Hartford \u2192 Providence)-" + "from_node": "roadm Hartford", + "to_node": "fiber (Hartford → Long_Island)-" }, { - "from_node": "fiber (Providence \u2192 Hartford)-", - "to_node": "fiber (Hartford \u2192 Long_Island)-" + "from_node": "fiber (Long_Island → Hartford)-", + "to_node": "roadm Hartford" + }, + { + "from_node": "roadm Hartford", + "to_node": "fiber (Hartford → Providence)-" + }, + { + "from_node": "fiber (Providence → Hartford)-", + "to_node": "roadm Hartford" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Austin)-" + "to_node": "fiber (Houston → Austin)-" }, { - "from_node": "fiber (Austin \u2192 Houston)-", + "from_node": "fiber (Austin → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Baton_Rouge)-" + "to_node": "fiber (Houston → Baton_Rouge)-" }, { - "from_node": "fiber (Baton_Rouge \u2192 Houston)-", + "from_node": "fiber (Baton_Rouge → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Houston", - "to_node": "fiber (Houston \u2192 Dallas)-" + "to_node": "fiber (Houston → Dallas)-" }, { - "from_node": "fiber (Dallas \u2192 Houston)-", + "from_node": "fiber (Dallas → Houston)-", "to_node": "roadm Houston" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Atlanta)-" + "to_node": "fiber (Jacksonville → Atlanta)-" }, { - "from_node": "fiber (Atlanta \u2192 Jacksonville)-", + "from_node": "fiber (Atlanta → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Charleston)-" + "to_node": "fiber (Jacksonville → Charleston)-" }, { - "from_node": "fiber (Charleston \u2192 Jacksonville)-", + "from_node": "fiber (Charleston → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Jacksonville", - "to_node": "fiber (Jacksonville \u2192 Orlando)-" + "to_node": "fiber (Jacksonville → Orlando)-" }, { - "from_node": "fiber (Orlando \u2192 Jacksonville)-", + "from_node": "fiber (Orlando → Jacksonville)-", "to_node": "roadm Jacksonville" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 Omaha)-" + "to_node": "fiber (Kansas_City → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Kansas_City)-", + "from_node": "fiber (Omaha → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 St_Louis)-" + "to_node": "fiber (Kansas_City → St_Louis)-" }, { - "from_node": "fiber (St_Louis \u2192 Kansas_City)-", + "from_node": "fiber (St_Louis → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Kansas_City", - "to_node": "fiber (Kansas_City \u2192 Tulsa)-" + "to_node": "fiber (Kansas_City → Tulsa)-" }, { - "from_node": "fiber (Tulsa \u2192 Kansas_City)-", + "from_node": "fiber (Tulsa → Kansas_City)-", "to_node": "roadm Kansas_City" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Albuquerque)-" + "to_node": "fiber (Las_Vegas → Albuquerque)-" }, { - "from_node": "fiber (Albuquerque \u2192 Las_Vegas)-", + "from_node": "fiber (Albuquerque → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Fresno)-" + "to_node": "fiber (Las_Vegas → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Las_Vegas)-", + "from_node": "fiber (Fresno → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Phoenix)-" + "to_node": "fiber (Las_Vegas → Phoenix)-" }, { - "from_node": "fiber (Phoenix \u2192 Las_Vegas)-", + "from_node": "fiber (Phoenix → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { "from_node": "roadm Las_Vegas", - "to_node": "fiber (Las_Vegas \u2192 Salt_Lake_City)-" + "to_node": "fiber (Las_Vegas → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Las_Vegas)-", + "from_node": "fiber (Salt_Lake_City → Las_Vegas)-", "to_node": "roadm Las_Vegas" }, { - "from_node": "fiber (Dallas \u2192 Little_Rock)-", - "to_node": "fiber (Little_Rock \u2192 Memphis)-" + "from_node": "roadm Little_Rock", + "to_node": "fiber (Little_Rock → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Little_Rock)-", + "to_node": "roadm Little_Rock" + }, + { + "from_node": "roadm Little_Rock", + "to_node": "fiber (Little_Rock → Memphis)-" }, { - "from_node": "fiber (Memphis \u2192 Little_Rock)-", - "to_node": "fiber (Little_Rock \u2192 Dallas)-" + "from_node": "fiber (Memphis → Little_Rock)-", + "to_node": "roadm Little_Rock" }, { - "from_node": "fiber (Hartford \u2192 Long_Island)-", - "to_node": "fiber (Long_Island \u2192 New_York)-" + "from_node": "roadm Long_Island", + "to_node": "fiber (Long_Island → Hartford)-" }, { - "from_node": "fiber (New_York \u2192 Long_Island)-", - "to_node": "fiber (Long_Island \u2192 Hartford)-" + "from_node": "fiber (Hartford → Long_Island)-", + "to_node": "roadm Long_Island" + }, + { + "from_node": "roadm Long_Island", + "to_node": "fiber (Long_Island → New_York)-" + }, + { + "from_node": "fiber (New_York → Long_Island)-", + "to_node": "roadm Long_Island" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Fresno)-" + "to_node": "fiber (Los_Angeles → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Los_Angeles)-", + "from_node": "fiber (Fresno → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Honolulu)-" + "to_node": "fiber (Los_Angeles → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Los_Angeles)-", + "from_node": "fiber (Honolulu → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 San_Diego)-" + "to_node": "fiber (Los_Angeles → San_Diego)-" }, { - "from_node": "fiber (San_Diego \u2192 Los_Angeles)-", + "from_node": "fiber (San_Diego → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Los_Angeles", - "to_node": "fiber (Los_Angeles \u2192 Santa_Barbara)-" + "to_node": "fiber (Los_Angeles → Santa_Barbara)-" }, { - "from_node": "fiber (Santa_Barbara \u2192 Los_Angeles)-", + "from_node": "fiber (Santa_Barbara → Los_Angeles)-", "to_node": "roadm Los_Angeles" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Cincinnati)-" + "to_node": "fiber (Louisville → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Louisville)-", + "from_node": "fiber (Cincinnati → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Greensboro)-" + "to_node": "fiber (Louisville → Greensboro)-" }, { - "from_node": "fiber (Greensboro \u2192 Louisville)-", + "from_node": "fiber (Greensboro → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 Nashville)-" + "to_node": "fiber (Louisville → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Louisville)-", + "from_node": "fiber (Nashville → Louisville)-", "to_node": "roadm Louisville" }, { "from_node": "roadm Louisville", - "to_node": "fiber (Louisville \u2192 St_Louis)-" + "to_node": "fiber (Louisville → St_Louis)-" }, { - "from_node": "fiber (St_Louis \u2192 Louisville)-", + "from_node": "fiber (St_Louis → Louisville)-", "to_node": "roadm Louisville" }, { - "from_node": "fiber (Little_Rock \u2192 Memphis)-", - "to_node": "fiber (Memphis \u2192 Nashville)-" + "from_node": "roadm Memphis", + "to_node": "fiber (Memphis → Little_Rock)-" + }, + { + "from_node": "fiber (Little_Rock → Memphis)-", + "to_node": "roadm Memphis" + }, + { + "from_node": "roadm Memphis", + "to_node": "fiber (Memphis → Nashville)-" }, { - "from_node": "fiber (Nashville \u2192 Memphis)-", - "to_node": "fiber (Memphis \u2192 Little_Rock)-" + "from_node": "fiber (Nashville → Memphis)-", + "to_node": "roadm Memphis" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 Paris)-" + "to_node": "fiber (Miami → Paris)-" }, { - "from_node": "fiber (Paris \u2192 Miami)-", + "from_node": "fiber (Paris → Miami)-", "to_node": "roadm Miami" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 Tampa)-" + "to_node": "fiber (Miami → Tampa)-" }, { - "from_node": "fiber (Tampa \u2192 Miami)-", + "from_node": "fiber (Tampa → Miami)-", "to_node": "roadm Miami" }, { "from_node": "roadm Miami", - "to_node": "fiber (Miami \u2192 West_Palm_Beach)-" + "to_node": "fiber (Miami → West_Palm_Beach)-" }, { - "from_node": "fiber (West_Palm_Beach \u2192 Miami)-", + "from_node": "fiber (West_Palm_Beach → Miami)-", "to_node": "roadm Miami" }, { - "from_node": "fiber (Chicago \u2192 Milwaukee)-", - "to_node": "fiber (Milwaukee \u2192 Minneapolis)-" + "from_node": "roadm Milwaukee", + "to_node": "fiber (Milwaukee → Chicago)-" }, { - "from_node": "fiber (Minneapolis \u2192 Milwaukee)-", - "to_node": "fiber (Milwaukee \u2192 Chicago)-" + "from_node": "fiber (Chicago → Milwaukee)-", + "to_node": "roadm Milwaukee" + }, + { + "from_node": "roadm Milwaukee", + "to_node": "fiber (Milwaukee → Minneapolis)-" + }, + { + "from_node": "fiber (Minneapolis → Milwaukee)-", + "to_node": "roadm Milwaukee" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Bismarck)-" + "to_node": "fiber (Minneapolis → Bismarck)-" }, { - "from_node": "fiber (Bismarck \u2192 Minneapolis)-", + "from_node": "fiber (Bismarck → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Milwaukee)-" + "to_node": "fiber (Minneapolis → Milwaukee)-" }, { - "from_node": "fiber (Milwaukee \u2192 Minneapolis)-", + "from_node": "fiber (Milwaukee → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Minneapolis", - "to_node": "fiber (Minneapolis \u2192 Omaha)-" + "to_node": "fiber (Minneapolis → Omaha)-" }, { - "from_node": "fiber (Omaha \u2192 Minneapolis)-", + "from_node": "fiber (Omaha → Minneapolis)-", "to_node": "roadm Minneapolis" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Birmingham)-" + "to_node": "fiber (Nashville → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 Nashville)-", + "from_node": "fiber (Birmingham → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Louisville)-" + "to_node": "fiber (Nashville → Louisville)-" }, { - "from_node": "fiber (Louisville \u2192 Nashville)-", + "from_node": "fiber (Louisville → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm Nashville", - "to_node": "fiber (Nashville \u2192 Memphis)-" + "to_node": "fiber (Nashville → Memphis)-" }, { - "from_node": "fiber (Memphis \u2192 Nashville)-", + "from_node": "fiber (Memphis → Nashville)-", "to_node": "roadm Nashville" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Baton_Rouge)-" + "to_node": "fiber (New_Orleans → Baton_Rouge)-" }, { - "from_node": "fiber (Baton_Rouge \u2192 New_Orleans)-", + "from_node": "fiber (Baton_Rouge → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Birmingham)-" + "to_node": "fiber (New_Orleans → Birmingham)-" }, { - "from_node": "fiber (Birmingham \u2192 New_Orleans)-", + "from_node": "fiber (Birmingham → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_Orleans", - "to_node": "fiber (New_Orleans \u2192 Tallahassee)-" + "to_node": "fiber (New_Orleans → Tallahassee)-" }, { - "from_node": "fiber (Tallahassee \u2192 New_Orleans)-", + "from_node": "fiber (Tallahassee → New_Orleans)-", "to_node": "roadm New_Orleans" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Amsterdam)-" + "to_node": "fiber (New_York → Amsterdam)-" }, { - "from_node": "fiber (Amsterdam \u2192 New_York)-", + "from_node": "fiber (Amsterdam → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Long_Island)-" + "to_node": "fiber (New_York → Long_Island)-" }, { - "from_node": "fiber (Long_Island \u2192 New_York)-", + "from_node": "fiber (Long_Island → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Newark)-" + "to_node": "fiber (New_York → Newark)-" }, { - "from_node": "fiber (Newark \u2192 New_York)-", + "from_node": "fiber (Newark → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Scranton)-" + "to_node": "fiber (New_York → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 New_York)-", + "from_node": "fiber (Scranton → New_York)-", "to_node": "roadm New_York" }, { "from_node": "roadm New_York", - "to_node": "fiber (New_York \u2192 Wilmington)-" + "to_node": "fiber (New_York → Wilmington)-" }, { - "from_node": "fiber (Wilmington \u2192 New_York)-", + "from_node": "fiber (Wilmington → New_York)-", "to_node": "roadm New_York" }, { - "from_node": "fiber (New_York \u2192 Newark)-", - "to_node": "fiber (Newark \u2192 Philadelphia)-" + "from_node": "roadm Newark", + "to_node": "fiber (Newark → New_York)-" + }, + { + "from_node": "fiber (New_York → Newark)-", + "to_node": "roadm Newark" + }, + { + "from_node": "roadm Newark", + "to_node": "fiber (Newark → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Newark)-", - "to_node": "fiber (Newark \u2192 New_York)-" + "from_node": "fiber (Philadelphia → Newark)-", + "to_node": "roadm Newark" }, { - "from_node": "fiber (Raleigh \u2192 Norfolk)-", - "to_node": "fiber (Norfolk \u2192 Wilmington)-" + "from_node": "roadm Norfolk", + "to_node": "fiber (Norfolk → Raleigh)-" }, { - "from_node": "fiber (Wilmington \u2192 Norfolk)-", - "to_node": "fiber (Norfolk \u2192 Raleigh)-" + "from_node": "fiber (Raleigh → Norfolk)-", + "to_node": "roadm Norfolk" + }, + { + "from_node": "roadm Norfolk", + "to_node": "fiber (Norfolk → Wilmington)-" + }, + { + "from_node": "fiber (Wilmington → Norfolk)-", + "to_node": "roadm Norfolk" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Fresno)-" + "to_node": "fiber (Oakland → Fresno)-" }, { - "from_node": "fiber (Fresno \u2192 Oakland)-", + "from_node": "fiber (Fresno → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Sacramento)-" + "to_node": "fiber (Oakland → Sacramento)-" }, { - "from_node": "fiber (Sacramento \u2192 Oakland)-", + "from_node": "fiber (Sacramento → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Salt_Lake_City)-" + "to_node": "fiber (Oakland → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Oakland)-", + "from_node": "fiber (Salt_Lake_City → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 San_Francisco)-" + "to_node": "fiber (Oakland → San_Francisco)-" }, { - "from_node": "fiber (San_Francisco \u2192 Oakland)-", + "from_node": "fiber (San_Francisco → Oakland)-", "to_node": "roadm Oakland" }, { "from_node": "roadm Oakland", - "to_node": "fiber (Oakland \u2192 Taipei)-" + "to_node": "fiber (Oakland → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Oakland)-", + "from_node": "fiber (Taipei → Oakland)-", "to_node": "roadm Oakland" }, { - "from_node": "fiber (Dallas \u2192 Oklahoma_City)-", - "to_node": "fiber (Oklahoma_City \u2192 Tulsa)-" + "from_node": "roadm Oklahoma_City", + "to_node": "fiber (Oklahoma_City → Dallas)-" + }, + { + "from_node": "fiber (Dallas → Oklahoma_City)-", + "to_node": "roadm Oklahoma_City" + }, + { + "from_node": "roadm Oklahoma_City", + "to_node": "fiber (Oklahoma_City → Tulsa)-" }, { - "from_node": "fiber (Tulsa \u2192 Oklahoma_City)-", - "to_node": "fiber (Oklahoma_City \u2192 Dallas)-" + "from_node": "fiber (Tulsa → Oklahoma_City)-", + "to_node": "roadm Oklahoma_City" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Denver)-" + "to_node": "fiber (Omaha → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Omaha)-", + "from_node": "fiber (Denver → Omaha)-", "to_node": "roadm Omaha" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Kansas_City)-" + "to_node": "fiber (Omaha → Kansas_City)-" }, { - "from_node": "fiber (Kansas_City \u2192 Omaha)-", + "from_node": "fiber (Kansas_City → Omaha)-", "to_node": "roadm Omaha" }, { "from_node": "roadm Omaha", - "to_node": "fiber (Omaha \u2192 Minneapolis)-" + "to_node": "fiber (Omaha → Minneapolis)-" }, { - "from_node": "fiber (Minneapolis \u2192 Omaha)-", + "from_node": "fiber (Minneapolis → Omaha)-", "to_node": "roadm Omaha" }, { - "from_node": "fiber (Jacksonville \u2192 Orlando)-", - "to_node": "fiber (Orlando \u2192 West_Palm_Beach)-" + "from_node": "roadm Orlando", + "to_node": "fiber (Orlando → Jacksonville)-" }, { - "from_node": "fiber (West_Palm_Beach \u2192 Orlando)-", - "to_node": "fiber (Orlando \u2192 Jacksonville)-" + "from_node": "fiber (Jacksonville → Orlando)-", + "to_node": "roadm Orlando" + }, + { + "from_node": "roadm Orlando", + "to_node": "fiber (Orlando → West_Palm_Beach)-" + }, + { + "from_node": "fiber (West_Palm_Beach → Orlando)-", + "to_node": "roadm Orlando" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Baltimore)-" + "to_node": "fiber (Philadelphia → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Philadelphia)-", + "from_node": "fiber (Baltimore → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Newark)-" + "to_node": "fiber (Philadelphia → Newark)-" }, { - "from_node": "fiber (Newark \u2192 Philadelphia)-", + "from_node": "fiber (Newark → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Philadelphia", - "to_node": "fiber (Philadelphia \u2192 Scranton)-" + "to_node": "fiber (Philadelphia → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 Philadelphia)-", + "from_node": "fiber (Scranton → Philadelphia)-", "to_node": "roadm Philadelphia" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 Las_Vegas)-" + "to_node": "fiber (Phoenix → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Phoenix)-", + "from_node": "fiber (Las_Vegas → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 San_Diego)-" + "to_node": "fiber (Phoenix → San_Diego)-" }, { - "from_node": "fiber (San_Diego \u2192 Phoenix)-", + "from_node": "fiber (San_Diego → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Phoenix", - "to_node": "fiber (Phoenix \u2192 Tucson)-" + "to_node": "fiber (Phoenix → Tucson)-" }, { - "from_node": "fiber (Tucson \u2192 Phoenix)-", + "from_node": "fiber (Tucson → Phoenix)-", "to_node": "roadm Phoenix" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Baltimore)-" + "to_node": "fiber (Pittsburgh → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Pittsburgh)-", + "from_node": "fiber (Baltimore → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Columbus)-" + "to_node": "fiber (Pittsburgh → Columbus)-" }, { - "from_node": "fiber (Columbus \u2192 Pittsburgh)-", + "from_node": "fiber (Columbus → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Pittsburgh", - "to_node": "fiber (Pittsburgh \u2192 Scranton)-" + "to_node": "fiber (Pittsburgh → Scranton)-" }, { - "from_node": "fiber (Scranton \u2192 Pittsburgh)-", + "from_node": "fiber (Scranton → Pittsburgh)-", "to_node": "roadm Pittsburgh" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Sacramento)-" + "to_node": "fiber (Portland → Sacramento)-" }, { - "from_node": "fiber (Sacramento \u2192 Portland)-", + "from_node": "fiber (Sacramento → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Salt_Lake_City)-" + "to_node": "fiber (Portland → Salt_Lake_City)-" }, { - "from_node": "fiber (Salt_Lake_City \u2192 Portland)-", + "from_node": "fiber (Salt_Lake_City → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Seattle)-" + "to_node": "fiber (Portland → Seattle)-" }, { - "from_node": "fiber (Seattle \u2192 Portland)-", + "from_node": "fiber (Seattle → Portland)-", "to_node": "roadm Portland" }, { "from_node": "roadm Portland", - "to_node": "fiber (Portland \u2192 Tokyo)-" + "to_node": "fiber (Portland → Tokyo)-" }, { - "from_node": "fiber (Tokyo \u2192 Portland)-", + "from_node": "fiber (Tokyo → Portland)-", "to_node": "roadm Portland" }, { - "from_node": "fiber (Boston \u2192 Providence)-", - "to_node": "fiber (Providence \u2192 Hartford)-" + "from_node": "roadm Providence", + "to_node": "fiber (Providence → Boston)-" + }, + { + "from_node": "fiber (Boston → Providence)-", + "to_node": "roadm Providence" + }, + { + "from_node": "roadm Providence", + "to_node": "fiber (Providence → Hartford)-" }, { - "from_node": "fiber (Hartford \u2192 Providence)-", - "to_node": "fiber (Providence \u2192 Boston)-" + "from_node": "fiber (Hartford → Providence)-", + "to_node": "roadm Providence" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Charleston)-" + "to_node": "fiber (Raleigh → Charleston)-" }, { - "from_node": "fiber (Charleston \u2192 Raleigh)-", + "from_node": "fiber (Charleston → Raleigh)-", "to_node": "roadm Raleigh" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Greensboro)-" + "to_node": "fiber (Raleigh → Greensboro)-" }, { - "from_node": "fiber (Greensboro \u2192 Raleigh)-", + "from_node": "fiber (Greensboro → Raleigh)-", "to_node": "roadm Raleigh" }, { "from_node": "roadm Raleigh", - "to_node": "fiber (Raleigh \u2192 Norfolk)-" + "to_node": "fiber (Raleigh → Norfolk)-" }, { - "from_node": "fiber (Norfolk \u2192 Raleigh)-", + "from_node": "fiber (Norfolk → Raleigh)-", "to_node": "roadm Raleigh" }, { - "from_node": "fiber (Greensboro \u2192 Richmond)-", - "to_node": "fiber (Richmond \u2192 Washington_DC)-" + "from_node": "roadm Richmond", + "to_node": "fiber (Richmond → Greensboro)-" + }, + { + "from_node": "fiber (Greensboro → Richmond)-", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Richmond", + "to_node": "fiber (Richmond → Washington_DC)-" + }, + { + "from_node": "fiber (Washington_DC → Richmond)-", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Rochester", + "to_node": "fiber (Rochester → Buffalo)-" }, { - "from_node": "fiber (Washington_DC \u2192 Richmond)-", - "to_node": "fiber (Richmond \u2192 Greensboro)-" + "from_node": "fiber (Buffalo → Rochester)-", + "to_node": "roadm Rochester" }, { - "from_node": "fiber (Buffalo \u2192 Rochester)-", - "to_node": "fiber (Rochester \u2192 Syracuse)-" + "from_node": "roadm Rochester", + "to_node": "fiber (Rochester → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Rochester)-", - "to_node": "fiber (Rochester \u2192 Buffalo)-" + "from_node": "fiber (Syracuse → Rochester)-", + "to_node": "roadm Rochester" }, { - "from_node": "fiber (Oakland \u2192 Sacramento)-", - "to_node": "fiber (Sacramento \u2192 Portland)-" + "from_node": "roadm Sacramento", + "to_node": "fiber (Sacramento → Oakland)-" }, { - "from_node": "fiber (Portland \u2192 Sacramento)-", - "to_node": "fiber (Sacramento \u2192 Oakland)-" + "from_node": "fiber (Oakland → Sacramento)-", + "to_node": "roadm Sacramento" + }, + { + "from_node": "roadm Sacramento", + "to_node": "fiber (Sacramento → Portland)-" + }, + { + "from_node": "fiber (Portland → Sacramento)-", + "to_node": "roadm Sacramento" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Denver)-" + "to_node": "fiber (Salt_Lake_City → Denver)-" }, { - "from_node": "fiber (Denver \u2192 Salt_Lake_City)-", + "from_node": "fiber (Denver → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Las_Vegas)-" + "to_node": "fiber (Salt_Lake_City → Las_Vegas)-" }, { - "from_node": "fiber (Las_Vegas \u2192 Salt_Lake_City)-", + "from_node": "fiber (Las_Vegas → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Oakland)-" + "to_node": "fiber (Salt_Lake_City → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Salt_Lake_City)-", + "from_node": "fiber (Oakland → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { "from_node": "roadm Salt_Lake_City", - "to_node": "fiber (Salt_Lake_City \u2192 Portland)-" + "to_node": "fiber (Salt_Lake_City → Portland)-" }, { - "from_node": "fiber (Portland \u2192 Salt_Lake_City)-", + "from_node": "fiber (Portland → Salt_Lake_City)-", "to_node": "roadm Salt_Lake_City" }, { - "from_node": "fiber (Austin \u2192 San_Antonio)-", - "to_node": "fiber (San_Antonio \u2192 El_Paso)-" + "from_node": "roadm San_Antonio", + "to_node": "fiber (San_Antonio → Austin)-" + }, + { + "from_node": "fiber (Austin → San_Antonio)-", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Antonio", + "to_node": "fiber (San_Antonio → El_Paso)-" + }, + { + "from_node": "fiber (El_Paso → San_Antonio)-", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Diego", + "to_node": "fiber (San_Diego → Los_Angeles)-" + }, + { + "from_node": "fiber (Los_Angeles → San_Diego)-", + "to_node": "roadm San_Diego" + }, + { + "from_node": "roadm San_Diego", + "to_node": "fiber (San_Diego → Phoenix)-" }, { - "from_node": "fiber (El_Paso \u2192 San_Antonio)-", - "to_node": "fiber (San_Antonio \u2192 Austin)-" + "from_node": "fiber (Phoenix → San_Diego)-", + "to_node": "roadm San_Diego" }, { - "from_node": "fiber (Los_Angeles \u2192 San_Diego)-", - "to_node": "fiber (San_Diego \u2192 Phoenix)-" + "from_node": "roadm San_Francisco", + "to_node": "fiber (San_Francisco → Oakland)-" }, { - "from_node": "fiber (Phoenix \u2192 San_Diego)-", - "to_node": "fiber (San_Diego \u2192 Los_Angeles)-" + "from_node": "fiber (Oakland → San_Francisco)-", + "to_node": "roadm San_Francisco" }, { - "from_node": "fiber (Oakland \u2192 San_Francisco)-", - "to_node": "fiber (San_Francisco \u2192 San_Jose)-" + "from_node": "roadm San_Francisco", + "to_node": "fiber (San_Francisco → San_Jose)-" }, { - "from_node": "fiber (San_Jose \u2192 San_Francisco)-", - "to_node": "fiber (San_Francisco \u2192 Oakland)-" + "from_node": "fiber (San_Jose → San_Francisco)-", + "to_node": "roadm San_Francisco" }, { - "from_node": "fiber (San_Francisco \u2192 San_Jose)-", - "to_node": "fiber (San_Jose \u2192 Santa_Barbara)-" + "from_node": "roadm San_Jose", + "to_node": "fiber (San_Jose → San_Francisco)-" }, { - "from_node": "fiber (Santa_Barbara \u2192 San_Jose)-", - "to_node": "fiber (San_Jose \u2192 San_Francisco)-" + "from_node": "fiber (San_Francisco → San_Jose)-", + "to_node": "roadm San_Jose" }, { - "from_node": "fiber (Los_Angeles \u2192 Santa_Barbara)-", - "to_node": "fiber (Santa_Barbara \u2192 San_Jose)-" + "from_node": "roadm San_Jose", + "to_node": "fiber (San_Jose → Santa_Barbara)-" }, { - "from_node": "fiber (San_Jose \u2192 Santa_Barbara)-", - "to_node": "fiber (Santa_Barbara \u2192 Los_Angeles)-" + "from_node": "fiber (Santa_Barbara → San_Jose)-", + "to_node": "roadm San_Jose" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "fiber (Santa_Barbara → Los_Angeles)-" + }, + { + "from_node": "fiber (Los_Angeles → Santa_Barbara)-", + "to_node": "roadm Santa_Barbara" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "fiber (Santa_Barbara → San_Jose)-" + }, + { + "from_node": "fiber (San_Jose → Santa_Barbara)-", + "to_node": "roadm Santa_Barbara" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 New_York)-" + "to_node": "fiber (Scranton → New_York)-" }, { - "from_node": "fiber (New_York \u2192 Scranton)-", + "from_node": "fiber (New_York → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Philadelphia)-" + "to_node": "fiber (Scranton → Philadelphia)-" }, { - "from_node": "fiber (Philadelphia \u2192 Scranton)-", + "from_node": "fiber (Philadelphia → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Pittsburgh)-" + "to_node": "fiber (Scranton → Pittsburgh)-" }, { - "from_node": "fiber (Pittsburgh \u2192 Scranton)-", + "from_node": "fiber (Pittsburgh → Scranton)-", "to_node": "roadm Scranton" }, { "from_node": "roadm Scranton", - "to_node": "fiber (Scranton \u2192 Syracuse)-" + "to_node": "fiber (Scranton → Syracuse)-" }, { - "from_node": "fiber (Syracuse \u2192 Scranton)-", + "from_node": "fiber (Syracuse → Scranton)-", "to_node": "roadm Scranton" }, { - "from_node": "fiber (Portland \u2192 Seattle)-", - "to_node": "fiber (Seattle \u2192 Spokane)-" + "from_node": "roadm Seattle", + "to_node": "fiber (Seattle → Portland)-" + }, + { + "from_node": "fiber (Portland → Seattle)-", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Seattle", + "to_node": "fiber (Seattle → Spokane)-" + }, + { + "from_node": "fiber (Spokane → Seattle)-", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Spokane", + "to_node": "fiber (Spokane → Billings)-" }, { - "from_node": "fiber (Spokane \u2192 Seattle)-", - "to_node": "fiber (Seattle \u2192 Portland)-" + "from_node": "fiber (Billings → Spokane)-", + "to_node": "roadm Spokane" }, { - "from_node": "fiber (Billings \u2192 Spokane)-", - "to_node": "fiber (Spokane \u2192 Seattle)-" + "from_node": "roadm Spokane", + "to_node": "fiber (Spokane → Seattle)-" }, { - "from_node": "fiber (Seattle \u2192 Spokane)-", - "to_node": "fiber (Spokane \u2192 Billings)-" + "from_node": "fiber (Seattle → Spokane)-", + "to_node": "roadm Spokane" }, { - "from_node": "fiber (Chicago \u2192 Springfield)-", - "to_node": "fiber (Springfield \u2192 St_Louis)-" + "from_node": "roadm Springfield", + "to_node": "fiber (Springfield → Chicago)-" }, { - "from_node": "fiber (St_Louis \u2192 Springfield)-", - "to_node": "fiber (Springfield \u2192 Chicago)-" + "from_node": "fiber (Chicago → Springfield)-", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm Springfield", + "to_node": "fiber (Springfield → St_Louis)-" + }, + { + "from_node": "fiber (St_Louis → Springfield)-", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm St_Louis", + "to_node": "fiber (St_Louis → Kansas_City)-" + }, + { + "from_node": "fiber (Kansas_City → St_Louis)-", + "to_node": "roadm St_Louis" }, { "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Kansas_City)-" + "to_node": "fiber (St_Louis → Louisville)-" }, { - "from_node": "fiber (Kansas_City \u2192 St_Louis)-", + "from_node": "fiber (Louisville → St_Louis)-", "to_node": "roadm St_Louis" }, { "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Louisville)-" + "to_node": "fiber (St_Louis → Springfield)-" }, { - "from_node": "fiber (Louisville \u2192 St_Louis)-", + "from_node": "fiber (Springfield → St_Louis)-", "to_node": "roadm St_Louis" }, { - "from_node": "roadm St_Louis", - "to_node": "fiber (St_Louis \u2192 Springfield)-" + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Albany)-" + }, + { + "from_node": "fiber (Albany → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Rochester)-" + }, + { + "from_node": "fiber (Rochester → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Syracuse", + "to_node": "fiber (Syracuse → Scranton)-" + }, + { + "from_node": "fiber (Scranton → Syracuse)-", + "to_node": "roadm Syracuse" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "fiber (Tallahassee → New_Orleans)-" + }, + { + "from_node": "fiber (New_Orleans → Tallahassee)-", + "to_node": "roadm Tallahassee" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "fiber (Tallahassee → Tampa)-" }, { - "from_node": "fiber (Springfield \u2192 St_Louis)-", - "to_node": "roadm St_Louis" + "from_node": "fiber (Tampa → Tallahassee)-", + "to_node": "roadm Tallahassee" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Albany)-" + "from_node": "roadm Tampa", + "to_node": "fiber (Tampa → Miami)-" }, { - "from_node": "fiber (Albany \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Miami → Tampa)-", + "to_node": "roadm Tampa" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Rochester)-" + "from_node": "roadm Tampa", + "to_node": "fiber (Tampa → Tallahassee)-" }, { - "from_node": "fiber (Rochester \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Tallahassee → Tampa)-", + "to_node": "roadm Tampa" }, { - "from_node": "roadm Syracuse", - "to_node": "fiber (Syracuse \u2192 Scranton)-" + "from_node": "roadm Toledo", + "to_node": "fiber (Toledo → Cleveland)-" }, { - "from_node": "fiber (Scranton \u2192 Syracuse)-", - "to_node": "roadm Syracuse" + "from_node": "fiber (Cleveland → Toledo)-", + "to_node": "roadm Toledo" }, { - "from_node": "fiber (New_Orleans \u2192 Tallahassee)-", - "to_node": "fiber (Tallahassee \u2192 Tampa)-" + "from_node": "roadm Toledo", + "to_node": "fiber (Toledo → Detroit)-" }, { - "from_node": "fiber (Tampa \u2192 Tallahassee)-", - "to_node": "fiber (Tallahassee \u2192 New_Orleans)-" + "from_node": "fiber (Detroit → Toledo)-", + "to_node": "roadm Toledo" }, { - "from_node": "fiber (Miami \u2192 Tampa)-", - "to_node": "fiber (Tampa \u2192 Tallahassee)-" + "from_node": "roadm Tucson", + "to_node": "fiber (Tucson → El_Paso)-" }, { - "from_node": "fiber (Tallahassee \u2192 Tampa)-", - "to_node": "fiber (Tampa \u2192 Miami)-" + "from_node": "fiber (El_Paso → Tucson)-", + "to_node": "roadm Tucson" }, { - "from_node": "fiber (Cleveland \u2192 Toledo)-", - "to_node": "fiber (Toledo \u2192 Detroit)-" + "from_node": "roadm Tucson", + "to_node": "fiber (Tucson → Phoenix)-" }, { - "from_node": "fiber (Detroit \u2192 Toledo)-", - "to_node": "fiber (Toledo \u2192 Cleveland)-" + "from_node": "fiber (Phoenix → Tucson)-", + "to_node": "roadm Tucson" }, { - "from_node": "fiber (El_Paso \u2192 Tucson)-", - "to_node": "fiber (Tucson \u2192 Phoenix)-" + "from_node": "roadm Tulsa", + "to_node": "fiber (Tulsa → Kansas_City)-" }, { - "from_node": "fiber (Phoenix \u2192 Tucson)-", - "to_node": "fiber (Tucson \u2192 El_Paso)-" + "from_node": "fiber (Kansas_City → Tulsa)-", + "to_node": "roadm Tulsa" }, { - "from_node": "fiber (Kansas_City \u2192 Tulsa)-", - "to_node": "fiber (Tulsa \u2192 Oklahoma_City)-" + "from_node": "roadm Tulsa", + "to_node": "fiber (Tulsa → Oklahoma_City)-" }, { - "from_node": "fiber (Oklahoma_City \u2192 Tulsa)-", - "to_node": "fiber (Tulsa \u2192 Kansas_City)-" + "from_node": "fiber (Oklahoma_City → Tulsa)-", + "to_node": "roadm Tulsa" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Baltimore)-" + "to_node": "fiber (Washington_DC → Baltimore)-" }, { - "from_node": "fiber (Baltimore \u2192 Washington_DC)-", + "from_node": "fiber (Baltimore → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Cincinnati)-" + "to_node": "fiber (Washington_DC → Cincinnati)-" }, { - "from_node": "fiber (Cincinnati \u2192 Washington_DC)-", + "from_node": "fiber (Cincinnati → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 London)-" + "to_node": "fiber (Washington_DC → London)-" }, { - "from_node": "fiber (London \u2192 Washington_DC)-", + "from_node": "fiber (London → Washington_DC)-", "to_node": "roadm Washington_DC" }, { "from_node": "roadm Washington_DC", - "to_node": "fiber (Washington_DC \u2192 Richmond)-" + "to_node": "fiber (Washington_DC → Richmond)-" }, { - "from_node": "fiber (Richmond \u2192 Washington_DC)-", + "from_node": "fiber (Richmond → Washington_DC)-", "to_node": "roadm Washington_DC" }, { - "from_node": "fiber (Miami \u2192 West_Palm_Beach)-", - "to_node": "fiber (West_Palm_Beach \u2192 Orlando)-" + "from_node": "roadm West_Palm_Beach", + "to_node": "fiber (West_Palm_Beach → Miami)-" + }, + { + "from_node": "fiber (Miami → West_Palm_Beach)-", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm West_Palm_Beach", + "to_node": "fiber (West_Palm_Beach → Orlando)-" + }, + { + "from_node": "fiber (Orlando → West_Palm_Beach)-", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm Wilmington", + "to_node": "fiber (Wilmington → New_York)-" }, { - "from_node": "fiber (Orlando \u2192 West_Palm_Beach)-", - "to_node": "fiber (West_Palm_Beach \u2192 Miami)-" + "from_node": "fiber (New_York → Wilmington)-", + "to_node": "roadm Wilmington" }, { - "from_node": "fiber (New_York \u2192 Wilmington)-", - "to_node": "fiber (Wilmington \u2192 Norfolk)-" + "from_node": "roadm Wilmington", + "to_node": "fiber (Wilmington → Norfolk)-" }, { - "from_node": "fiber (Norfolk \u2192 Wilmington)-", - "to_node": "fiber (Wilmington \u2192 New_York)-" + "from_node": "fiber (Norfolk → Wilmington)-", + "to_node": "roadm Wilmington" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Berlin)-" + "to_node": "fiber (Amsterdam → Berlin)-" }, { - "from_node": "fiber (Berlin \u2192 Amsterdam)-", + "from_node": "fiber (Berlin → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Brussels)-" + "to_node": "fiber (Amsterdam → Brussels)-" }, { - "from_node": "fiber (Brussels \u2192 Amsterdam)-", + "from_node": "fiber (Brussels → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 Frankfurt)-" + "to_node": "fiber (Amsterdam → Frankfurt)-" }, { - "from_node": "fiber (Frankfurt \u2192 Amsterdam)-", + "from_node": "fiber (Frankfurt → Amsterdam)-", "to_node": "roadm Amsterdam" }, { "from_node": "roadm Amsterdam", - "to_node": "fiber (Amsterdam \u2192 New_York)-" + "to_node": "fiber (Amsterdam → New_York)-" }, { - "from_node": "fiber (New_York \u2192 Amsterdam)-", + "from_node": "fiber (New_York → Amsterdam)-", "to_node": "roadm Amsterdam" }, { - "from_node": "fiber (Amsterdam \u2192 Berlin)-", - "to_node": "fiber (Berlin \u2192 Warsaw)-" + "from_node": "roadm Berlin", + "to_node": "fiber (Berlin → Amsterdam)-" + }, + { + "from_node": "fiber (Amsterdam → Berlin)-", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Berlin", + "to_node": "fiber (Berlin → Warsaw)-" + }, + { + "from_node": "fiber (Warsaw → Berlin)-", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Brussels", + "to_node": "fiber (Brussels → Amsterdam)-" + }, + { + "from_node": "fiber (Amsterdam → Brussels)-", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Brussels", + "to_node": "fiber (Brussels → London)-" + }, + { + "from_node": "fiber (London → Brussels)-", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Bucharest", + "to_node": "fiber (Bucharest → Istanbul)-" }, { - "from_node": "fiber (Warsaw \u2192 Berlin)-", - "to_node": "fiber (Berlin \u2192 Amsterdam)-" + "from_node": "fiber (Istanbul → Bucharest)-", + "to_node": "roadm Bucharest" }, { - "from_node": "fiber (Amsterdam \u2192 Brussels)-", - "to_node": "fiber (Brussels \u2192 London)-" + "from_node": "roadm Bucharest", + "to_node": "fiber (Bucharest → Warsaw)-" }, { - "from_node": "fiber (London \u2192 Brussels)-", - "to_node": "fiber (Brussels \u2192 Amsterdam)-" + "from_node": "fiber (Warsaw → Bucharest)-", + "to_node": "roadm Bucharest" }, { - "from_node": "fiber (Istanbul \u2192 Bucharest)-", - "to_node": "fiber (Bucharest \u2192 Warsaw)-" + "from_node": "roadm Frankfurt", + "to_node": "fiber (Frankfurt → Amsterdam)-" }, { - "from_node": "fiber (Warsaw \u2192 Bucharest)-", - "to_node": "fiber (Bucharest \u2192 Istanbul)-" + "from_node": "fiber (Amsterdam → Frankfurt)-", + "to_node": "roadm Frankfurt" }, { - "from_node": "fiber (Amsterdam \u2192 Frankfurt)-", - "to_node": "fiber (Frankfurt \u2192 Vienna)-" + "from_node": "roadm Frankfurt", + "to_node": "fiber (Frankfurt → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Frankfurt)-", - "to_node": "fiber (Frankfurt \u2192 Amsterdam)-" + "from_node": "fiber (Vienna → Frankfurt)-", + "to_node": "roadm Frankfurt" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Bucharest)-" + "to_node": "fiber (Istanbul → Bucharest)-" }, { - "from_node": "fiber (Bucharest \u2192 Istanbul)-", + "from_node": "fiber (Bucharest → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Delhi)-" + "to_node": "fiber (Istanbul → Delhi)-" }, { - "from_node": "fiber (Delhi \u2192 Istanbul)-", + "from_node": "fiber (Delhi → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm Istanbul", - "to_node": "fiber (Istanbul \u2192 Rome)-" + "to_node": "fiber (Istanbul → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Istanbul)-", + "from_node": "fiber (Rome → Istanbul)-", "to_node": "roadm Istanbul" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Brussels)-" + "to_node": "fiber (London → Brussels)-" }, { - "from_node": "fiber (Brussels \u2192 London)-", + "from_node": "fiber (Brussels → London)-", "to_node": "roadm London" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Paris)-" + "to_node": "fiber (London → Paris)-" }, { - "from_node": "fiber (Paris \u2192 London)-", + "from_node": "fiber (Paris → London)-", "to_node": "roadm London" }, { "from_node": "roadm London", - "to_node": "fiber (London \u2192 Washington_DC)-" + "to_node": "fiber (London → Washington_DC)-" }, { - "from_node": "fiber (Washington_DC \u2192 London)-", + "from_node": "fiber (Washington_DC → London)-", "to_node": "roadm London" }, { - "from_node": "fiber (Paris \u2192 Madrid)-", - "to_node": "fiber (Madrid \u2192 Zurich)-" + "from_node": "roadm Madrid", + "to_node": "fiber (Madrid → Paris)-" }, { - "from_node": "fiber (Zurich \u2192 Madrid)-", - "to_node": "fiber (Madrid \u2192 Paris)-" + "from_node": "fiber (Paris → Madrid)-", + "to_node": "roadm Madrid" + }, + { + "from_node": "roadm Madrid", + "to_node": "fiber (Madrid → Zurich)-" + }, + { + "from_node": "fiber (Zurich → Madrid)-", + "to_node": "roadm Madrid" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 London)-" + "to_node": "fiber (Paris → London)-" }, { - "from_node": "fiber (London \u2192 Paris)-", + "from_node": "fiber (London → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 Madrid)-" + "to_node": "fiber (Paris → Madrid)-" }, { - "from_node": "fiber (Madrid \u2192 Paris)-", + "from_node": "fiber (Madrid → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Paris", - "to_node": "fiber (Paris \u2192 Miami)-" + "to_node": "fiber (Paris → Miami)-" }, { - "from_node": "fiber (Miami \u2192 Paris)-", + "from_node": "fiber (Miami → Paris)-", "to_node": "roadm Paris" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Istanbul)-" + "to_node": "fiber (Rome → Istanbul)-" }, { - "from_node": "fiber (Istanbul \u2192 Rome)-", + "from_node": "fiber (Istanbul → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Mumbai)-" + "to_node": "fiber (Rome → Mumbai)-" }, { - "from_node": "fiber (Mumbai \u2192 Rome)-", + "from_node": "fiber (Mumbai → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Vienna)-" + "to_node": "fiber (Rome → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Rome)-", + "from_node": "fiber (Vienna → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Rome", - "to_node": "fiber (Rome \u2192 Zurich)-" + "to_node": "fiber (Rome → Zurich)-" }, { - "from_node": "fiber (Zurich \u2192 Rome)-", + "from_node": "fiber (Zurich → Rome)-", "to_node": "roadm Rome" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Frankfurt)-" + "to_node": "fiber (Vienna → Frankfurt)-" }, { - "from_node": "fiber (Frankfurt \u2192 Vienna)-", + "from_node": "fiber (Frankfurt → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Rome)-" + "to_node": "fiber (Vienna → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Vienna)-", + "from_node": "fiber (Rome → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Vienna", - "to_node": "fiber (Vienna \u2192 Warsaw)-" + "to_node": "fiber (Vienna → Warsaw)-" }, { - "from_node": "fiber (Warsaw \u2192 Vienna)-", + "from_node": "fiber (Warsaw → Vienna)-", "to_node": "roadm Vienna" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Berlin)-" + "to_node": "fiber (Warsaw → Berlin)-" }, { - "from_node": "fiber (Berlin \u2192 Warsaw)-", + "from_node": "fiber (Berlin → Warsaw)-", "to_node": "roadm Warsaw" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Bucharest)-" + "to_node": "fiber (Warsaw → Bucharest)-" }, { - "from_node": "fiber (Bucharest \u2192 Warsaw)-", + "from_node": "fiber (Bucharest → Warsaw)-", "to_node": "roadm Warsaw" }, { "from_node": "roadm Warsaw", - "to_node": "fiber (Warsaw \u2192 Vienna)-" + "to_node": "fiber (Warsaw → Vienna)-" }, { - "from_node": "fiber (Vienna \u2192 Warsaw)-", + "from_node": "fiber (Vienna → Warsaw)-", "to_node": "roadm Warsaw" }, { - "from_node": "fiber (Madrid \u2192 Zurich)-", - "to_node": "fiber (Zurich \u2192 Rome)-" + "from_node": "roadm Zurich", + "to_node": "fiber (Zurich → Madrid)-" + }, + { + "from_node": "fiber (Madrid → Zurich)-", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Zurich", + "to_node": "fiber (Zurich → Rome)-" + }, + { + "from_node": "fiber (Rome → Zurich)-", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Bangkok", + "to_node": "fiber (Bangkok → Delhi)-" + }, + { + "from_node": "fiber (Delhi → Bangkok)-", + "to_node": "roadm Bangkok" + }, + { + "from_node": "roadm Bangkok", + "to_node": "fiber (Bangkok → Hong_Kong)-" }, { - "from_node": "fiber (Rome \u2192 Zurich)-", - "to_node": "fiber (Zurich \u2192 Madrid)-" + "from_node": "fiber (Hong_Kong → Bangkok)-", + "to_node": "roadm Bangkok" }, { - "from_node": "fiber (Delhi \u2192 Bangkok)-", - "to_node": "fiber (Bangkok \u2192 Hong_Kong)-" + "from_node": "roadm Beijing", + "to_node": "fiber (Beijing → Seoul)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Bangkok)-", - "to_node": "fiber (Bangkok \u2192 Delhi)-" + "from_node": "fiber (Seoul → Beijing)-", + "to_node": "roadm Beijing" }, { - "from_node": "fiber (Seoul \u2192 Beijing)-", - "to_node": "fiber (Beijing \u2192 Shanghai)-" + "from_node": "roadm Beijing", + "to_node": "fiber (Beijing → Shanghai)-" }, { - "from_node": "fiber (Shanghai \u2192 Beijing)-", - "to_node": "fiber (Beijing \u2192 Seoul)-" + "from_node": "fiber (Shanghai → Beijing)-", + "to_node": "roadm Beijing" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Bangkok)-" + "to_node": "fiber (Delhi → Bangkok)-" }, { - "from_node": "fiber (Bangkok \u2192 Delhi)-", + "from_node": "fiber (Bangkok → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Istanbul)-" + "to_node": "fiber (Delhi → Istanbul)-" }, { - "from_node": "fiber (Istanbul \u2192 Delhi)-", + "from_node": "fiber (Istanbul → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Delhi", - "to_node": "fiber (Delhi \u2192 Mumbai)-" + "to_node": "fiber (Delhi → Mumbai)-" }, { - "from_node": "fiber (Mumbai \u2192 Delhi)-", + "from_node": "fiber (Mumbai → Delhi)-", "to_node": "roadm Delhi" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Bangkok)-" + "to_node": "fiber (Hong_Kong → Bangkok)-" }, { - "from_node": "fiber (Bangkok \u2192 Hong_Kong)-", + "from_node": "fiber (Bangkok → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Shanghai)-" + "to_node": "fiber (Hong_Kong → Shanghai)-" }, { - "from_node": "fiber (Shanghai \u2192 Hong_Kong)-", + "from_node": "fiber (Shanghai → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Sydney)-" + "to_node": "fiber (Hong_Kong → Sydney)-" }, { - "from_node": "fiber (Sydney \u2192 Hong_Kong)-", + "from_node": "fiber (Sydney → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Hong_Kong", - "to_node": "fiber (Hong_Kong \u2192 Taipei)-" + "to_node": "fiber (Hong_Kong → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Hong_Kong)-", + "from_node": "fiber (Taipei → Hong_Kong)-", "to_node": "roadm Hong_Kong" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Los_Angeles)-" + "to_node": "fiber (Honolulu → Los_Angeles)-" }, { - "from_node": "fiber (Los_Angeles \u2192 Honolulu)-", + "from_node": "fiber (Los_Angeles → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Sydney)-" + "to_node": "fiber (Honolulu → Sydney)-" }, { - "from_node": "fiber (Sydney \u2192 Honolulu)-", + "from_node": "fiber (Sydney → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Honolulu", - "to_node": "fiber (Honolulu \u2192 Taipei)-" + "to_node": "fiber (Honolulu → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Honolulu)-", + "from_node": "fiber (Taipei → Honolulu)-", "to_node": "roadm Honolulu" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Delhi)-" + "to_node": "fiber (Mumbai → Delhi)-" }, { - "from_node": "fiber (Delhi \u2192 Mumbai)-", + "from_node": "fiber (Delhi → Mumbai)-", "to_node": "roadm Mumbai" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Rome)-" + "to_node": "fiber (Mumbai → Rome)-" }, { - "from_node": "fiber (Rome \u2192 Mumbai)-", + "from_node": "fiber (Rome → Mumbai)-", "to_node": "roadm Mumbai" }, { "from_node": "roadm Mumbai", - "to_node": "fiber (Mumbai \u2192 Singapore)-" + "to_node": "fiber (Mumbai → Singapore)-" }, { - "from_node": "fiber (Singapore \u2192 Mumbai)-", + "from_node": "fiber (Singapore → Mumbai)-", "to_node": "roadm Mumbai" }, { - "from_node": "fiber (Beijing \u2192 Seoul)-", - "to_node": "fiber (Seoul \u2192 Tokyo)-" + "from_node": "roadm Seoul", + "to_node": "fiber (Seoul → Beijing)-" }, { - "from_node": "fiber (Tokyo \u2192 Seoul)-", - "to_node": "fiber (Seoul \u2192 Beijing)-" + "from_node": "fiber (Beijing → Seoul)-", + "to_node": "roadm Seoul" }, { - "from_node": "fiber (Beijing \u2192 Shanghai)-", - "to_node": "fiber (Shanghai \u2192 Hong_Kong)-" + "from_node": "roadm Seoul", + "to_node": "fiber (Seoul → Tokyo)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Shanghai)-", - "to_node": "fiber (Shanghai \u2192 Beijing)-" + "from_node": "fiber (Tokyo → Seoul)-", + "to_node": "roadm Seoul" }, { - "from_node": "fiber (Mumbai \u2192 Singapore)-", - "to_node": "fiber (Singapore \u2192 Sydney)-" + "from_node": "roadm Shanghai", + "to_node": "fiber (Shanghai → Beijing)-" }, { - "from_node": "fiber (Sydney \u2192 Singapore)-", - "to_node": "fiber (Singapore \u2192 Mumbai)-" + "from_node": "fiber (Beijing → Shanghai)-", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Shanghai", + "to_node": "fiber (Shanghai → Hong_Kong)-" + }, + { + "from_node": "fiber (Hong_Kong → Shanghai)-", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Singapore", + "to_node": "fiber (Singapore → Mumbai)-" + }, + { + "from_node": "fiber (Mumbai → Singapore)-", + "to_node": "roadm Singapore" + }, + { + "from_node": "roadm Singapore", + "to_node": "fiber (Singapore → Sydney)-" + }, + { + "from_node": "fiber (Sydney → Singapore)-", + "to_node": "roadm Singapore" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Hong_Kong)-" + "to_node": "fiber (Sydney → Hong_Kong)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Sydney)-", + "from_node": "fiber (Hong_Kong → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Honolulu)-" + "to_node": "fiber (Sydney → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Sydney)-", + "from_node": "fiber (Honolulu → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Sydney", - "to_node": "fiber (Sydney \u2192 Singapore)-" + "to_node": "fiber (Sydney → Singapore)-" }, { - "from_node": "fiber (Singapore \u2192 Sydney)-", + "from_node": "fiber (Singapore → Sydney)-", "to_node": "roadm Sydney" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Hong_Kong)-" + "to_node": "fiber (Taipei → Hong_Kong)-" }, { - "from_node": "fiber (Hong_Kong \u2192 Taipei)-", + "from_node": "fiber (Hong_Kong → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Honolulu)-" + "to_node": "fiber (Taipei → Honolulu)-" }, { - "from_node": "fiber (Honolulu \u2192 Taipei)-", + "from_node": "fiber (Honolulu → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Oakland)-" + "to_node": "fiber (Taipei → Oakland)-" }, { - "from_node": "fiber (Oakland \u2192 Taipei)-", + "from_node": "fiber (Oakland → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Taipei", - "to_node": "fiber (Taipei \u2192 Tokyo)-" + "to_node": "fiber (Taipei → Tokyo)-" }, { - "from_node": "fiber (Tokyo \u2192 Taipei)-", + "from_node": "fiber (Tokyo → Taipei)-", "to_node": "roadm Taipei" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Portland)-" + "to_node": "fiber (Tokyo → Portland)-" }, { - "from_node": "fiber (Portland \u2192 Tokyo)-", + "from_node": "fiber (Portland → Tokyo)-", "to_node": "roadm Tokyo" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Seoul)-" + "to_node": "fiber (Tokyo → Seoul)-" }, { - "from_node": "fiber (Seoul \u2192 Tokyo)-", + "from_node": "fiber (Seoul → Tokyo)-", "to_node": "roadm Tokyo" }, { "from_node": "roadm Tokyo", - "to_node": "fiber (Tokyo \u2192 Taipei)-" + "to_node": "fiber (Tokyo → Taipei)-" }, { - "from_node": "fiber (Taipei \u2192 Tokyo)-", + "from_node": "fiber (Taipei → Tokyo)-", "to_node": "roadm Tokyo" }, + { + "from_node": "trx Abilene", + "to_node": "roadm Abilene" + }, + { + "from_node": "roadm Abilene", + "to_node": "trx Abilene" + }, + { + "from_node": "trx Albany", + "to_node": "roadm Albany" + }, + { + "from_node": "roadm Albany", + "to_node": "trx Albany" + }, { "from_node": "trx Albuquerque", "to_node": "roadm Albuquerque" @@ -7922,6 +9506,14 @@ "from_node": "roadm Atlanta", "to_node": "trx Atlanta" }, + { + "from_node": "trx Austin", + "to_node": "roadm Austin" + }, + { + "from_node": "roadm Austin", + "to_node": "trx Austin" + }, { "from_node": "trx Baltimore", "to_node": "roadm Baltimore" @@ -7930,6 +9522,14 @@ "from_node": "roadm Baltimore", "to_node": "trx Baltimore" }, + { + "from_node": "trx Baton_Rouge", + "to_node": "roadm Baton_Rouge" + }, + { + "from_node": "roadm Baton_Rouge", + "to_node": "trx Baton_Rouge" + }, { "from_node": "trx Billings", "to_node": "roadm Billings" @@ -7946,6 +9546,46 @@ "from_node": "roadm Birmingham", "to_node": "trx Birmingham" }, + { + "from_node": "trx Bismarck", + "to_node": "roadm Bismarck" + }, + { + "from_node": "roadm Bismarck", + "to_node": "trx Bismarck" + }, + { + "from_node": "trx Boston", + "to_node": "roadm Boston" + }, + { + "from_node": "roadm Boston", + "to_node": "trx Boston" + }, + { + "from_node": "trx Buffalo", + "to_node": "roadm Buffalo" + }, + { + "from_node": "roadm Buffalo", + "to_node": "trx Buffalo" + }, + { + "from_node": "trx Charleston", + "to_node": "roadm Charleston" + }, + { + "from_node": "roadm Charleston", + "to_node": "trx Charleston" + }, + { + "from_node": "trx Charlotte", + "to_node": "roadm Charlotte" + }, + { + "from_node": "roadm Charlotte", + "to_node": "trx Charlotte" + }, { "from_node": "trx Chicago", "to_node": "roadm Chicago" @@ -7994,6 +9634,14 @@ "from_node": "roadm Denver", "to_node": "trx Denver" }, + { + "from_node": "trx Detroit", + "to_node": "roadm Detroit" + }, + { + "from_node": "roadm Detroit", + "to_node": "trx Detroit" + }, { "from_node": "trx El_Paso", "to_node": "roadm El_Paso" @@ -8018,6 +9666,14 @@ "from_node": "roadm Greensboro", "to_node": "trx Greensboro" }, + { + "from_node": "trx Hartford", + "to_node": "roadm Hartford" + }, + { + "from_node": "roadm Hartford", + "to_node": "trx Hartford" + }, { "from_node": "trx Houston", "to_node": "roadm Houston" @@ -8050,6 +9706,22 @@ "from_node": "roadm Las_Vegas", "to_node": "trx Las_Vegas" }, + { + "from_node": "trx Little_Rock", + "to_node": "roadm Little_Rock" + }, + { + "from_node": "roadm Little_Rock", + "to_node": "trx Little_Rock" + }, + { + "from_node": "trx Long_Island", + "to_node": "roadm Long_Island" + }, + { + "from_node": "roadm Long_Island", + "to_node": "trx Long_Island" + }, { "from_node": "trx Los_Angeles", "to_node": "roadm Los_Angeles" @@ -8066,6 +9738,14 @@ "from_node": "roadm Louisville", "to_node": "trx Louisville" }, + { + "from_node": "trx Memphis", + "to_node": "roadm Memphis" + }, + { + "from_node": "roadm Memphis", + "to_node": "trx Memphis" + }, { "from_node": "trx Miami", "to_node": "roadm Miami" @@ -8074,6 +9754,14 @@ "from_node": "roadm Miami", "to_node": "trx Miami" }, + { + "from_node": "trx Milwaukee", + "to_node": "roadm Milwaukee" + }, + { + "from_node": "roadm Milwaukee", + "to_node": "trx Milwaukee" + }, { "from_node": "trx Minneapolis", "to_node": "roadm Minneapolis" @@ -8106,6 +9794,22 @@ "from_node": "roadm New_York", "to_node": "trx New_York" }, + { + "from_node": "trx Newark", + "to_node": "roadm Newark" + }, + { + "from_node": "roadm Newark", + "to_node": "trx Newark" + }, + { + "from_node": "trx Norfolk", + "to_node": "roadm Norfolk" + }, + { + "from_node": "roadm Norfolk", + "to_node": "trx Norfolk" + }, { "from_node": "trx Oakland", "to_node": "roadm Oakland" @@ -8114,6 +9818,14 @@ "from_node": "roadm Oakland", "to_node": "trx Oakland" }, + { + "from_node": "trx Oklahoma_City", + "to_node": "roadm Oklahoma_City" + }, + { + "from_node": "roadm Oklahoma_City", + "to_node": "trx Oklahoma_City" + }, { "from_node": "trx Omaha", "to_node": "roadm Omaha" @@ -8122,6 +9834,14 @@ "from_node": "roadm Omaha", "to_node": "trx Omaha" }, + { + "from_node": "trx Orlando", + "to_node": "roadm Orlando" + }, + { + "from_node": "roadm Orlando", + "to_node": "trx Orlando" + }, { "from_node": "trx Philadelphia", "to_node": "roadm Philadelphia" @@ -8154,6 +9874,14 @@ "from_node": "roadm Portland", "to_node": "trx Portland" }, + { + "from_node": "trx Providence", + "to_node": "roadm Providence" + }, + { + "from_node": "roadm Providence", + "to_node": "trx Providence" + }, { "from_node": "trx Raleigh", "to_node": "roadm Raleigh" @@ -8162,6 +9890,30 @@ "from_node": "roadm Raleigh", "to_node": "trx Raleigh" }, + { + "from_node": "trx Richmond", + "to_node": "roadm Richmond" + }, + { + "from_node": "roadm Richmond", + "to_node": "trx Richmond" + }, + { + "from_node": "trx Rochester", + "to_node": "roadm Rochester" + }, + { + "from_node": "roadm Rochester", + "to_node": "trx Rochester" + }, + { + "from_node": "trx Sacramento", + "to_node": "roadm Sacramento" + }, + { + "from_node": "roadm Sacramento", + "to_node": "trx Sacramento" + }, { "from_node": "trx Salt_Lake_City", "to_node": "roadm Salt_Lake_City" @@ -8170,6 +9922,46 @@ "from_node": "roadm Salt_Lake_City", "to_node": "trx Salt_Lake_City" }, + { + "from_node": "trx San_Antonio", + "to_node": "roadm San_Antonio" + }, + { + "from_node": "roadm San_Antonio", + "to_node": "trx San_Antonio" + }, + { + "from_node": "trx San_Diego", + "to_node": "roadm San_Diego" + }, + { + "from_node": "roadm San_Diego", + "to_node": "trx San_Diego" + }, + { + "from_node": "trx San_Francisco", + "to_node": "roadm San_Francisco" + }, + { + "from_node": "roadm San_Francisco", + "to_node": "trx San_Francisco" + }, + { + "from_node": "trx San_Jose", + "to_node": "roadm San_Jose" + }, + { + "from_node": "roadm San_Jose", + "to_node": "trx San_Jose" + }, + { + "from_node": "trx Santa_Barbara", + "to_node": "roadm Santa_Barbara" + }, + { + "from_node": "roadm Santa_Barbara", + "to_node": "trx Santa_Barbara" + }, { "from_node": "trx Scranton", "to_node": "roadm Scranton" @@ -8178,6 +9970,30 @@ "from_node": "roadm Scranton", "to_node": "trx Scranton" }, + { + "from_node": "trx Seattle", + "to_node": "roadm Seattle" + }, + { + "from_node": "roadm Seattle", + "to_node": "trx Seattle" + }, + { + "from_node": "trx Spokane", + "to_node": "roadm Spokane" + }, + { + "from_node": "roadm Spokane", + "to_node": "trx Spokane" + }, + { + "from_node": "trx Springfield", + "to_node": "roadm Springfield" + }, + { + "from_node": "roadm Springfield", + "to_node": "trx Springfield" + }, { "from_node": "trx St_Louis", "to_node": "roadm St_Louis" @@ -8194,6 +10010,46 @@ "from_node": "roadm Syracuse", "to_node": "trx Syracuse" }, + { + "from_node": "trx Tallahassee", + "to_node": "roadm Tallahassee" + }, + { + "from_node": "roadm Tallahassee", + "to_node": "trx Tallahassee" + }, + { + "from_node": "trx Tampa", + "to_node": "roadm Tampa" + }, + { + "from_node": "roadm Tampa", + "to_node": "trx Tampa" + }, + { + "from_node": "trx Toledo", + "to_node": "roadm Toledo" + }, + { + "from_node": "roadm Toledo", + "to_node": "trx Toledo" + }, + { + "from_node": "trx Tucson", + "to_node": "roadm Tucson" + }, + { + "from_node": "roadm Tucson", + "to_node": "trx Tucson" + }, + { + "from_node": "trx Tulsa", + "to_node": "roadm Tulsa" + }, + { + "from_node": "roadm Tulsa", + "to_node": "trx Tulsa" + }, { "from_node": "trx Washington_DC", "to_node": "roadm Washington_DC" @@ -8202,6 +10058,22 @@ "from_node": "roadm Washington_DC", "to_node": "trx Washington_DC" }, + { + "from_node": "trx West_Palm_Beach", + "to_node": "roadm West_Palm_Beach" + }, + { + "from_node": "roadm West_Palm_Beach", + "to_node": "trx West_Palm_Beach" + }, + { + "from_node": "trx Wilmington", + "to_node": "roadm Wilmington" + }, + { + "from_node": "roadm Wilmington", + "to_node": "trx Wilmington" + }, { "from_node": "trx Amsterdam", "to_node": "roadm Amsterdam" @@ -8210,6 +10082,38 @@ "from_node": "roadm Amsterdam", "to_node": "trx Amsterdam" }, + { + "from_node": "trx Berlin", + "to_node": "roadm Berlin" + }, + { + "from_node": "roadm Berlin", + "to_node": "trx Berlin" + }, + { + "from_node": "trx Brussels", + "to_node": "roadm Brussels" + }, + { + "from_node": "roadm Brussels", + "to_node": "trx Brussels" + }, + { + "from_node": "trx Bucharest", + "to_node": "roadm Bucharest" + }, + { + "from_node": "roadm Bucharest", + "to_node": "trx Bucharest" + }, + { + "from_node": "trx Frankfurt", + "to_node": "roadm Frankfurt" + }, + { + "from_node": "roadm Frankfurt", + "to_node": "trx Frankfurt" + }, { "from_node": "trx Istanbul", "to_node": "roadm Istanbul" @@ -8226,6 +10130,14 @@ "from_node": "roadm London", "to_node": "trx London" }, + { + "from_node": "trx Madrid", + "to_node": "roadm Madrid" + }, + { + "from_node": "roadm Madrid", + "to_node": "trx Madrid" + }, { "from_node": "trx Paris", "to_node": "roadm Paris" @@ -8258,6 +10170,30 @@ "from_node": "roadm Warsaw", "to_node": "trx Warsaw" }, + { + "from_node": "trx Zurich", + "to_node": "roadm Zurich" + }, + { + "from_node": "roadm Zurich", + "to_node": "trx Zurich" + }, + { + "from_node": "trx Bangkok", + "to_node": "roadm Bangkok" + }, + { + "from_node": "roadm Bangkok", + "to_node": "trx Bangkok" + }, + { + "from_node": "trx Beijing", + "to_node": "roadm Beijing" + }, + { + "from_node": "roadm Beijing", + "to_node": "trx Beijing" + }, { "from_node": "trx Delhi", "to_node": "roadm Delhi" @@ -8290,6 +10226,30 @@ "from_node": "roadm Mumbai", "to_node": "trx Mumbai" }, + { + "from_node": "trx Seoul", + "to_node": "roadm Seoul" + }, + { + "from_node": "roadm Seoul", + "to_node": "trx Seoul" + }, + { + "from_node": "trx Shanghai", + "to_node": "roadm Shanghai" + }, + { + "from_node": "roadm Shanghai", + "to_node": "trx Shanghai" + }, + { + "from_node": "trx Singapore", + "to_node": "roadm Singapore" + }, + { + "from_node": "roadm Singapore", + "to_node": "trx Singapore" + }, { "from_node": "trx Sydney", "to_node": "roadm Sydney" diff --git a/tests/data/excelTestFile.xls b/tests/data/excelTestFile.xls index 7e168f39c4164d7227063456fcf6a2896e76a78c..68de98c3631fd0554f0ffa7db2f8c70df4df4506 100644 GIT binary patch delta 789 zcmaJ5V()1;8?dyr*72QS za~Oo-nZOSt$L|iD=DlIROYx}hJX!(_a$r#@X!njPwt_Gc)#?T9&Zt_3Mz5X_FC$M% z8P+J1Ol6ppYBewWkBV8|1Xv<=kIm~;#LbkQL42KMtTnQ;calft)ns3Vhg4IaKXb*L z6gQ0m>0_8CFBIba+3}4A<^lJbyRhEw2BEVWY&mQFuHW<4g3X5=f4Jbb&bn7;umtzN z@8WN#9zSg=6G&w{3YHuARL+!ha3ofSb-Geg$#9NlR`VuIjA$}xa>Ew6n$II&16NK! zqtfU<>nHSvydAE{&%{pBufS{6QEsC~Zkn`>cKoLC(&zc(|D2KA8BWirU^zsylI3+0 zt=6Y!oD2!gPT6O~?&5QUy^ZVMGDCdZtLuPm6MtRocpVlm>(qEv-< delta 803 zcmaJ<&ubGw6n-;1*-g^yrfD16qBdi-p(%@){(ypris+$8K$d3N8+73Aa=U!4d{&;%|d#x0bt_+;6`q{qmt8el_h#Vsg!|Y!WMQTcrsUGicB`7 zv4vVqUY1Tuns)$5mVOw$$SPH^9At^A&A=N;zl;;oOgx<}yP4gotZsBe*;wt*L@763 z1v&;rquLlCE{e#niEC49cQ4_5P`_QDUTsHVpSQwyf`lK3)l|p(OyNe(?CM dSa0(Do2`exGv|be*UV2pHhhlDJF^qQLpy2ijcN@(oN#V;-uQ2^9y4swIUq&;*EuyVj3R9$pS{2f z>#JcDO@u+zW#oxxgPB9Ndw0@2(h`6z%upyYZUtv=y)OiaQ zI$6cr0Ezj6yKU7vIaQWz^)ZHQqqVi9rEJGM6RY@?!ylxkjTSeks7S87d|h|>zEkV0Hg zmZo2>jgp#g=Mb`rD`b!*5@o5Y|mq`G?vylYHlYrfl;c D?kR$+ delta 749 zcmZoDXeiilg^iJU^HsKJHWMRW1|VQ%@XJq0EpBDtWndG52>4{?WrGDyN3CJYP?K*C3yNU0Nu^Lw0FMaEJ6oFG1qI|tk60>K zw~_F{OPmr;Slq2Ox)6XFV^g^j+@krTya};1xCHxgqL|Q1bxqeNw4&ZHK7;Z85l$;t zd*Ys^H_TY*OIU4%0wCbzL-cUqwG@U0R4gyU1QYVQ1QN7-OqT&1mbo4|meiJ?(R3_t zBhWG2Sjm3o861*G8j{9JvM;=*m70}eJ(W@OnT05fL59-uA8k~U2kk6pOdT#bOoKlJ zSONkBEEmI0EYb=xEaD=(E`tm$Oh^wc#)X&H-QW%b;hOWxzn+PoVY_Dz)416n(`npiAHyPAs2+ytKSVi9<6mI z+wuSU0l|;<08#UCbWXa@6^VBmU>=zcoaKy)nsA+bq&DtuULD4fbKHDVXNlw8{GO7@ EFWla1!T*lR#OVe%uOv;@JlUM2+Ge*%+vA9FUn2KQBpHBQgak# z&|px-r)+YCVs*VHlEXkQn2N*)d2A;?!iCHX%nT1W5PTM3@IDYg@L7Q#MV4o0U}q=? z`Tzt#0mlJ^$nu;FoI(sNNFmDw_F{`t8#5m_IJg-ZxPg#~Y4U1?%*l;vlj>PGB~jvm zg%808#{&`{Cj-djF;q8cDH`%W+ VPHtq;*?hpzf|>E^=AS0)%mCO0bn^fJ diff --git a/tests/data/meshTopologyExampleV2Eqpt_expected.json b/tests/data/meshTopologyExampleV2Eqpt_expected.json index 61ececd63..b452711dc 100644 --- a/tests/data/meshTopologyExampleV2Eqpt_expected.json +++ b/tests/data/meshTopologyExampleV2Eqpt_expected.json @@ -169,7 +169,7 @@ "type": "Roadm" }, { - "uid": "ingress fused spans in Corlay", + "uid": "west fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -181,7 +181,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Loudeac", + "uid": "west fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -193,7 +193,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Morlaix", + "uid": "west fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -205,7 +205,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Corlay", + "uid": "east fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -217,7 +217,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Loudeac", + "uid": "east fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -229,7 +229,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Morlaix", + "uid": "east fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -241,7 +241,7 @@ "type": "Fused" }, { - "uid": "fiber (Lannion_CAS \u2192 Corlay)-F061", + "uid": "fiber (Lannion_CAS → Corlay)-F061", "metadata": { "location": { "latitude": 2.0, @@ -259,7 +259,7 @@ } }, { - "uid": "fiber (Corlay \u2192 Loudeac)-F010", + "uid": "fiber (Corlay → Loudeac)-F010", "metadata": { "location": { "latitude": 2.0, @@ -277,7 +277,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "uid": "fiber (Loudeac → Lorient_KMA)-F054", "metadata": { "location": { "latitude": 2.0, @@ -295,7 +295,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055", "metadata": { "location": { "latitude": 2.0, @@ -313,7 +313,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", + "uid": "fiber (Lannion_CAS → Stbrieuc)-F056", "metadata": { "location": { "latitude": 1.5, @@ -331,7 +331,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "uid": "fiber (Stbrieuc → Rennes_STA)-F057", "metadata": { "location": { "latitude": 0.5, @@ -349,7 +349,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059", + "uid": "fiber (Lannion_CAS → Morlaix)-F059", "metadata": { "location": { "latitude": 2.5, @@ -367,7 +367,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "uid": "fiber (Morlaix → Brest_KLA)-F060", "metadata": { "location": { "latitude": 3.5, @@ -385,7 +385,7 @@ } }, { - "uid": "fiber (toto \u2192 tata)-", + "uid": "fiber (toto → tata)-", "metadata": { "location": { "latitude": 6.5, @@ -403,7 +403,7 @@ } }, { - "uid": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "uid": "fiber (Corlay → Lannion_CAS)-F061", "metadata": { "location": { "latitude": 2.0, @@ -421,7 +421,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Corlay)-F010", + "uid": "fiber (Loudeac → Corlay)-F010", "metadata": { "location": { "latitude": 2.0, @@ -439,7 +439,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054", + "uid": "fiber (Lorient_KMA → Loudeac)-F054", "metadata": { "location": { "latitude": 2.0, @@ -457,7 +457,7 @@ } }, { - "uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055", "metadata": { "location": { "latitude": 2.0, @@ -475,7 +475,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "uid": "fiber (Stbrieuc → Lannion_CAS)-F056", "metadata": { "location": { "latitude": 1.5, @@ -493,7 +493,7 @@ } }, { - "uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", + "uid": "fiber (Rennes_STA → Stbrieuc)-F057", "metadata": { "location": { "latitude": 0.5, @@ -511,7 +511,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "uid": "fiber (Morlaix → Lannion_CAS)-F059", "metadata": { "location": { "latitude": 2.5, @@ -529,7 +529,7 @@ } }, { - "uid": "fiber (Brest_KLA \u2192 Morlaix)-F060", + "uid": "fiber (Brest_KLA → Morlaix)-F060", "metadata": { "location": { "latitude": 3.5, @@ -547,7 +547,7 @@ } }, { - "uid": "fiber (tata \u2192 toto)-", + "uid": "fiber (tata → toto)-", "metadata": { "location": { "latitude": 6.5, @@ -565,7 +565,7 @@ } }, { - "uid": "egress edfa in Lannion_CAS to Corlay", + "uid": "east edfa in Lannion_CAS to Corlay", "metadata": { "location": { "city": "Lannion_CAS", @@ -578,11 +578,12 @@ "type_variety": "test", "operational": { "gain_target": 0, - "tilt_target": 0 + "tilt_target": 0, + "out_voa": 0 } }, { - "uid": "egress edfa in Lorient_KMA to Loudeac", + "uid": "east edfa in Lorient_KMA to Loudeac", "metadata": { "location": { "city": "Lorient_KMA", @@ -595,153 +596,154 @@ "type_variety": "test", "operational": { "gain_target": 0, - "tilt_target": 0 + "tilt_target": 0, + "out_voa": 0 } } ], "connections": [ { "from_node": "roadm Lannion_CAS", - "to_node": "egress edfa in Lannion_CAS to Corlay" + "to_node": "east edfa in Lannion_CAS to Corlay" }, { - "from_node": "egress edfa in Lannion_CAS to Corlay", - "to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061" + "from_node": "east edfa in Lannion_CAS to Corlay", + "to_node": "fiber (Lannion_CAS → Corlay)-F061" }, { - "from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "from_node": "fiber (Corlay → Lannion_CAS)-F061", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056" + "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" }, { - "from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059" + "to_node": "fiber (Lannion_CAS → Morlaix)-F059" }, { - "from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "from_node": "fiber (Morlaix → Lannion_CAS)-F059", "to_node": "roadm Lannion_CAS" }, { - "from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061", - "to_node": "ingress fused spans in Corlay" + "from_node": "fiber (Lannion_CAS → Corlay)-F061", + "to_node": "west fused spans in Corlay" }, { - "from_node": "ingress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Loudeac)-F010" + "from_node": "west fused spans in Corlay", + "to_node": "fiber (Corlay → Loudeac)-F010" }, { - "from_node": "fiber (Loudeac \u2192 Corlay)-F010", - "to_node": "egress fused spans in Corlay" + "from_node": "fiber (Loudeac → Corlay)-F010", + "to_node": "east fused spans in Corlay" }, { - "from_node": "egress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061" + "from_node": "east fused spans in Corlay", + "to_node": "fiber (Corlay → Lannion_CAS)-F061" }, { - "from_node": "fiber (Corlay \u2192 Loudeac)-F010", - "to_node": "ingress fused spans in Loudeac" + "from_node": "fiber (Corlay → Loudeac)-F010", + "to_node": "west fused spans in Loudeac" }, { - "from_node": "ingress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054" + "from_node": "west fused spans in Loudeac", + "to_node": "fiber (Loudeac → Lorient_KMA)-F054" }, { - "from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054", - "to_node": "egress fused spans in Loudeac" + "from_node": "fiber (Lorient_KMA → Loudeac)-F054", + "to_node": "east fused spans in Loudeac" }, { - "from_node": "egress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Corlay)-F010" + "from_node": "east fused spans in Loudeac", + "to_node": "fiber (Loudeac → Corlay)-F010" }, { "from_node": "roadm Lorient_KMA", - "to_node": "egress edfa in Lorient_KMA to Loudeac" + "to_node": "east edfa in Lorient_KMA to Loudeac" }, { - "from_node": "egress edfa in Lorient_KMA to Loudeac", - "to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054" + "from_node": "east edfa in Lorient_KMA to Loudeac", + "to_node": "fiber (Lorient_KMA → Loudeac)-F054" }, { - "from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "from_node": "fiber (Loudeac → Lorient_KMA)-F054", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055" + "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" }, { - "from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Vannes_KBE", - "to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055" + "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" }, { - "from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", "to_node": "roadm Vannes_KBE" }, { - "from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", - "to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057" + "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", + "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" }, { - "from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", - "to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056" + "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", + "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" }, { "from_node": "roadm Rennes_STA", - "to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057" + "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" }, { - "from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", "to_node": "roadm Rennes_STA" }, { - "from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059", - "to_node": "ingress fused spans in Morlaix" + "from_node": "fiber (Lannion_CAS → Morlaix)-F059", + "to_node": "west fused spans in Morlaix" }, { - "from_node": "ingress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060" + "from_node": "west fused spans in Morlaix", + "to_node": "fiber (Morlaix → Brest_KLA)-F060" }, { - "from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060", - "to_node": "egress fused spans in Morlaix" + "from_node": "fiber (Brest_KLA → Morlaix)-F060", + "to_node": "east fused spans in Morlaix" }, { - "from_node": "egress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059" + "from_node": "east fused spans in Morlaix", + "to_node": "fiber (Morlaix → Lannion_CAS)-F059" }, { "from_node": "roadm Brest_KLA", - "to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060" + "to_node": "fiber (Brest_KLA → Morlaix)-F060" }, { - "from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "from_node": "fiber (Morlaix → Brest_KLA)-F060", "to_node": "roadm Brest_KLA" }, { "from_node": "roadm toto", - "to_node": "fiber (toto \u2192 tata)-" + "to_node": "fiber (toto → tata)-" }, { - "from_node": "fiber (tata \u2192 toto)-", + "from_node": "fiber (tata → toto)-", "to_node": "roadm toto" }, { "from_node": "roadm tata", - "to_node": "fiber (tata \u2192 toto)-" + "to_node": "fiber (tata → toto)-" }, { - "from_node": "fiber (toto \u2192 tata)-", + "from_node": "fiber (toto → tata)-", "to_node": "roadm tata" }, { diff --git a/tests/data/meshTopologyExampleV2_expected.json b/tests/data/meshTopologyExampleV2_expected.json index 3bda32688..2bc65e99f 100644 --- a/tests/data/meshTopologyExampleV2_expected.json +++ b/tests/data/meshTopologyExampleV2_expected.json @@ -169,7 +169,7 @@ "type": "Roadm" }, { - "uid": "ingress fused spans in Corlay", + "uid": "west fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -181,7 +181,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Loudeac", + "uid": "west fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -193,7 +193,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Morlaix", + "uid": "west fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -205,7 +205,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Corlay", + "uid": "east fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -217,7 +217,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Loudeac", + "uid": "east fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -229,7 +229,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Morlaix", + "uid": "east fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -241,7 +241,7 @@ "type": "Fused" }, { - "uid": "fiber (Lannion_CAS \u2192 Corlay)-F061", + "uid": "fiber (Lannion_CAS → Corlay)-F061", "metadata": { "location": { "latitude": 2.0, @@ -259,7 +259,7 @@ } }, { - "uid": "fiber (Corlay \u2192 Loudeac)-F010", + "uid": "fiber (Corlay → Loudeac)-F010", "metadata": { "location": { "latitude": 2.0, @@ -277,7 +277,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "uid": "fiber (Loudeac → Lorient_KMA)-F054", "metadata": { "location": { "latitude": 2.0, @@ -295,7 +295,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "uid": "fiber (Lorient_KMA → Vannes_KBE)-F055", "metadata": { "location": { "latitude": 2.0, @@ -313,7 +313,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", + "uid": "fiber (Lannion_CAS → Stbrieuc)-F056", "metadata": { "location": { "latitude": 1.5, @@ -331,7 +331,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "uid": "fiber (Stbrieuc → Rennes_STA)-F057", "metadata": { "location": { "latitude": 0.5, @@ -349,7 +349,7 @@ } }, { - "uid": "fiber (Lannion_CAS \u2192 Morlaix)-F059", + "uid": "fiber (Lannion_CAS → Morlaix)-F059", "metadata": { "location": { "latitude": 2.5, @@ -367,7 +367,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "uid": "fiber (Morlaix → Brest_KLA)-F060", "metadata": { "location": { "latitude": 3.5, @@ -385,7 +385,7 @@ } }, { - "uid": "fiber (toto \u2192 tata)-", + "uid": "fiber (toto → tata)-", "metadata": { "location": { "latitude": 6.5, @@ -403,7 +403,7 @@ } }, { - "uid": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "uid": "fiber (Corlay → Lannion_CAS)-F061", "metadata": { "location": { "latitude": 2.0, @@ -421,7 +421,7 @@ } }, { - "uid": "fiber (Loudeac \u2192 Corlay)-F010", + "uid": "fiber (Loudeac → Corlay)-F010", "metadata": { "location": { "latitude": 2.0, @@ -439,7 +439,7 @@ } }, { - "uid": "fiber (Lorient_KMA \u2192 Loudeac)-F054", + "uid": "fiber (Lorient_KMA → Loudeac)-F054", "metadata": { "location": { "latitude": 2.0, @@ -457,7 +457,7 @@ } }, { - "uid": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "uid": "fiber (Vannes_KBE → Lorient_KMA)-F055", "metadata": { "location": { "latitude": 2.0, @@ -475,7 +475,7 @@ } }, { - "uid": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "uid": "fiber (Stbrieuc → Lannion_CAS)-F056", "metadata": { "location": { "latitude": 1.5, @@ -493,7 +493,7 @@ } }, { - "uid": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", + "uid": "fiber (Rennes_STA → Stbrieuc)-F057", "metadata": { "location": { "latitude": 0.5, @@ -511,7 +511,7 @@ } }, { - "uid": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "uid": "fiber (Morlaix → Lannion_CAS)-F059", "metadata": { "location": { "latitude": 2.5, @@ -529,7 +529,7 @@ } }, { - "uid": "fiber (Brest_KLA \u2192 Morlaix)-F060", + "uid": "fiber (Brest_KLA → Morlaix)-F060", "metadata": { "location": { "latitude": 3.5, @@ -547,7 +547,7 @@ } }, { - "uid": "fiber (tata \u2192 toto)-", + "uid": "fiber (tata → toto)-", "metadata": { "location": { "latitude": 6.5, @@ -568,138 +568,138 @@ "connections": [ { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Corlay)-F061" + "to_node": "fiber (Lannion_CAS → Corlay)-F061" }, { - "from_node": "fiber (Corlay \u2192 Lannion_CAS)-F061", + "from_node": "fiber (Corlay → Lannion_CAS)-F061", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056" + "to_node": "fiber (Lannion_CAS → Stbrieuc)-F056" }, { - "from_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056", + "from_node": "fiber (Stbrieuc → Lannion_CAS)-F056", "to_node": "roadm Lannion_CAS" }, { "from_node": "roadm Lannion_CAS", - "to_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059" + "to_node": "fiber (Lannion_CAS → Morlaix)-F059" }, { - "from_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059", + "from_node": "fiber (Morlaix → Lannion_CAS)-F059", "to_node": "roadm Lannion_CAS" }, { - "from_node": "fiber (Lannion_CAS \u2192 Corlay)-F061", - "to_node": "ingress fused spans in Corlay" + "from_node": "fiber (Lannion_CAS → Corlay)-F061", + "to_node": "west fused spans in Corlay" }, { - "from_node": "ingress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Loudeac)-F010" + "from_node": "west fused spans in Corlay", + "to_node": "fiber (Corlay → Loudeac)-F010" }, { - "from_node": "fiber (Loudeac \u2192 Corlay)-F010", - "to_node": "egress fused spans in Corlay" + "from_node": "fiber (Loudeac → Corlay)-F010", + "to_node": "east fused spans in Corlay" }, { - "from_node": "egress fused spans in Corlay", - "to_node": "fiber (Corlay \u2192 Lannion_CAS)-F061" + "from_node": "east fused spans in Corlay", + "to_node": "fiber (Corlay → Lannion_CAS)-F061" }, { - "from_node": "fiber (Corlay \u2192 Loudeac)-F010", - "to_node": "ingress fused spans in Loudeac" + "from_node": "fiber (Corlay → Loudeac)-F010", + "to_node": "west fused spans in Loudeac" }, { - "from_node": "ingress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054" + "from_node": "west fused spans in Loudeac", + "to_node": "fiber (Loudeac → Lorient_KMA)-F054" }, { - "from_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054", - "to_node": "egress fused spans in Loudeac" + "from_node": "fiber (Lorient_KMA → Loudeac)-F054", + "to_node": "east fused spans in Loudeac" }, { - "from_node": "egress fused spans in Loudeac", - "to_node": "fiber (Loudeac \u2192 Corlay)-F010" + "from_node": "east fused spans in Loudeac", + "to_node": "fiber (Loudeac → Corlay)-F010" }, { "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA \u2192 Loudeac)-F054" + "to_node": "fiber (Lorient_KMA → Loudeac)-F054" }, { - "from_node": "fiber (Loudeac \u2192 Lorient_KMA)-F054", + "from_node": "fiber (Loudeac → Lorient_KMA)-F054", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Lorient_KMA", - "to_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055" + "to_node": "fiber (Lorient_KMA → Vannes_KBE)-F055" }, { - "from_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055", + "from_node": "fiber (Vannes_KBE → Lorient_KMA)-F055", "to_node": "roadm Lorient_KMA" }, { "from_node": "roadm Vannes_KBE", - "to_node": "fiber (Vannes_KBE \u2192 Lorient_KMA)-F055" + "to_node": "fiber (Vannes_KBE → Lorient_KMA)-F055" }, { - "from_node": "fiber (Lorient_KMA \u2192 Vannes_KBE)-F055", + "from_node": "fiber (Lorient_KMA → Vannes_KBE)-F055", "to_node": "roadm Vannes_KBE" }, { - "from_node": "fiber (Lannion_CAS \u2192 Stbrieuc)-F056", - "to_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057" + "from_node": "fiber (Lannion_CAS → Stbrieuc)-F056", + "to_node": "fiber (Stbrieuc → Rennes_STA)-F057" }, { - "from_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057", - "to_node": "fiber (Stbrieuc \u2192 Lannion_CAS)-F056" + "from_node": "fiber (Rennes_STA → Stbrieuc)-F057", + "to_node": "fiber (Stbrieuc → Lannion_CAS)-F056" }, { "from_node": "roadm Rennes_STA", - "to_node": "fiber (Rennes_STA \u2192 Stbrieuc)-F057" + "to_node": "fiber (Rennes_STA → Stbrieuc)-F057" }, { - "from_node": "fiber (Stbrieuc \u2192 Rennes_STA)-F057", + "from_node": "fiber (Stbrieuc → Rennes_STA)-F057", "to_node": "roadm Rennes_STA" }, { - "from_node": "fiber (Lannion_CAS \u2192 Morlaix)-F059", - "to_node": "ingress fused spans in Morlaix" + "from_node": "fiber (Lannion_CAS → Morlaix)-F059", + "to_node": "west fused spans in Morlaix" }, { - "from_node": "ingress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Brest_KLA)-F060" + "from_node": "west fused spans in Morlaix", + "to_node": "fiber (Morlaix → Brest_KLA)-F060" }, { - "from_node": "fiber (Brest_KLA \u2192 Morlaix)-F060", - "to_node": "egress fused spans in Morlaix" + "from_node": "fiber (Brest_KLA → Morlaix)-F060", + "to_node": "east fused spans in Morlaix" }, { - "from_node": "egress fused spans in Morlaix", - "to_node": "fiber (Morlaix \u2192 Lannion_CAS)-F059" + "from_node": "east fused spans in Morlaix", + "to_node": "fiber (Morlaix → Lannion_CAS)-F059" }, { "from_node": "roadm Brest_KLA", - "to_node": "fiber (Brest_KLA \u2192 Morlaix)-F060" + "to_node": "fiber (Brest_KLA → Morlaix)-F060" }, { - "from_node": "fiber (Morlaix \u2192 Brest_KLA)-F060", + "from_node": "fiber (Morlaix → Brest_KLA)-F060", "to_node": "roadm Brest_KLA" }, { "from_node": "roadm toto", - "to_node": "fiber (toto \u2192 tata)-" + "to_node": "fiber (toto → tata)-" }, { - "from_node": "fiber (tata \u2192 toto)-", + "from_node": "fiber (tata → toto)-", "to_node": "roadm toto" }, { "from_node": "roadm tata", - "to_node": "fiber (tata \u2192 toto)-" + "to_node": "fiber (tata → toto)-" }, { - "from_node": "fiber (toto \u2192 tata)-", + "from_node": "fiber (toto → tata)-", "to_node": "roadm tata" }, { diff --git a/tests/test_parser.py b/tests/test_parser.py index 751bcb382..a3c037a4e 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -39,7 +39,7 @@ def test_excel_json_generation(xls_input, expected_json_output): actual_json_output = xls_input.with_suffix('.json') with open(actual_json_output, encoding='utf-8') as f: actual = load(f) - unlink(actual_json_output) + #unlink(actual_json_output) with open(expected_json_output, encoding='utf-8') as f: expected = load(f) From fa949f977af9e7ffab088dfe5c35bfd0ad6c4052 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 13 Dec 2018 10:55:03 +0100 Subject: [PATCH 094/108] add name find closest match in xls parser convert.py Signed-off-by: Jean-Luc Auge output node name gestalt matching to a json Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index a2952e555..a36b20728 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -243,9 +243,24 @@ def convert_file(input_filename, filter_region=[]): cities = {lnk.from_city for lnk in links} | {lnk.to_city for lnk in links} nodes = [n for n in nodes if n.city in cities] + global nodes_by_city nodes_by_city = {n.city: n for n in nodes} + #create matching dictionary for node name mismatch analysis + cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes} + cities_to_match = [k for k in cities] + city_match_dic = defaultdict(list) + for city in cities: + if city in cities_to_match: + cities_to_match.remove(city) + matches = get_close_matches(city, cities_to_match, 4, 0.85) + for m in matches: + city_match_dic[cities[city]].append(cities[m]) + #print(city_match_dic) + with open('name_match_dictionary.json', 'w', encoding='utf-8') as city_match_dic_file: + city_match_dic_file.write(dumps(city_match_dic, indent=2, ensure_ascii=False)) + global links_by_city links_by_city = defaultdict(list) for link in links: From c9106c3a6f3f333e892bdf0f1672370fa15e63a2 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 17 Dec 2018 17:18:00 +0100 Subject: [PATCH 095/108] enhance name matching dictionary Signed-off-by: Jean-Luc Auge --- gnpy/core/convert.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index a36b20728..69f1ecaf2 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -200,8 +200,8 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city): 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) - if duplicate_links != []: - time.sleep(3) + #if duplicate_links != []: + #time.sleep(3) for l in duplicate_links: links.remove(l) @@ -247,7 +247,8 @@ def convert_file(input_filename, filter_region=[]): global nodes_by_city nodes_by_city = {n.city: n for n in nodes} - #create matching dictionary for node name mismatch analysis + #create matching dictionary for node name mismatch analysis + cities = {''.join(c.strip() for c in n.city.split('C+L')).lower(): n.city for n in nodes} cities_to_match = [k for k in cities] city_match_dic = defaultdict(list) @@ -257,6 +258,12 @@ def convert_file(input_filename, filter_region=[]): matches = get_close_matches(city, cities_to_match, 4, 0.85) for m in matches: city_match_dic[cities[city]].append(cities[m]) + #check lower case/upper case + for city in nodes_by_city: + for match_city in nodes_by_city: + if match_city.lower() == city.lower() and match_city != city: + city_match_dic[city].append(match_city) + #print(city_match_dic) with open('name_match_dictionary.json', 'w', encoding='utf-8') as city_match_dic_file: city_match_dic_file.write(dumps(city_match_dic, indent=2, ensure_ascii=False)) From bc9eee326a3911fd9a1cc621ce5f2c073d63c076 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Fri, 11 Jan 2019 11:33:03 +0100 Subject: [PATCH 096/108] parametrization of the name matching dictionary -names argument Signed-off-by: Jean-Luc Auge --- examples/transmission_main_example.py | 3 ++- gnpy/core/convert.py | 5 +++-- gnpy/core/network.py | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/transmission_main_example.py b/examples/transmission_main_example.py index ce091441f..af452a000 100755 --- a/examples/transmission_main_example.py +++ b/examples/transmission_main_example.py @@ -116,6 +116,7 @@ def main(network, equipment, source, destination, req = None): parser.add_argument('-v', '--verbose', action='count', default=0, help='increases verbosity for each occurence') 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('-names', '--names-matching', action='store_true', help='display network names that are closed matches') #parser.add_argument('-plb', '--power-lower-bound', default=0, help='power sweep lower bound') #parser.add_argument('-pub', '--power-upper-bound', default=1, help='power sweep upper bound') parser.add_argument('filename', nargs='?', type=Path, @@ -131,7 +132,7 @@ def main(network, equipment, source, destination, req = None): equipment = load_equipment(args.equipment) # logger.info(equipment) # print(args.filename) - network = load_network(args.filename, equipment) + network = load_network(args.filename, equipment, args.names_matching) # print(network) transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)} diff --git a/gnpy/core/convert.py b/gnpy/core/convert.py index 69f1ecaf2..6eea917ef 100755 --- a/gnpy/core/convert.py +++ b/gnpy/core/convert.py @@ -232,7 +232,7 @@ def sanity_check(nodes, links, nodes_by_city, links_by_city, eqpts_by_city): n.node_type='ROADM' return nodes, links -def convert_file(input_filename, filter_region=[]): +def convert_file(input_filename, names_matching=False, filter_region=[]): nodes, links, eqpts = parse_excel(input_filename) if filter_region: @@ -264,7 +264,8 @@ def convert_file(input_filename, filter_region=[]): if match_city.lower() == city.lower() and match_city != city: city_match_dic[city].append(match_city) - #print(city_match_dic) + if names_matching: + print('\ncity match dictionary:',city_match_dic) with open('name_match_dictionary.json', 'w', encoding='utf-8') as city_match_dic_file: city_match_dic_file.write(dumps(city_match_dic, indent=2, ensure_ascii=False)) diff --git a/gnpy/core/network.py b/gnpy/core/network.py index bd53a5357..6e4131d20 100644 --- a/gnpy/core/network.py +++ b/gnpy/core/network.py @@ -24,11 +24,11 @@ logger = getLogger(__name__) -def load_network(filename, equipment): +def load_network(filename, equipment, name_matching = False): json_filename = '' if filename.suffix.lower() == '.xls': logger.info('Automatically generating topology JSON file') - json_filename = convert_file(filename) + json_filename = convert_file(filename, name_matching) elif filename.suffix.lower() == '.json': json_filename = filename else: From 92f3fd206331637f23a9afa628532afad5ed0ad5 Mon Sep 17 00:00:00 2001 From: James Date: Tue, 15 Jan 2019 11:33:17 -0500 Subject: [PATCH 097/108] Update node.py --- gnpy/core/node.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gnpy/core/node.py b/gnpy/core/node.py index 72e9c5102..875b6d946 100644 --- a/gnpy/core/node.py +++ b/gnpy/core/node.py @@ -36,7 +36,7 @@ def __init__(self, uid, name=None, params=None, metadata={'location':{}}, operat @property def coords(self): - return (self.lng, self.lat) + return self.lng, self.lat @property def location(self): From 0338ccb08f08a333109d3c540c45af427ebcb05f Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 16 Jan 2019 12:24:57 +0100 Subject: [PATCH 098/108] restore SI basic parameters f_min f_max & spacing remove unnecessary SI parameters in eqpt config.json: -rx osnr -bit rate -min spacing -cost remove nb_channel parameter when calling SI Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 6 +----- examples/path_requests_run.py | 30 ++++++++++++++++++------------ gnpy/core/equipment.py | 26 +++++++++++++------------- gnpy/core/info.py | 8 ++++---- gnpy/core/request.py | 19 ++++++++++--------- 5 files changed, 46 insertions(+), 43 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index d26faf157..3152772f5 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -108,11 +108,7 @@ "power_dbm": 0, "power_range_db": [0,0,0.5], "roll_off": 0.15, - "OSNR": 11, - "bit_rate":100e9, - "tx_osnr": 45, - "min_spacing": 37.5e9, - "cost":1 + "tx_osnr": 45 }], "Transceiver":[ { diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index e79338ae0..39cf5c91e 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -74,14 +74,20 @@ def requests_from_json(json_data,equipment): # params['power'] is updated if req['path-constraints']['te-bandwidth']['output-power']: params['power'] = req['path-constraints']['te-bandwidth']['output-power'] + # same process for nb-channel - fmin = params['frequency']['min'] - fmax = params['frequency']['max'] + f_min = params['f_min'] + f_max_from_si = params['f_max'] if req['path-constraints']['te-bandwidth']['max-nb-of-channel'] is not None : - params['nb_channel'] = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + nch = req['path-constraints']['te-bandwidth']['max-nb-of-channel'] + params['nb_channel'] = nch + spacing = params['spacing'] + params['f_max'] = f_min + nch*spacing else : - params['nb_channel'] = automatic_nch(fmin,fmax,params['spacing']) - consitency_check(params) + params['nb_channel'] = automatic_nch(f_min,f_max_from_si,params['spacing']) + + consistency_check(params, f_max_from_si) + try : params['path_bandwidth'] = req['path-constraints']['te-bandwidth']['path_bandwidth'] except KeyError: @@ -89,11 +95,11 @@ def requests_from_json(json_data,equipment): requests_list.append(Path_request(**params)) return requests_list -def consitency_check(params): - fmin = params['frequency']['min'] - fmax = params['frequency']['max'] - max_recommanded_nb_channels = automatic_nch(fmin,fmax, - params['spacing']) +def consistency_check(params, f_max_from_si): + f_min = params['f_min'] + f_max = params['f_max'] + max_recommanded_nb_channels = automatic_nch(f_min,f_max, + params['spacing']) if params['baud_rate'] is not None: #implicitely means that a mode is defined with min_spacing if params['min_spacing']>params['spacing'] : @@ -103,10 +109,10 @@ def consitency_check(params): print(msg) logger.critical(msg) exit() - if params['nb_channel']>max_recommanded_nb_channels: + if f_max>f_max_from_si: msg = dedent(f''' Requested channel number {params["nb_channel"]}, baud rate {params["baud_rate"]} GHz and requested spacing {params["spacing"]*1e-9}GHz - is not consistent with frequency range {fmin*1e-12} THz, {fmax*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. + is not consistent with frequency range {f_min*1e-12} THz, {f_max*1e-12} THz, min recommanded spacing {params["min_spacing"]*1e-9}GHz. max recommanded nb of channels is {max_recommanded_nb_channels} Computation stopped.''') logger.critical(msg) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 06bbea555..1edce295f 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db OSNR bit_rate tx_osnr min_spacing cost') + power_dbm power_range_db tx_osnr') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -162,6 +162,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F try: trxs = equipment['Transceiver'] + #if called from path_requests_run.py, trx_mode is filled with None when not specified by user + #if called from transmission_main.py, trx_mode is '' if trx_mode is not None: mode_params = next(mode for trx in trxs \ if trx == trx_type_variety \ @@ -184,7 +186,8 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F "min_spacing":None, "cost":None} trx_params = {**mode_params} - trx_params['frequency'] = equipment['Transceiver'][trx_type_variety].frequency + trx_params['f_min'] = equipment['Transceiver'][trx_type_variety].frequency['min'] + trx_params['f_max'] = equipment['Transceiver'][trx_type_variety].frequency['max'] # TODO: novel automatic feature maybe unwanted if spacing is specified # trx_params['spacing'] = automatic_spacing(trx_params['baud_rate']) @@ -198,21 +201,18 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F else: # default transponder charcteristics # mainly used with transmission_main_example.py - trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max} + trx_params['f_min'] = default_si_data.f_min + trx_params['f_max'] = default_si_data.f_max trx_params['baud_rate'] = default_si_data.baud_rate trx_params['spacing'] = default_si_data.spacing - trx_params['OSNR'] = default_si_data.OSNR - trx_params['bit_rate'] = default_si_data.bit_rate - trx_params['cost'] = default_si_data.cost + trx_params['OSNR'] = None + trx_params['bit_rate'] = None + trx_params['cost'] = None trx_params['roll_off'] = default_si_data.roll_off - trx_params['nb_channel'] = automatic_nch(trx_params['frequency']['min'], - trx_params['frequency']['max'], - trx_params['spacing']) trx_params['tx_osnr'] = default_si_data.tx_osnr - trx_params['min_spacing'] = default_si_data.min_spacing - nch = automatic_nch(trx_params['frequency']['min'], - trx_params['frequency']['max'], - trx_params['spacing']) + trx_params['min_spacing'] = None + nch = automatic_nch(trx_params['f_min'], trx_params['f_max'], trx_params['spacing']) + trx_params['nb_channel'] = nch print(f'There are {nch} channels propagating') trx_params['power'] = db2lin(default_si_data.power_dbm)*1e-3 diff --git a/gnpy/core/info.py b/gnpy/core/info.py index 12596e7e6..c1da0cd28 100644 --- a/gnpy/core/info.py +++ b/gnpy/core/info.py @@ -14,6 +14,7 @@ from gnpy.core.utils import lin2db, db2lin from json import loads from gnpy.core.utils import load_json +from gnpy.core.equipment import automatic_nch, automatic_spacing class ConvenienceAccess: @@ -56,18 +57,17 @@ def merge_input_spectral_information(*si): #TODO pass -def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing, nb_channel, tx_osnr): +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) - ase_power = (power / db2lin(tx_osnr)) * (baud_rate / 12.5e9) si = SpectralInformation(pref=Pref(pref, pref)) + nb_channel = automatic_nch(f_min, f_max, spacing) si = si.update(carriers=[ Channel(f, (f_min+spacing*f), - baud_rate, roll_off, Power(power, 0, ase_power)) for f in range(1,nb_channel+1) + 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( diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 8739dd2c3..3e1a039b0 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -34,7 +34,7 @@ RequestParams = namedtuple('RequestParams','request_id source destination trx_type'+ -' trx_mode nodes_list loose_list spacing power nb_channel frequency format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth') +' trx_mode nodes_list loose_list spacing power nb_channel f_min f_max format baud_rate OSNR bit_rate roll_off tx_osnr min_spacing cost path_bandwidth') DisjunctionParams = namedtuple('DisjunctionParams','disjunction_id relaxable link_diverse node_diverse disjunctions_req') class Path_request: @@ -51,7 +51,8 @@ def __init__(self, *args, **params): self.spacing = params.spacing self.power = params.power self.nb_channel = params.nb_channel - self.frequency = params.frequency + self.f_min = params.f_min + self.f_max = params.f_max self.format = params.format self.OSNR = params.OSNR self.bit_rate = params.bit_rate @@ -388,8 +389,8 @@ def propagate(path, req, equipment, show=False): #update roadm loss in case of power sweep (power mode only) set_roadm_loss(path, equipment, lin2db(req.power*1e3)) si = create_input_spectral_information( - req.frequency['min'], req.roll_off, req.baud_rate, - req.power, req.spacing, req.nb_channel, req.tx_osnr) + req.f_min, req.f_max, req.roll_off, req.baud_rate, + req.power, req.spacing) for el in path: si = el(si) if show : @@ -400,11 +401,10 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): #update roadm loss in case of power sweep (power mode only) set_roadm_loss(path, equipment, lin2db(req.power*1e3)) # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the - # spacing. TODO add a min_spacing attribute in transceivers. for now just using baudrate*1.1 # 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 min_spacing very close to req spacing eg 50.001 50.000 + # 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 @@ -420,8 +420,8 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): # TODO : if the loop in mode optimization does not have a feasible path, then bugs for m in modes_to_explore : si = create_input_spectral_information( - req.frequency['min'], equipment['SI']['default'].roll_off, - b, req.power, req.spacing, req.nb_channel, m['tx_osnr']) + 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 : @@ -869,7 +869,8 @@ def compare_reqs(req1,req2,disjlist) : req1.spacing == req2.spacing and \ req1.power == req2.power and \ req1.nb_channel == req2.nb_channel and \ - req1.frequency == req2.frequency and \ + req1.f_min == req2.f_min and \ + req1.f_max == req2.f_max and \ req1.format == req2.format and \ req1.OSNR == req2.OSNR and \ req1.roll_off == req2.roll_off and \ From 46d25df241a2cba0ffaf9de5b31b4ae4c3a10646 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 16 Jan 2019 15:56:21 +0100 Subject: [PATCH 099/108] add Tx_osnr at the end of the transmission Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 17 ++++++++++++++--- gnpy/core/request.py | 2 ++ gnpy/core/utils.py | 4 ++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 8dd364cfc..0cb41b2f0 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -25,7 +25,7 @@ from gnpy.core.node import Node from gnpy.core.units import UNITS -from gnpy.core.utils import lin2db, db2lin, itufs +from gnpy.core.utils import lin2db, db2lin, itufs, snr_sum class Transceiver(Node): def __init__(self, *args, **kwargs): @@ -35,19 +35,30 @@ def __init__(self, *args, **kwargs): self.osnr_nli = None self.snr = None self.passive = False + self.baud_rate = None def _calc_snr(self, spectral_info): with errstate(divide='ignore'): + self.baud_rate = [c.baud_rate for c in spectral_info.carriers] self.osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) for c in spectral_info.carriers] - ratio_01nm = [lin2db(12.5e9/c.baud_rate) - for c in spectral_info.carriers] + ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate] self.osnr_ase_01nm = [ase - ratio for ase, ratio in zip(self.osnr_ase, ratio_01nm)] self.osnr_nli = [lin2db(divide(c.power.signal, c.power.nli)) for c in spectral_info.carriers] self.snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase)) for c in spectral_info.carriers] + + def update_snr(self, snr_added, bw_added=12.5e9): + self.osnr_ase = self.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), + self.osnr_ase, self.baud_rate)) + self.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), + self.osnr_nli, self.baud_rate)) + self.snr = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), + self.snr, self.baud_rate)) + self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added,bw_added), + self.osnr_ase_01nm)) @property def to_json(self): diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 3e1a039b0..c88c93900 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -395,6 +395,7 @@ def propagate(path, req, equipment, show=False): si = el(si) if show : print(el) + path[-1].update_snr(req.tx_osnr) return path def propagate_and_optimize_mode(path, req, equipment, show=False): @@ -427,6 +428,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if show : print(el) if path[-1].snr is not None: + path[-1].update_snr(m['tx_osnr']) if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : found_a_feasible_mode = True return path, m diff --git a/gnpy/core/utils.py b/gnpy/core/utils.py index 44c4c0999..6225e0dc0 100644 --- a/gnpy/core/utils.py +++ b/gnpy/core/utils.py @@ -120,6 +120,10 @@ def freq2wavelength(value): """ return c() / value +def snr_sum(snr, bw, snr_added, bw_added=12.5e9): + snr_added = snr_added - lin2db(bw/bw_added) + snr = -lin2db(db2lin(-snr)+db2lin(-snr_added)) + return snr def deltawl2deltaf(delta_wl, wavelength): """ deltawl2deltaf(delta_wl, wavelength): From 8635a7c182bc966dacd83fd19397d068d4267a97 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 17 Jan 2019 15:49:05 +0100 Subject: [PATCH 100/108] Code fix: non cumulative osnr penalties add Tx_osnr or add_drop_osnr on raw snr values to avoid cumulated adding Signed-off-by: Jean-Luc Auge bug fix: forgot to add add_drop_osnr with tx_osnr Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 48 +++++++++++++++++++++++++++++-------------- gnpy/core/request.py | 4 ++-- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 0cb41b2f0..82f66f4a4 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -40,25 +40,43 @@ def __init__(self, *args, **kwargs): def _calc_snr(self, spectral_info): with errstate(divide='ignore'): self.baud_rate = [c.baud_rate for c in spectral_info.carriers] - self.osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) - for c in spectral_info.carriers] ratio_01nm = [lin2db(12.5e9/b_rate) for b_rate in self.baud_rate] - self.osnr_ase_01nm = [ase - ratio for ase, ratio - in zip(self.osnr_ase, ratio_01nm)] - self.osnr_nli = [lin2db(divide(c.power.signal, c.power.nli)) + + #set raw values to record original calculation, before update_snr() + self.raw_osnr_ase = [lin2db(divide(c.power.signal, c.power.ase)) + for c in spectral_info.carriers] + self.raw_osnr_ase_01nm = [ase - ratio for ase, ratio + in zip(self.raw_osnr_ase, ratio_01nm)] + self.raw_osnr_nli = [lin2db(divide(c.power.signal, c.power.nli)) for c in spectral_info.carriers] - self.snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase)) + self.raw_snr = [lin2db(divide(c.power.signal, c.power.nli+c.power.ase)) for c in spectral_info.carriers] + + self.osnr_ase = self.raw_osnr_ase + self.osnr_ase_01nm = self.raw_osnr_ase_01nm + self.osnr_nli = self.raw_osnr_nli + self.snr = self.raw_snr - def update_snr(self, snr_added, bw_added=12.5e9): - self.osnr_ase = self.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), - self.osnr_ase, self.baud_rate)) - self.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), - self.osnr_nli, self.baud_rate)) - self.snr = list(map(lambda x,y:snr_sum(x,y,snr_added,bw_added), - self.snr, self.baud_rate)) - self.osnr_ase_01nm = list(map(lambda x:snr_sum(x,12.5e9,snr_added,bw_added), - self.osnr_ase_01nm)) + 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.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added), + self.raw_osnr_nli, 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)) @property def to_json(self): diff --git a/gnpy/core/request.py b/gnpy/core/request.py index c88c93900..4789058eb 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -395,7 +395,7 @@ def propagate(path, req, equipment, show=False): si = el(si) if show : print(el) - path[-1].update_snr(req.tx_osnr) + path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr) return path def propagate_and_optimize_mode(path, req, equipment, show=False): @@ -428,7 +428,7 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): if show : print(el) if path[-1].snr is not None: - path[-1].update_snr(m['tx_osnr']) + path[-1].update_snr(m['tx_osnr'], equipment['Roadms']['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 From b25a298087ec70ac3657e1b394639df6af56bbda Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 16 Jan 2019 16:11:45 +0100 Subject: [PATCH 101/108] NEW add_drop OSNR in Roadms model add_drop_osnr contribution is added at the end of the transmission propagation, at the same time as Tx_osnr Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 17 +++++++------- examples/meshTopologyExampleV2.json | 36 ++++++++++++++--------------- gnpy/core/equipment.py | 2 +- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 3152772f5..50e732035 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -98,7 +98,8 @@ ], "Roadms":[{ "gain_mode_default_loss": 20, - "power_mode_pout_target": -20 + "power_mode_pout_target": -20, + "add_drop_osnr": 38 }], "SI":[{ "f_min": 191.3e12, @@ -108,7 +109,7 @@ "power_dbm": 0, "power_range_db": [0,0,0.5], "roll_off": 0.15, - "tx_osnr": 45 + "tx_osnr": 40 }], "Transceiver":[ { @@ -125,7 +126,7 @@ "OSNR": 11, "bit_rate": 100e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 37.5e9, "cost":1 }, @@ -135,7 +136,7 @@ "OSNR": 15, "bit_rate": 200e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 75e9, "cost":1 } @@ -154,7 +155,7 @@ "OSNR": 12, "bit_rate": 100e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 37.5e9, "cost":1 }, @@ -164,7 +165,7 @@ "OSNR": 18, "bit_rate": 300e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 62.5e9, "cost":1 }, @@ -174,7 +175,7 @@ "OSNR": 21, "bit_rate": 400e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 75e9, "cost":1 }, @@ -184,7 +185,7 @@ "OSNR": 16, "bit_rate": 200e9, "roll_off": 0.15, - "tx_osnr": 45, + "tx_osnr": 40, "min_spacing": 75e9, "cost":1 } diff --git a/examples/meshTopologyExampleV2.json b/examples/meshTopologyExampleV2.json index 107b54ff1..5556574cd 100644 --- a/examples/meshTopologyExampleV2.json +++ b/examples/meshTopologyExampleV2.json @@ -121,7 +121,7 @@ "type": "Roadm" }, { - "uid": "ingress fused spans in Corlay", + "uid": "west fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -133,7 +133,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Loudeac", + "uid": "west fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -145,7 +145,7 @@ "type": "Fused" }, { - "uid": "ingress fused spans in Morlaix", + "uid": "west fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -157,7 +157,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Corlay", + "uid": "east fused spans in Corlay", "metadata": { "location": { "city": "Corlay", @@ -169,7 +169,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Loudeac", + "uid": "east fused spans in Loudeac", "metadata": { "location": { "city": "Loudeac", @@ -181,7 +181,7 @@ "type": "Fused" }, { - "uid": "egress fused spans in Morlaix", + "uid": "east fused spans in Morlaix", "metadata": { "location": { "city": "Morlaix", @@ -652,34 +652,34 @@ }, { "from_node": "fiber (Lannion_CAS → Corlay)-F061", - "to_node": "ingress fused spans in Corlay" + "to_node": "west fused spans in Corlay" }, { - "from_node": "ingress fused spans in Corlay", + "from_node": "west fused spans in Corlay", "to_node": "fiber (Corlay → Loudeac)-F010" }, { "from_node": "fiber (Loudeac → Corlay)-F010", - "to_node": "egress fused spans in Corlay" + "to_node": "east fused spans in Corlay" }, { - "from_node": "egress fused spans in Corlay", + "from_node": "east fused spans in Corlay", "to_node": "fiber (Corlay → Lannion_CAS)-F061" }, { "from_node": "fiber (Corlay → Loudeac)-F010", - "to_node": "ingress fused spans in Loudeac" + "to_node": "west fused spans in Loudeac" }, { - "from_node": "ingress fused spans in Loudeac", + "from_node": "west fused spans in Loudeac", "to_node": "fiber (Loudeac → Lorient_KMA)-F054" }, { "from_node": "fiber (Lorient_KMA → Loudeac)-F054", - "to_node": "egress fused spans in Loudeac" + "to_node": "east fused spans in Loudeac" }, { - "from_node": "egress fused spans in Loudeac", + "from_node": "east fused spans in Loudeac", "to_node": "fiber (Loudeac → Corlay)-F010" }, { @@ -748,18 +748,18 @@ }, { "from_node": "fiber (Lannion_CAS → Morlaix)-F059", - "to_node": "ingress fused spans in Morlaix" + "to_node": "west fused spans in Morlaix" }, { - "from_node": "ingress fused spans in Morlaix", + "from_node": "west fused spans in Morlaix", "to_node": "fiber (Morlaix → Brest_KLA)-F060" }, { "from_node": "fiber (Brest_KLA → Morlaix)-F060", - "to_node": "egress fused spans in Morlaix" + "to_node": "east fused spans in Morlaix" }, { - "from_node": "egress fused spans in Morlaix", + "from_node": "east fused spans in Morlaix", "to_node": "fiber (Morlaix → Lannion_CAS)-F059" }, { diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 1edce295f..850128c69 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -25,7 +25,7 @@ Spans = namedtuple('Spans', 'power_mode delta_power_range_db max_length length_units \ max_loss padding EOL con_in con_out') Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') -Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target') +Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ power_dbm power_range_db tx_osnr') AmpBase = namedtuple( From 2dd017bddc536179167842c36e03662b2326911a Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Thu, 17 Jan 2019 15:58:59 +0100 Subject: [PATCH 102/108] code speed improvement (-30% computation time) remove path propagation for each mode path propogation is only done for each baud rate Signed-off-by: Jean-Luc Auge --- examples/path_requests_run.py | 2 +- gnpy/core/request.py | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/examples/path_requests_run.py b/examples/path_requests_run.py index 39cf5c91e..fd13dd7e8 100755 --- a/examples/path_requests_run.py +++ b/examples/path_requests_run.py @@ -212,7 +212,7 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist): logger.warning(msg) total_path = [] else: - total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) + 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 : diff --git a/gnpy/core/request.py b/gnpy/core/request.py index 4789058eb..16aa69ffe 100644 --- a/gnpy/core/request.py +++ b/gnpy/core/request.py @@ -398,7 +398,7 @@ def propagate(path, req, equipment, show=False): path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr) return path -def propagate_and_optimize_mode(path, req, equipment, show=False): +def propagate_and_optimize_mode(path, req, equipment): #update roadm loss in case of power sweep (power mode only) set_roadm_loss(path, equipment, lin2db(req.power*1e3)) # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the @@ -419,14 +419,12 @@ def propagate_and_optimize_mode(path, req, equipment, show=False): 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) for m in modes_to_explore : - 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) if path[-1].snr is not None: path[-1].update_snr(m['tx_osnr'], equipment['Roadms']['default'].add_drop_osnr) if round(min(path[-1].snr+lin2db(b/(12.5e9))),2) > m['OSNR'] : From 30234f913cd9ab75083f0a1ad76152423cef0051 Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Mon, 21 Jan 2019 14:48:24 +0100 Subject: [PATCH 103/108] system margins implementation -read sys_margins in equipment['SI'] -add sys_margins to all Transceivers OSNR for path feasibility check (path_request_run only) Signed-off-by: Jean-Luc Auge --- examples/eqpt_config.json | 3 ++- gnpy/core/equipment.py | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/examples/eqpt_config.json b/examples/eqpt_config.json index 50e732035..57abcc17b 100644 --- a/examples/eqpt_config.json +++ b/examples/eqpt_config.json @@ -109,7 +109,8 @@ "power_dbm": 0, "power_range_db": [0,0,0.5], "roll_off": 0.15, - "tx_osnr": 40 + "tx_osnr": 40, + "sys_margins": 0 }], "Transceiver":[ { diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index 850128c69..b542da72b 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -27,7 +27,7 @@ Transceiver = namedtuple('Transceiver', 'type_variety frequency mode') Roadms = namedtuple('Roadms', 'gain_mode_default_loss power_mode_pout_target add_drop_osnr') SI = namedtuple('SI', 'f_min f_max baud_rate spacing roll_off \ - power_dbm power_range_db tx_osnr') + power_dbm power_range_db tx_osnr sys_margins') AmpBase = namedtuple( 'AmpBase', 'type_variety type_def gain_flatmax gain_min p_max' @@ -233,6 +233,13 @@ def load_equipment(filename): json_data = load_json(filename) return equipment_from_json(json_data, filename) +def update_trx_osnr(equipment): + """add sys_margins to all Transceivers OSNR values""" + for trx in equipment['Transceiver'].values(): + for m in trx.mode: + m['OSNR'] = m['OSNR'] + equipment['SI']['default'].sys_margins + return equipment + def equipment_from_json(json_data, filename): """build global dictionnary eqpt_library that stores all eqpt characteristics: edfa type type_variety, fiber type_variety @@ -257,4 +264,5 @@ def equipment_from_json(json_data, filename): equipment[key][subkey] = Amp.from_default_json(config, **entry) else: equipment[key][subkey] = typ(**entry) + equipment = update_trx_osnr(equipment) return equipment From e23fef3f64c7149005ddd773e38de7728fe930bb Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Tue, 22 Jan 2019 19:12:02 +0100 Subject: [PATCH 104/108] update and pass tests Signed-off-by: Jean-Luc Auge --- gnpy/core/equipment.py | 3 +++ tests/data/eqpt_config.json | 8 +++----- tests/test_amplifier.py | 6 ++++-- tests/test_automaticmodefeature.py | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gnpy/core/equipment.py b/gnpy/core/equipment.py index b542da72b..dcf910d7c 100644 --- a/gnpy/core/equipment.py +++ b/gnpy/core/equipment.py @@ -229,6 +229,9 @@ def automatic_spacing(baud_rate): def automatic_nch(f_min, f_max, spacing): return int((f_max - f_min)//spacing) +def automatic_fmax(f_min, spacing, nch): + return f_min + spacing * nch + def load_equipment(filename): json_data = load_json(filename) return equipment_from_json(json_data, filename) diff --git a/tests/data/eqpt_config.json b/tests/data/eqpt_config.json index 137ed2279..0877d6d2a 100644 --- a/tests/data/eqpt_config.json +++ b/tests/data/eqpt_config.json @@ -66,7 +66,8 @@ ], "Roadms":[{ "gain_mode_default_loss": 20, - "power_mode_pout_target": -20 + "power_mode_pout_target": -20, + "add_drop_osnr": 100 }], "SI":[{ "f_min": 191.3e12, @@ -76,11 +77,8 @@ "power_dbm": 0, "power_range_db": [0,0.5,0.5], "roll_off": 0.15, - "OSNR": 15, - "bit_rate":100e9, "tx_osnr": 100, - "min_spacing": 50e9, - "cost":1 + "sys_margins": 0 }], "Transceiver":[ { diff --git a/tests/test_amplifier.py b/tests/test_amplifier.py index 22e60b090..dc0dac4f1 100644 --- a/tests/test_amplifier.py +++ b/tests/test_amplifier.py @@ -9,7 +9,7 @@ from gnpy.core.elements import Transceiver, Fiber, Edfa from gnpy.core.utils import lin2db, db2lin from gnpy.core.info import create_input_spectral_information, SpectralInformation, Channel, Power, Pref -from gnpy.core.equipment import load_equipment +from gnpy.core.equipment import load_equipment, automatic_fmax from gnpy.core.network import build_network, load_network, set_roadm_loss from pathlib import Path import pytest @@ -66,7 +66,9 @@ def setup_trx(): def si(nch_and_spacing, bw): """parametrize a channel comb with nb_channel, spacing and signal bw""" nb_channel, spacing = nch_and_spacing - return create_input_spectral_information(191.3e12, 0.15, bw, 1e-3, spacing, nb_channel, 100) + f_min = 191.3e12 + f_max = automatic_fmax(f_min, spacing, nb_channel) + return create_input_spectral_information(f_min, f_max, 0.15, bw, 1e-3, spacing) @pytest.mark.parametrize("gain, nf_expected", [(10, 15), (15, 10), (25, 5.8)]) def test_variable_gain_nf(gain, nf_expected, setup_edfa_variable_gain, si): diff --git a/tests/test_automaticmodefeature.py b/tests/test_automaticmodefeature.py index d5a2ec7ad..6f6fb9c62 100644 --- a/tests/test_automaticmodefeature.py +++ b/tests/test_automaticmodefeature.py @@ -74,7 +74,7 @@ def test_automaticmodefeature(net,eqpt,serv,expected_mode): path_res_list.append(pathreq.format) total_path = propagate(total_path,pathreq,equipment, show=False) else: - total_path,mode = propagate_and_optimize_mode(total_path,pathreq,equipment, show=False) + 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 : From f65059dd7fffaaf2d554330b01c8edd57e500f7d Mon Sep 17 00:00:00 2001 From: jonas Date: Wed, 23 Jan 2019 14:16:43 +0100 Subject: [PATCH 105/108] correct README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 5279f43c4..ed9c9be61 100644 --- a/README.rst +++ b/README.rst @@ -152,7 +152,7 @@ further instructions on how to prepare the Excel input file, see The main transmission example will calculate the average signal OSNR and SNR across network elements (transceiver, ROADMs, fibers, and amplifiers) between two transceivers selected by the user. Additional details are provided by doing ``transmission_main_example.py -h``. (By default, for the CORONET Global -network, it will show the transmission of spectral information between Albuquerque and Atlanta (Georgia)) +network, it will show the transmission of spectral information between Abilene and Albany) This script calculates the average signal OSNR = |OSNR| and SNR = |SNR|. From 76c8296a5dbeb1e84704b8832d2cbf5a5141fabf Mon Sep 17 00:00:00 2001 From: Jean-Luc Auge Date: Wed, 23 Jan 2019 14:43:59 +0100 Subject: [PATCH 106/108] remove osnr_nli update when adding add_drop_osnr or tx_osnr Signed-off-by: Jean-Luc Auge --- gnpy/core/elements.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gnpy/core/elements.py b/gnpy/core/elements.py index 82f66f4a4..9cc50601b 100644 --- a/gnpy/core/elements.py +++ b/gnpy/core/elements.py @@ -71,8 +71,6 @@ def update_snr(self, *args): 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.osnr_nli = list(map(lambda x,y:snr_sum(x,y,snr_added), - self.raw_osnr_nli, 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), From f6041cd8444667cc2cc488a86cccfea6b7493bb3 Mon Sep 17 00:00:00 2001 From: Gert Grammel Date: Wed, 30 Jan 2019 19:35:42 +0000 Subject: [PATCH 107/108] Update Contributors Adding Contributors to the list --- docs/index.rst | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index d41d173a5..14e9292dd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,6 +43,10 @@ Contributors in alphabetical order +==========+============+=======================+======================================+ | Alessio | Ferrari | Politecnico di Torino | alessio.ferrari@polito.it | +----------+------------+-----------------------+--------------------------------------+ +| Anders | Lindgren | Telia Company | Anders.X.Lindgren@teliacompany.com | ++----------+------------+-----------------------+--------------------------------------+ +| Andrea | d'Amico | Politecnico di Torino | andrea.damico@polito.it | ++----------+------------+-----------------------+--------------------------------------+ | Brian | Taylor | Facebook | briantaylor@fb.com | +----------+------------+-----------------------+--------------------------------------+ | David | Boertjes | Ciena | dboertje@ciena.com | @@ -59,12 +63,19 @@ Contributors in alphabetical order +----------+------------+-----------------------+--------------------------------------+ | Jeanluc | Auge | Orange | jeanluc.auge@orange.com | +----------+------------+-----------------------+--------------------------------------+ +| Jonas | Martensson | RISE Research Sweden | jonas.martensson@ri.se | ++----------+------------+-----------------------+--------------------------------------+ | Mattia | Cantono | Politecnico di Torino | mattia.cantono@polito.it | +----------+------------+-----------------------+--------------------------------------+ +| Miguel | Garrich | University Catalunya | miquel.garrich@upct.es | ++----------+------------+-----------------------+--------------------------------------+ +| Stefan | Melin | Telia Company | Stefan.Melin@teliacompany.com | ++----------+------------+-----------------------+--------------------------------------+ +| Raj | Nagarajan | Lumentum | raj.nagarajan@lumentum.com | ++----------+------------+-----------------------+--------------------------------------+ | Vittorio | Curri | Politecnico di Torino | vittorio.curri@polito.it | +----------+------------+-----------------------+--------------------------------------+ -PSE WG Charter -------------- - Goal is to build an end-to-end simulation environment which defines the From dec15f67972ee27c6b48061b036b2357c63732a6 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 30 Jan 2019 15:40:38 -0500 Subject: [PATCH 108/108] Update README.rst --- README.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index ed9c9be61..ddb683751 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,11 @@ A brief outline of major (tagged) `gnpy` releases: +---------------+-------------+-----------------------------------------------+ | release date | version tag | notes | +===============+=============+===============================================+ -| Oct 16, 2018 | v1.0 | - first "production"-ready release | +| Jan 30, 2019 | v1.1 | - XLS parser enhancements | +| | | - carrier probe feature | +| | | - bug fixes | ++---------------+-------------+-----------------------------------------------+ +| Oct 16, 2018 | v1.0 | - first "production"-ready release | | | | - open network element model (EDFA, GN-model) | | | | - auto-design functionality | | | | - path request functionality | @@ -588,4 +592,4 @@ License ``gnpy`` is distributed under a standard BSD 3-Clause License. -See `LICENSE `__ for more details. \ No newline at end of file +See `LICENSE `__ for more details.