mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2026-01-27 02:21:40 +00:00
Add more information on output
- min required OSNR - min propagated GSNR - PDL, PMD, CD penalties This enables user to diagnose the case whan GSNR is OK but path is failing change the test_parser: - keep the testTopology_response.json file as it is as input file for test_csv_response_generation so that previous json exports are still tested. - use a new testTopology_response_expected.json to check the actual json generation with the additionnal informations - add a 'fake response.json file with various types of response to better test the jsontocsv function Add more info in the logger for the case of no_ feasible_mode and add a test for this case. refactor a bit the functions Signed-off-by: EstherLerouzic <esther.lerouzic@orange.com> Change-Id: I92105f58adb7303f3d1475e4b21bd3e36e227090
This commit is contained in:
@@ -29,14 +29,14 @@ from networkx.utils import pairwise
|
||||
from numpy import mean, argmin
|
||||
|
||||
from gnpy.core.elements import Transceiver, Roadm, Edfa, Multiband_amplifier
|
||||
from gnpy.core.utils import lin2db, unique_ordered, find_common_range
|
||||
from gnpy.core.utils import lin2db, unique_ordered, find_common_range, watt2dbm
|
||||
from gnpy.core.info import create_input_spectral_information, carriers_to_spectral_information, \
|
||||
demuxed_spectral_information, muxed_spectral_information, SpectralInformation
|
||||
from gnpy.core import network as network_module
|
||||
from gnpy.core.exceptions import ServiceError, DisjunctionError
|
||||
from copy import deepcopy
|
||||
from csv import writer
|
||||
from math import ceil
|
||||
from math import ceil, isinf
|
||||
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
@@ -229,7 +229,7 @@ class ResultElement:
|
||||
},
|
||||
{
|
||||
'metric-type': 'SNR-0.1nm',
|
||||
'accumulative-value': round(mean(pth[-1].snr + lin2db(req.baud_rate / 12.5e9)), 2)
|
||||
'accumulative-value': round(mean(pth[-1].snr_01nm), 2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'OSNR-bandwidth',
|
||||
@@ -239,6 +239,22 @@ class ResultElement:
|
||||
'metric-type': 'OSNR-0.1nm',
|
||||
'accumulative-value': round(mean(pth[-1].osnr_ase_01nm), 2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'lowest_SNR-0.1nm',
|
||||
'accumulative-value': round(min(pth[-1].snr_01nm), 2)
|
||||
},
|
||||
{
|
||||
'metric-type': 'PDL_penalty',
|
||||
'accumulative-value': get_penalty_from_receiver(pth[-1], 'pdl')
|
||||
},
|
||||
{
|
||||
'metric-type': 'CD_penalty',
|
||||
'accumulative-value': get_penalty_from_receiver(pth[-1], 'chromatic_dispersion')
|
||||
},
|
||||
{
|
||||
'metric-type': 'PMD_penalty',
|
||||
'accumulative-value': get_penalty_from_receiver(pth[-1], 'pmd')
|
||||
},
|
||||
{
|
||||
'metric-type': 'reference_power',
|
||||
'accumulative-value': req.power
|
||||
@@ -246,8 +262,7 @@ class ResultElement:
|
||||
{
|
||||
'metric-type': 'path_bandwidth',
|
||||
'accumulative-value': req.path_bandwidth
|
||||
}
|
||||
]
|
||||
}]
|
||||
if self.path_request.bidir:
|
||||
path_properties = {
|
||||
'path-metric': path_metric(self.computed_path, self.path_request),
|
||||
@@ -448,8 +463,13 @@ def propagate_and_optimize_mode(path, req, equipment):
|
||||
# only get to this point if no baudrate/mode satisfies OSNR requirement
|
||||
|
||||
# returns the last propagated path and mode
|
||||
msg = f'\tWarning! Request {req.request_id}: no mode satisfies path SNR requirement.\n'
|
||||
LOGGER.warning(msg)
|
||||
snr01nm_with_penalty = path[-1].snr_01nm - path[-1].total_penalty
|
||||
min_ind = argmin(snr01nm_with_penalty)
|
||||
msg = f'\tWarning! Request {req.request_id} computed path from' \
|
||||
+ f' {req.source} to {req.destination}: no mode satisfies path SNR requirement.' \
|
||||
+ f' Best propagated mode {last_explored_mode["format"]}'
|
||||
msg = penalty_msg(path[-1], msg, min_ind, last_explored_mode["OSNR"], equipment["SI"]["default"].sys_margins)
|
||||
LOGGER.info(msg)
|
||||
req.blocking_reason = 'NO_FEASIBLE_MODE'
|
||||
return path, last_explored_mode
|
||||
else:
|
||||
@@ -460,26 +480,34 @@ def propagate_and_optimize_mode(path, req, equipment):
|
||||
return [], None
|
||||
|
||||
|
||||
def read_property(path_metric, metric):
|
||||
"""Reads property if it exists
|
||||
"""
|
||||
try:
|
||||
return next(e['accumulative-value'] for e in path_metric if e['metric-type'] == metric)
|
||||
except StopIteration:
|
||||
return ''
|
||||
|
||||
|
||||
def jsontopath_metric(path_metric):
|
||||
"""a functions that reads resulting metric from json string"""
|
||||
output_snr = next(e['accumulative-value']
|
||||
for e in path_metric if e['metric-type'] == 'SNR-0.1nm')
|
||||
output_snrbandwidth = next(e['accumulative-value']
|
||||
for e in path_metric if e['metric-type'] == 'SNR-bandwidth')
|
||||
output_osnr = next(e['accumulative-value']
|
||||
for e in path_metric if e['metric-type'] == 'OSNR-0.1nm')
|
||||
# ouput osnr@bandwidth is not used
|
||||
# output_osnrbandwidth = next(e['accumulative-value']
|
||||
# for e in path_metric if e['metric-type'] == 'OSNR-bandwidth')
|
||||
power = next(e['accumulative-value']
|
||||
for e in path_metric if e['metric-type'] == 'reference_power')
|
||||
path_bandwidth = next(e['accumulative-value']
|
||||
for e in path_metric if e['metric-type'] == 'path_bandwidth')
|
||||
return output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth
|
||||
""" a functions that reads resulting metric from json string
|
||||
"""
|
||||
output_snr = read_property(path_metric, 'SNR-0.1nm')
|
||||
output_snrbandwidth = read_property(path_metric, 'SNR-bandwidth')
|
||||
output_osnr = read_property(path_metric, 'OSNR-0.1nm')
|
||||
power = read_property(path_metric, 'reference_power')
|
||||
path_bandwidth = read_property(path_metric, 'path_bandwidth')
|
||||
output_snr_min = read_property(path_metric, 'lowest_SNR-0.1nm')
|
||||
pdl = read_property(path_metric, 'PDL_penalty')
|
||||
cd = read_property(path_metric, 'CD_penalty')
|
||||
pmd = read_property(path_metric, 'PMD_penalty')
|
||||
return output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth, output_snr_min, pdl, cd, pmd
|
||||
|
||||
|
||||
def jsontoparams(my_p, tsp, mode, equipment):
|
||||
"""a function that derives optical params from transponder type and mode supports the no mode case"""
|
||||
""" a function that derives optical params from transponder type and mode
|
||||
supports the no-mode case
|
||||
"""
|
||||
temp = []
|
||||
for elem in my_p['path-properties']['path-route-objects']:
|
||||
if 'num-unnum-hop' in elem['path-route-object']:
|
||||
@@ -506,11 +534,11 @@ def jsontoparams(my_p, tsp, mode, equipment):
|
||||
for m in equipment['Transceiver'][tsp].mode if m['format'] == mode)
|
||||
else:
|
||||
[minosnr, baud_rate, bit_rate, cost] = ['', '', '', '']
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth = \
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth, output_snr_min, pdl, cd, pmd = \
|
||||
jsontopath_metric(my_p['path-properties']['path-metric'])
|
||||
|
||||
return pth, minosnr, baud_rate, bit_rate, cost, output_snr, \
|
||||
output_snrbandwidth, output_osnr, power, path_bandwidth, sptrm
|
||||
output_snrbandwidth, output_osnr, power, path_bandwidth, sptrm, output_snr_min, pdl, cd, pmd
|
||||
|
||||
|
||||
def jsontocsv(json_data, equipment, fileout):
|
||||
@@ -521,10 +549,14 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
"""
|
||||
mywriter = writer(fileout)
|
||||
mywriter.writerow(('response-id', 'source', 'destination', 'path_bandwidth', 'Pass?',
|
||||
'nb of tsp pairs', 'total cost', 'transponder-type', 'transponder-mode',
|
||||
'OSNR-0.1nm', 'SNR-0.1nm', 'SNR-bandwidth', 'baud rate (Gbaud)',
|
||||
'input power (dBm)', 'path', 'spectrum (N,M)', 'reversed path OSNR-0.1nm',
|
||||
'reversed path SNR-0.1nm', 'reversed path SNR-bandwidth'))
|
||||
'nb of tsp pairs', 'total cost', 'transponder-type', 'transponder-mode', 'bit rate',
|
||||
'OSNR-0.1nm (average)', 'SNR-0.1nm (average)', 'SNR-bandwidth (average)',
|
||||
'SNR-0.1nm (min)', 'PDL_penalty', 'CD_penalty', 'PMD_penalty',
|
||||
'min required OSNR (inc. margin)', 'baud rate (Gbaud)',
|
||||
'input power (dBm)', 'path', 'spectrum (N,M)', 'reversed path OSNR-0.1nm (average)',
|
||||
'reversed path SNR-0.1nm (average)', 'reversed path SNR-bandwidth (average)',
|
||||
'reversed path SNR-0.1nm (min)', 'reversed path PDL_penalty', 'reversed path CD_penalty',
|
||||
'reversed path PMD_penalty',))
|
||||
|
||||
for pth_el in json_data['response']:
|
||||
path_id = pth_el['response-id']
|
||||
@@ -539,15 +571,25 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
isok = pth_el['no-path']['no-path']
|
||||
tsp = ''
|
||||
mode = ''
|
||||
bitr = ''
|
||||
rosnr = ''
|
||||
rsnr = ''
|
||||
rsnrb = ''
|
||||
rsnr_min = ''
|
||||
pdl = ''
|
||||
cd = ''
|
||||
pmd = ''
|
||||
minosnr_inc_margin = ''
|
||||
brate = ''
|
||||
pwr = ''
|
||||
pth = ''
|
||||
revosnr = ''
|
||||
revsnr = ''
|
||||
revsnrb = ''
|
||||
revsnr_min = ''
|
||||
revpdl = ''
|
||||
revcd = ''
|
||||
revpmd = ''
|
||||
else:
|
||||
# the objects are listed with this order:
|
||||
# - id of hop
|
||||
@@ -565,17 +607,19 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
if pth_el['no-path']['no-path'] in BLOCKING_NOMODE or \
|
||||
pth_el['no-path']['no-path'] in BLOCKING_NOSPECTRUM:
|
||||
pth, minosnr, baud_rate, bit_rate, cost, output_snr, output_snrbandwidth, \
|
||||
output_osnr, power, path_bandwidth, sptrm = \
|
||||
output_osnr, power, path_bandwidth, sptrm, rsnr_min, pdl, cd, pmd = \
|
||||
jsontoparams(pth_el['no-path'], tsp, mode, equipment)
|
||||
minosnr_inc_margin = minosnr + equipment['SI']['default'].sys_margins
|
||||
pthbdbw = ''
|
||||
rosnr = round(output_osnr, 2)
|
||||
rsnr = round(output_snr, 2)
|
||||
rsnrb = round(output_snrbandwidth, 2)
|
||||
brate = round(baud_rate * 1e-9, 2)
|
||||
pwr = round(lin2db(power) + 30, 2)
|
||||
pwr = round(watt2dbm(power), 2)
|
||||
bitr = round(bit_rate * 1e-9, 2)
|
||||
if 'z-a-path-metric' in pth_el['no-path']['path-properties'].keys():
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth = \
|
||||
jsontopath_metric(pth_el['no-path']['path-properties']['z-a-path-metric'])
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth, revsnr_min, revpdl, \
|
||||
revcd, revpmd = jsontopath_metric(pth_el['no-path']['path-properties']['z-a-path-metric'])
|
||||
revosnr = round(output_osnr, 2)
|
||||
revsnr = round(output_snr, 2)
|
||||
revsnrb = round(output_snrbandwidth, 2)
|
||||
@@ -583,6 +627,10 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
revosnr = ''
|
||||
revsnr = ''
|
||||
revsnrb = ''
|
||||
revsnr_min = ''
|
||||
revpdl = ''
|
||||
revcd = ''
|
||||
revpmd = ''
|
||||
else:
|
||||
# when label will be assigned destination will be with index -3, and transponder with index 2
|
||||
source = pth_el['path-properties']['path-route-objects'][0]['path-route-object']['num-unnum-hop']['node-id']
|
||||
@@ -596,21 +644,23 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
# on tsp (type) and mode (format).
|
||||
# loading equipment already tests the existence of tsp type and mode:
|
||||
pth, minosnr, baud_rate, bit_rate, cost, output_snr, output_snrbandwidth, \
|
||||
output_osnr, power, path_bandwidth, sptrm = \
|
||||
output_osnr, power, path_bandwidth, sptrm, rsnr_min, pdl, cd, pmd = \
|
||||
jsontoparams(pth_el, tsp, mode, equipment)
|
||||
# this part only works if the request has a blocking_reason atribute, ie if it could not be satisfied
|
||||
isok = output_snr >= minosnr
|
||||
minosnr_inc_margin = minosnr + equipment['SI']['default'].sys_margins
|
||||
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)
|
||||
brate = round(baud_rate * 1e-9, 2)
|
||||
pwr = round(lin2db(power) + 30, 2)
|
||||
pwr = round(watt2dbm(power), 2)
|
||||
bitr = round(bit_rate * 1e-9, 2)
|
||||
total_cost = nb_tsp * cost
|
||||
if 'z-a-path-metric' in pth_el['path-properties'].keys():
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth = \
|
||||
jsontopath_metric(pth_el['path-properties']['z-a-path-metric'])
|
||||
output_snr, output_snrbandwidth, output_osnr, power, path_bandwidth, \
|
||||
revsnr_min, revpdl, revcd, revpmd = jsontopath_metric(pth_el['path-properties']['z-a-path-metric'])
|
||||
revosnr = round(output_osnr, 2)
|
||||
revsnr = round(output_snr, 2)
|
||||
revsnrb = round(output_snrbandwidth, 2)
|
||||
@@ -618,6 +668,10 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
revosnr = ''
|
||||
revsnr = ''
|
||||
revsnrb = ''
|
||||
revsnr_min = ''
|
||||
revpdl = ''
|
||||
revcd = ''
|
||||
revpmd = ''
|
||||
mywriter.writerow((path_id,
|
||||
source,
|
||||
destination,
|
||||
@@ -627,16 +681,26 @@ def jsontocsv(json_data, equipment, fileout):
|
||||
total_cost,
|
||||
tsp,
|
||||
mode,
|
||||
bitr,
|
||||
rosnr,
|
||||
rsnr,
|
||||
rsnrb,
|
||||
rsnr_min,
|
||||
pdl,
|
||||
cd,
|
||||
pmd,
|
||||
minosnr_inc_margin,
|
||||
brate,
|
||||
pwr,
|
||||
pth,
|
||||
sptrm,
|
||||
revosnr,
|
||||
revsnr,
|
||||
revsnrb
|
||||
revsnrb,
|
||||
revsnr_min,
|
||||
revpdl,
|
||||
revcd,
|
||||
revpmd,
|
||||
))
|
||||
|
||||
|
||||
@@ -1161,11 +1225,9 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist, red
|
||||
min_ind = argmin(snr01nm_with_penalty)
|
||||
if round(snr01nm_with_penalty[min_ind], 2) < pathreq.OSNR + equipment['SI']['default'].sys_margins:
|
||||
msg = f'\tWarning! Request {pathreq.request_id} computed path from' \
|
||||
+ f' {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}' \
|
||||
+ f'\n\tcomputed SNR in 0.1nm = {round(total_path[-1].snr_01nm[min_ind], 2)}'
|
||||
msg = _penalty_msg(total_path, msg, min_ind) \
|
||||
+ f'\n\trequired osnr = {pathreq.OSNR}' \
|
||||
+ f'\n\tsystem margin = {equipment["SI"]["default"].sys_margins}'
|
||||
+ f' {pathreq.source} to {pathreq.destination} does not pass with {pathreq.tsp_mode}'
|
||||
msg = penalty_msg(total_path[-1], msg, min_ind, pathreq.OSNR,
|
||||
equipment["SI"]["default"].sys_margins)
|
||||
LOGGER.warning(msg)
|
||||
pathreq.blocking_reason = 'MODE_NOT_FEASIBLE'
|
||||
else:
|
||||
@@ -1212,11 +1274,8 @@ def compute_path_with_disjunction(network, equipment, pathreqlist, pathlist, red
|
||||
min_ind = argmin(snr01nm_with_penalty)
|
||||
if round(snr01nm_with_penalty[min_ind], 2) < pathreq.OSNR + equipment['SI']['default'].sys_margins:
|
||||
msg = f'\tWarning! Request {pathreq.request_id} computed path from' \
|
||||
+ f' {pathreq.destination} to {pathreq.source} does not pass with {pathreq.tsp_mode}' \
|
||||
+ f'\n\tcomputed SNR in 0.1nm = {round(rev_p[-1].snr_01nm[min_ind], 2)}'
|
||||
msg = _penalty_msg(rev_p, msg, min_ind) \
|
||||
+ f'\n\trequired osnr = {pathreq.OSNR}' \
|
||||
+ f'\n\tsystem margin = {equipment["SI"]["default"].sys_margins}'
|
||||
+ f' {pathreq.destination} to {pathreq.source} does not pass with {pathreq.tsp_mode}'
|
||||
msg = penalty_msg(rev_p[-1], msg, min_ind, pathreq.OSNR, equipment["SI"]["default"].sys_margins)
|
||||
LOGGER.warning(msg)
|
||||
# TODO selection of mode should also be on reversed direction !!
|
||||
if not hasattr(pathreq, 'blocking_reason'):
|
||||
@@ -1310,3 +1369,38 @@ def find_elements_common_range(el_list: list, equipment: dict) -> List[dict]:
|
||||
amp_bands = [n.params.bands for n in el_list if isinstance(n, (Edfa, Multiband_amplifier))]
|
||||
return find_common_range(amp_bands, equipment['SI']['default'].f_min, equipment['SI']['default'].f_max,
|
||||
equipment['SI']['default'].spacing)
|
||||
|
||||
|
||||
def penalty_msg(receiver, msg, min_ind, required_osnr, system_margins):
|
||||
"""Returns a message that contains complementary reasons for blocking
|
||||
Reason can be that lowest GSNR is below threshold, or that accumulated impairment adds a lot of penalty
|
||||
This message is intended to help identifying causes of blocking
|
||||
"""
|
||||
penalty_dict = {
|
||||
'pdl': 'PDL_penalty',
|
||||
'chromatic_dispersion': 'CD_penalty',
|
||||
'pmd': 'PMD_penalty'
|
||||
}
|
||||
complement = {
|
||||
'lowest_SNR-0.1nm': round(receiver.snr_01nm[min_ind], 2),
|
||||
'required_OSNR@0.1nm': required_osnr,
|
||||
'system_margins': system_margins
|
||||
}
|
||||
for penalty, name in penalty_dict.items():
|
||||
complement[name] = get_penalty_from_receiver(receiver, penalty)
|
||||
|
||||
for penalty, value in complement.items():
|
||||
msg += f'\n\t{penalty} = {value}'
|
||||
return msg
|
||||
|
||||
|
||||
def get_penalty_from_receiver(receiver, impairment):
|
||||
"""Read penalty if impairment is in the list
|
||||
"""
|
||||
if impairment in receiver.penalties:
|
||||
penalty_value = round(mean(receiver.penalties[impairment]), 2)
|
||||
if isinf(penalty_value):
|
||||
return "Infinity"
|
||||
return penalty_value
|
||||
else:
|
||||
return 'not evaluated'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,OSNR-0.1nm,SNR-0.1nm,SNR-bandwidth,baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm,reversed path SNR-0.1nm,reversed path SNR-bandwidth
|
||||
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,30.84,30.84,26.75,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-284], [4]",,,
|
||||
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,22.65,22.10,18.02,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | east edfa in Brest_KLA to Morlaix | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | west edfa in Lorient_KMA to Loudeac | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-276], [4]",,,
|
||||
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,28.29,25.85,21.77,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | west edfa in Rennes_STA to Stbrieuc | roadm Rennes_STA | trx Rennes_STA,"[-284], [4]",,,
|
||||
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,22.27,22.14,15.05,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Ploermel | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | west edfa in Vannes_KBE to Ploermel | roadm Vannes_KBE | east edfa in Vannes_KBE to Lorient_KMA | fiber (Vannes_KBE → Lorient_KMA)-F055 | west edfa in Lorient_KMA to Vannes_KBE | roadm Lorient_KMA | east edfa in Lorient_KMA to Loudeac | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"[-266], [6]",,,
|
||||
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,30.79,28.74,21.65,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,"[-274], [6]",,,
|
||||
6,,,,NO_PATH,,,,,,,,,,,,,,
|
||||
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,bit rate,OSNR-0.1nm (average),SNR-0.1nm (average),SNR-bandwidth (average),SNR-0.1nm (min),PDL_penalty,CD_penalty,PMD_penalty,min required OSNR (inc. margin),baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm (average),reversed path SNR-0.1nm (average),reversed path SNR-bandwidth (average),reversed path SNR-0.1nm (min),reversed path PDL_penalty,reversed path CD_penalty,reversed path PMD_penalty
|
||||
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,100.0,30.84,30.84,26.75,,,,,12,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-284], [4]",,,,,,,
|
||||
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,100.0,22.65,22.10,18.02,,,,,12,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | east edfa in Brest_KLA to Morlaix | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | west edfa in Lorient_KMA to Loudeac | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-276], [4]",,,,,,,
|
||||
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,100.0,28.29,25.85,21.77,,,,,11,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | west edfa in Rennes_STA to Stbrieuc | roadm Rennes_STA | trx Rennes_STA,"[-284], [4]",,,,,,,
|
||||
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,200.0,22.27,22.14,15.05,,,,,15,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Ploermel | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | west edfa in Vannes_KBE to Ploermel | roadm Vannes_KBE | east edfa in Vannes_KBE to Lorient_KMA | fiber (Vannes_KBE → Lorient_KMA)-F055 | west edfa in Lorient_KMA to Vannes_KBE | roadm Lorient_KMA | east edfa in Lorient_KMA to Loudeac | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"[-266], [6]",,,,,,,
|
||||
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,200.0,30.79,28.74,21.65,,,,,15,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,"[-274], [6]",,,,,,,
|
||||
6,,,,NO_PATH,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
|
||||
|
1685
tests/data/testTopology_response_expected.json
Normal file
1685
tests/data/testTopology_response_expected.json
Normal file
File diff suppressed because it is too large
Load Diff
2315
tests/data/testTopology_response_with_more_cases.json
Normal file
2315
tests/data/testTopology_response_with_more_cases.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
response-id,source,destination,path_bandwidth,Pass?,nb of tsp pairs,total cost,transponder-type,transponder-mode,bit rate,OSNR-0.1nm (average),SNR-0.1nm (average),SNR-bandwidth (average),SNR-0.1nm (min),PDL_penalty,CD_penalty,PMD_penalty,min required OSNR (inc. margin),baud rate (Gbaud),input power (dBm),path,"spectrum (N,M)",reversed path OSNR-0.1nm (average),reversed path SNR-0.1nm (average),reversed path SNR-bandwidth (average),reversed path SNR-0.1nm (min),reversed path PDL_penalty,reversed path CD_penalty,reversed path PMD_penalty
|
||||
0,trx Lorient_KMA,trx Vannes_KBE,100.0,True,1,1,Voyager,mode 1,100.0,30.84,30.84,26.75,30.81,not evaluated,not evaluated,not evaluated,12,32.0,0.0,trx Lorient_KMA | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-284], [4]",,,,,,,
|
||||
1,trx Brest_KLA,trx Vannes_KBE,10.0,True,1,1,Voyager,mode 1,100.0,22.65,22.11,18.03,22.07,not evaluated,not evaluated,not evaluated,12,32.0,1.0,trx Brest_KLA | roadm Brest_KLA | east edfa in Brest_KLA to Morlaix | fiber (Brest_KLA → Morlaix)-F060 | east fused spans in Morlaix | fiber (Morlaix → Lannion_CAS)-F059 | west edfa in Lannion_CAS to Morlaix | roadm Lannion_CAS | east edfa in Lannion_CAS to Corlay | fiber (Lannion_CAS → Corlay)-F061 | west fused spans in Corlay | fiber (Corlay → Loudeac)-F010 | west fused spans in Loudeac | fiber (Loudeac → Lorient_KMA)-F054 | west edfa in Lorient_KMA to Loudeac | roadm Lorient_KMA | east edfa in Lorient_KMA to Vannes_KBE | fiber (Lorient_KMA → Vannes_KBE)-F055 | west edfa in Vannes_KBE to Lorient_KMA | roadm Vannes_KBE | trx Vannes_KBE,"[-276], [4]",,,,,,,
|
||||
3,trx Lannion_CAS,trx Rennes_STA,60.0,True,1,1,vendorA_trx-type1,mode 1,100.0,28.29,25.85,21.77,25.74,not evaluated,not evaluated,not evaluated,11,32.0,1.0,trx Lannion_CAS | roadm Lannion_CAS | east edfa in Lannion_CAS to Stbrieuc | fiber (Lannion_CAS → Stbrieuc)-F056 | east edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Rennes_STA)-F057 | west edfa in Rennes_STA to Stbrieuc | roadm Rennes_STA | trx Rennes_STA,"[-284], [4]",,,,,,,
|
||||
4,trx Rennes_STA,trx Lannion_CAS,150.0,True,1,1,vendorA_trx-type1,mode 2,200.0,22.27,22.15,15.05,22.11,not evaluated,not evaluated,not evaluated,15,64.0,0.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Ploermel | fiber (Rennes_STA → Ploermel)- | east edfa in Ploermel to Vannes_KBE | fiber (Ploermel → Vannes_KBE)- | west edfa in Vannes_KBE to Ploermel | roadm Vannes_KBE | east edfa in Vannes_KBE to Lorient_KMA | fiber (Vannes_KBE → Lorient_KMA)-F055 | west edfa in Lorient_KMA to Vannes_KBE | roadm Lorient_KMA | east edfa in Lorient_KMA to Loudeac | fiber (Lorient_KMA → Loudeac)-F054 | east fused spans in Loudeac | fiber (Loudeac → Corlay)-F010 | east fused spans in Corlay | fiber (Corlay → Lannion_CAS)-F061 | west edfa in Lannion_CAS to Corlay | roadm Lannion_CAS | trx Lannion_CAS,"[-266], [6]",,,,,,,
|
||||
5,trx Rennes_STA,trx Lannion_CAS,20.0,True,1,1,vendorA_trx-type1,mode 2,200.0,30.79,28.78,21.68,28.69,not evaluated,not evaluated,not evaluated,15,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,"[-274], [6]",,,,,,,
|
||||
6,,,,NO_PATH,,,,,,,,,,,,,,,,,,,,,,,,
|
||||
7fake,trx Rennes_STA,trx Lannion_CAS,,MODE_NOT_FEASIBLE,,,vendorA_trx-type1,mode 2,200.0,30.79,28.78,21.68,10.0,0.5,not evaluated,Infinity,15,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,,30.79,28.78,21.68,20.0,0.5,not evaluated,Infinity
|
||||
8fake,trx Rennes_STA,trx Lannion_CAS,400.0,True,2,2,vendorA_trx-type1,mode 2,200.0,30.79,28.78,21.68,25.0,0.5,not evaluated,not evaluated,15,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,"[-274], [6]",30.79,28.78,21.68,26.0,0.5,0.5,0.5
|
||||
9fake,trx Rennes_STA,trx Lannion_CAS,,MODE_NOT_FEASIBLE,,,vendorA_trx-type1,mode 2,200.0,30.79,28.78,21.68,10.0,0.5,not evaluated,Infinity,15,64.0,3.0,trx Rennes_STA | roadm Rennes_STA | east edfa in Rennes_STA to Stbrieuc | fiber (Rennes_STA → Stbrieuc)-F057 | west edfa in Stbrieuc to Rennes_STA | fiber (Stbrieuc → Lannion_CAS)-F056 | west edfa in Lannion_CAS to Stbrieuc | roadm Lannion_CAS | trx Lannion_CAS,,,,,,,,
|
||||
|
@@ -33,9 +33,9 @@ INFO gnpy.topology.request:request.py
|
||||
with path constraint: ['trx Abilene', 'trx Albany']
|
||||
Computed path (roadms):['roadm Abilene', 'roadm Dallas', 'roadm Little_Rock', 'roadm Memphis', 'roadm Nashville', 'roadm Louisville', 'roadm Cincinnati', 'roadm Columbus', 'roadm Cleveland', 'roadm Buffalo', 'roadm Rochester', 'roadm Syracuse', 'roadm Albany']
|
||||
WARNING gnpy.topology.request:request.py Warning! Request 0 computed path from trx Abilene to trx Albany does not pass with mode 3
|
||||
computed SNR in 0.1nm = 14.32
|
||||
PDL penalty not evaluated
|
||||
CD penalty not evaluated
|
||||
PMD penalty not evaluated
|
||||
required osnr = 18
|
||||
system margin = 2
|
||||
lowest_SNR-0.1nm = 14.32
|
||||
required_OSNR@0.1nm = 18
|
||||
system_margins = 2
|
||||
PDL_penalty = not evaluated
|
||||
CD_penalty = not evaluated
|
||||
PMD_penalty = not evaluated
|
||||
|
||||
@@ -14,6 +14,7 @@ checks that empty info on mode, power, nbchannel in service file are supported
|
||||
|
||||
"""
|
||||
|
||||
from logging import INFO
|
||||
from pathlib import Path
|
||||
from logging import INFO
|
||||
from numpy.testing import assert_allclose
|
||||
@@ -22,10 +23,10 @@ import pytest
|
||||
from gnpy.core.network import build_network
|
||||
from gnpy.core.utils import automatic_nch, lin2db, watt2dbm, dbm2watt
|
||||
from gnpy.core.elements import Roadm
|
||||
from gnpy.topology.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode, correct_json_route_list
|
||||
from gnpy.topology.request import compute_path_dsjctn, propagate, propagate_and_optimize_mode, \
|
||||
correct_json_route_list, PathRequest
|
||||
from gnpy.tools.json_io import load_network, load_equipment, requests_from_json, load_requests, load_json, \
|
||||
_equipment_from_json
|
||||
from gnpy.topology.request import PathRequest
|
||||
|
||||
|
||||
data_dir = Path(__file__).parent.parent / 'tests/data'
|
||||
@@ -211,6 +212,9 @@ def test_propagate_and_optimize_mode(caplog):
|
||||
assert round(min(path[-1].snr_01nm), 2) == 22.22
|
||||
assert mode['format'] == 'mode 1'
|
||||
assert rqs[1].blocking_reason == 'NO_FEASIBLE_MODE'
|
||||
expected_mesg = '\tWarning! Request 1: no mode satisfies path SNR requirement.'
|
||||
expected_mesg = '\tWarning! Request 1 computed path from trx Brest_KLA to trx Vannes_KBE: no mode satisfies ' \
|
||||
+ 'path SNR requirement. Best propagated mode mode 1\n\tlowest_SNR-0.1nm = 22.42' \
|
||||
+ '\n\trequired_OSNR@0.1nm = 12\n\tsystem_margins = 0\n\tPDL_penalty = Infinity' \
|
||||
+ '\n\tCD_penalty = 0.0\n\tPMD_penalty = 0.0'
|
||||
# Last log records mustcontain the message about the las explored mode
|
||||
assert expected_mesg in caplog.records[-1].message
|
||||
|
||||
@@ -183,7 +183,8 @@ def test_excel_service_json_generation(xls_input, expected_json_output):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('json_input',
|
||||
(DATA_DIR / 'testTopology_response.json', )
|
||||
(DATA_DIR / 'testTopology_response.json',
|
||||
DATA_DIR / 'testTopology_response_with_more_cases.json')
|
||||
)
|
||||
def test_csv_response_generation(tmpdir, json_input):
|
||||
"""tests if generated csv is consistant with expected generation same columns (order not important)"""
|
||||
@@ -194,31 +195,6 @@ def test_csv_response_generation(tmpdir, json_input):
|
||||
jsontocsv(json_data, equipment, fcsv)
|
||||
|
||||
expected_csv_filename = json_input.parent / (json_input.stem + '_expected.csv')
|
||||
|
||||
# expected header
|
||||
# csv_header = \
|
||||
# [
|
||||
# 'response-id',
|
||||
# 'source',
|
||||
# 'destination',
|
||||
# 'path_bandwidth',
|
||||
# 'Pass?',
|
||||
# 'nb of tsp pairs',
|
||||
# 'total cost',
|
||||
# 'transponder-type',
|
||||
# 'transponder-mode',
|
||||
# 'OSNR-0.1nm',
|
||||
# 'SNR-0.1nm',
|
||||
# 'SNR-bandwidth',
|
||||
# 'baud rate (Gbaud)',
|
||||
# 'input power (dBm)',
|
||||
# 'path',
|
||||
# 'spectrum (N,M)',
|
||||
# 'reversed path OSNR-0.1nm',
|
||||
# 'reversed path SNR-0.1nm',
|
||||
# 'reversed path SNR-bandwidth'
|
||||
# ]
|
||||
|
||||
resp = read_csv(csv_filename)
|
||||
print(resp)
|
||||
unlink(csv_filename)
|
||||
@@ -248,7 +224,7 @@ def test_csv_response_generation(tmpdir, json_input):
|
||||
|
||||
# test json answers creation
|
||||
@pytest.mark.parametrize('xls_input, expected_response_file', {
|
||||
DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_response.json',
|
||||
DATA_DIR / 'testTopology.xls': DATA_DIR / 'testTopology_response_expected.json',
|
||||
}.items())
|
||||
def test_json_response_generation(xls_input, expected_response_file):
|
||||
"""tests if json response is correctly generated for all combinations of requests"""
|
||||
@@ -304,12 +280,17 @@ def test_json_response_generation(xls_input, expected_response_file):
|
||||
assert expected['response'][i] != response
|
||||
print(f'response {response["response-id"]} should not match')
|
||||
expected['response'][2]['path-properties']['z-a-path-metric'] = [
|
||||
{'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.809999999999999},
|
||||
{'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.890000000000001},
|
||||
{'metric-type': 'SNR-bandwidth', 'accumulative-value': 22.8},
|
||||
{'metric-type': 'SNR-0.1nm', 'accumulative-value': 26.88},
|
||||
{'metric-type': 'OSNR-bandwidth', 'accumulative-value': 26.239999999999998},
|
||||
{'metric-type': 'OSNR-0.1nm', 'accumulative-value': 30.32},
|
||||
{'metric-type': 'lowest_SNR-0.1nm', 'accumulative-value': 26.72},
|
||||
{'metric-type': 'PDL_penalty', 'accumulative-value': 'not evaluated'},
|
||||
{'metric-type': 'CD_penalty', 'accumulative-value': 'not evaluated'},
|
||||
{'metric-type': 'PMD_penalty', 'accumulative-value': 'not evaluated'},
|
||||
{'metric-type': 'reference_power', 'accumulative-value': 0.0012589254117941673},
|
||||
{'metric-type': 'path_bandwidth', 'accumulative-value': 60000000000.0}]
|
||||
assert expected['response'][2] == response
|
||||
# test should be OK now
|
||||
else:
|
||||
assert expected['response'][i] == response
|
||||
|
||||
Reference in New Issue
Block a user