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 = [] for i,pathreq in enumerate(pathreqlist): # 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 = 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 : if pathreq.baud_rate is not None: total_path = propagate(total_path,pathreq,equipment) # for el in total_path: print(el) 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) # 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.tx_osnr = mode['tx_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 # 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 test_automaticmodefeature(net, eqpt, serv, expected_mode): data = load_requests(serv, eqpt, bidir=False) 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) else: total_path, mode = propagate_and_optimize_mode( total_path, pathreq, equipment) # if no baudrate satisfies spacing, no mode is returned and an empty path is returned # a warning is shown in the propagate_and_optimize_mode if mode is not None: print(mode['format']) path_res_list.append(mode['format']) else: print('nok') path_res_list.append('nok') print(path_res_list) assert path_res_list == expected_mode
def compute_path(network, equipment, pathreqlist): # This function is obsolete and not relevant with respect to network building: suggest either to correct # or to suppress it path_res_list = [] for pathreq in 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) build_network(network, equipment, p_db, p_total_db) pathreq.nodes_list.append(pathreq.destination) #we assume that the destination is a strict constraint pathreq.loose_list.append('strict') 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 = compute_constrained_path(network, pathreq) print( f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}\n' ) 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_with_disjunction(network, equipment, pathreqlist, pathlist): """ use a list but a dictionnary might be helpful to find path based on request_id TODO change all these req, dsjct, res lists into dict ! """ path_res_list = [] reversed_path_res_list = [] propagated_reversed_path_res_list = [] for i, pathreq in enumerate(pathreqlist): # use the power specified in requests but might be different from the one # specified for design the power is an optional parameter for requests # definition if optional, use the one defines in eqt_config.json p_db = lin2db(pathreq.power * 1e3) p_total_db = p_db + lin2db(pathreq.nb_channel) print(f'request {pathreq.request_id}') print(f'Computing path from {pathreq.source} to {pathreq.destination}') # adding first node to be clearer on the output print(f'with path constraint: {[pathreq.source] + pathreq.nodes_list}') # pathlist[i] contains the whole path information for request i # last element is a transciver and where the result of the propagation is # recorded. # 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 simulation. This is why # we use deepcopy: to ensure that each propagation is recorded and not overwritten total_path = deepcopy(pathlist[i]) print( f'Computed path (roadms):{[e.uid for e in total_path if isinstance(e, Roadm)]}' ) # for debug # print(f'{pathreq.baud_rate} {pathreq.power} {pathreq.spacing} {pathreq.nb_channel}') if total_path: if pathreq.baud_rate is not None: # means that at this point the mode was entered/forced by user and thus a # baud_rate was defined total_path = propagate(total_path, pathreq, equipment) 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' +\ f' {pathreq.source} to {pathreq.destination} does not pass with' +\ f' {pathreq.tsp_mode}\n\tcomputedSNR in 0.1nm = {temp_snr01nm} ' +\ f'- required osnr {pathreq.OSNR}' print(msg) LOGGER.warning(msg) pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' else: total_path, mode = propagate_and_optimize_mode( total_path, pathreq, equipment) # if no baudrate satisfies spacing, no mode is returned and the last explored mode # a warning is shown in the propagate_and_optimize_mode # propagate_and_optimize_mode function returns the mode with the highest bitrate # that passes. if no mode passes, then a attribute blocking_reason is added on # pathreq that contains the reason for blocking: 'NO_PATH', 'NO_FEASIBLE_MODE', ... try: if pathreq.blocking_reason in BLOCKING_NOPATH: total_path = [] elif pathreq.blocking_reason in BLOCKING_NOMODE: pathreq.baud_rate = mode['baud_rate'] 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'] # other blocking reason should not appear at this point except AttributeError: pathreq.baud_rate = mode['baud_rate'] 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'] # reversed path is needed for correct spectrum assignment reversed_path = find_reversed_path(pathlist[i]) if pathreq.bidir: # only propagate if bidir is true, but needs the reversed path anyway for # correct spectrum assignment rev_p = deepcopy(reversed_path) print( f'\n\tPropagating Z to A direction {pathreq.destination} to {pathreq.source}' ) print( f'\tPath (roadsm) {[r.uid for r in rev_p if isinstance(r,Roadm)]}\n' ) propagated_reversed_path = propagate(rev_p, pathreq, equipment) temp_snr01nm = round(mean(propagated_reversed_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' +\ f' {pathreq.source} to {pathreq.destination} does not pass with' +\ f' {pathreq.tsp_mode}\n' +\ f'\tcomputedSNR in 0.1nm = {temp_snr01nm} - required osnr {pathreq.OSNR}' print(msg) LOGGER.warning(msg) # TODO selection of mode should also be on reversed direction !! pathreq.blocking_reason = 'MODE_NOT_FEASIBLE' else: propagated_reversed_path = [] else: msg = 'Total path is empty. No propagation' print(msg) LOGGER.info(msg) reversed_path = [] propagated_reversed_path = [] path_res_list.append(total_path) reversed_path_res_list.append(reversed_path) propagated_reversed_path_res_list.append(propagated_reversed_path) # print to have a nice output print('') return path_res_list, reversed_path_res_list, propagated_reversed_path_res_list
def main(network, equipment, source, destination, req=None): result_dicts = {} network_data = [{ 'network_name': str(args.filename), 'source': source.uid, 'destination': destination.uid }] result_dicts.update({'network': network_data}) design_data = [{ 'power_mode': equipment['Spans']['default'].power_mode, 'span_power_range': equipment['Spans']['default'].delta_power_range_db, 'design_pch': equipment['SI']['default'].power_dbm, 'baud_rate': equipment['SI']['default'].baud_rate }] result_dicts.update({'design': design_data}) simulation_data = [] result_dicts.update({'simulation results': simulation_data}) power_mode = equipment['Spans']['default'].power_mode print('\n'.join([ f'Power mode is set to {power_mode}', f'=> it can be modified in eqpt_config.json - Spans' ])) pref_ch_db = lin2db(req.power * 1e3) #reference channel power / span (SL=20dB) pref_total_db = pref_ch_db + lin2db( req.nb_channel) #reference total power / span (SL=20dB) build_network(network, equipment, pref_ch_db, pref_total_db) path = compute_constrained_path(network, req) spans = [s.length for s in path if isinstance(s, Fiber)] print( f'\nThere are {len(spans)} fiber spans over {sum(spans):.0f}m between {source.uid} and {destination.uid}' ) 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) except TypeError: print( 'invalid power range definition in eqpt_config, should be power_range_db: [lower, upper, step]' ) power_range = [0] for dp_db in power_range: req.power = db2lin(pref_ch_db + dp_db) * 1e-3 print( f'\nPropagating with input power = {lin2db(req.power*1e3):.2f}dBm :' ) propagate(path, req, equipment, show=len(power_range) == 1) print( f'\nTransmission result for input power = {lin2db(req.power*1e3):.2f}dBm :' ) print(destination) simulation_data.append({ 'Pch_dBm': pref_ch_db + dp_db, 'OSNR_ASE_0.1nm': round(mean(destination.osnr_ase_01nm), 2), 'OSNR_ASE_signal_bw': round(mean(destination.osnr_ase), 2), 'SNR_nli_signal_bw': round(mean(destination.osnr_nli), 2), 'SNR_total_signal_bw': round(mean(destination.snr), 2) }) write_csv(result_dicts, 'simulation_result.csv') return path