def si(nch_and_spacing, bw): """parametrize a channel comb with nb_channel, spacing and signal bw""" nb_channel, spacing = nch_and_spacing 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)
def propagation(input_power, con_in, con_out,dest): equipment = load_equipment(eqpt_library_name) network = load_network(network_file_name,equipment) build_network(network, equipment, 0, 20) # parametrize the network elements with the con losses and adapt gain # (assumes all spans are identical) for e in network.nodes(): if isinstance(e, Fiber): loss = e.loss_coef * e.length e.con_in = con_in e.con_out = con_out if isinstance(e, Edfa): e.operational.gain_target = loss + con_in + con_out transceivers = {n.uid: n for n in network.nodes() if isinstance(n, Transceiver)} p = input_power p = db2lin(p) * 1e-3 spacing = 50e9 # THz si = create_input_spectral_information(191.3e12, 191.3e12+79*spacing, 0.15, 32e9, p, spacing) source = next(transceivers[uid] for uid in transceivers if uid == 'trx A') sink = next(transceivers[uid] for uid in transceivers if uid == dest) path = dijkstra_path(network, source, sink) for el in path: si = el(si) print(el) # remove this line when sweeping across several powers edfa_sample = next(el for el in path if isinstance(el, Edfa)) nf = mean(edfa_sample.nf) print(f'pw: {input_power} conn in: {con_in} con out: {con_out}', f'[email protected]: {round(mean(sink.osnr_ase_01nm),2)}', f'SNR@bandwitdth: {round(mean(sink.snr),2)}') return sink , nf
def test_raman_fiber(): """ Test the accuracy of propagating the RamanFiber. """ # spectral information generation power = 1e-3 eqpt_params = load_json(TEST_DIR / 'data' / 'eqpt_config.json') spectral_info_params = eqpt_params['SI'][0] spectral_info_params.pop('power_dbm') spectral_info_params.pop('power_range_db') spectral_info_params.pop('tx_osnr') spectral_info_params.pop('sys_margins') spectral_info_input = create_input_spectral_information(power=power, **spectral_info_params) sim_params = SimParams(**load_json(TEST_DIR / 'data' / 'sim_params.json')) Simulation.set_params(sim_params) fiber = RamanFiber(**load_json(TEST_DIR / 'data' / 'raman_fiber_config.json')) # propagation spectral_info_out = fiber(spectral_info_input) p_signal = [carrier.power.signal for carrier in spectral_info_out.carriers] p_ase = [carrier.power.ase for carrier in spectral_info_out.carriers] p_nli = [carrier.power.nli for carrier in spectral_info_out.carriers] expected_results = read_csv(TEST_DIR / 'data' / 'expected_results_science_utils.csv') assert_allclose(p_signal, expected_results['signal'], rtol=1e-3) assert_allclose(p_ase, expected_results['ase'], rtol=1e-3) assert_allclose(p_nli, expected_results['nli'], rtol=1e-3)
def propagate(path, req, equipment): si = create_input_spectral_information( req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for el in path: si = el(si) path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) return path
def propagation(input_power, network, sim_path, initial_slot, num_slots, eqpt): """ Calculate and output SNR based on inputs input_power: Power in decibels network: Network created from GNPy topology sim_path: list of nodes service will travel through num_slots: number of slots in service eqpt: equipment library for GNPy """ # Values to create Spectral Information object spacing = 12.5e9 min_freq = 195942783006536 + initial_slot * spacing max_freq = min_freq + (num_slots - 1) * spacing p = input_power p = db2lin(p) * 1e-3 si = create_input_spectral_information(min_freq, max_freq, 0.15, 32e9, p, spacing) p_total_db = input_power + lin2db( automatic_nch(min_freq, max_freq, spacing)) build_network(network, eqpt, input_power, p_total_db) # Store network elements transceivers = { n.uid: n for n in network.nodes() if isinstance(n, Transceiver) } fibers = {n.uid: n for n in network.nodes() if isinstance(n, Fiber)} edfas = {n.uid: n for n in network.nodes() if isinstance(n, Edfa)} # Recreate path in the GNPy network using node list from simulator path = [] for index, node in enumerate(sim_path): # add transceiver to path path.append(transceivers[node]) # add fiber connecting transceivers to path, unless source transceiver is last in path if index + 1 < len(sim_path): fiber_str = f"Fiber ({node} \u2192 {sim_path[index+1]})" for uid in fibers: # add all fibers to path even if they are split up if uid[0:len(fiber_str)] == fiber_str: path.append(fibers[uid]) # add amplifier to path, if necessary edfa = f"Edfa0_{uid}" fiber_neighbors = [ n.uid for n in neighbors(network, fibers[uid]) ] if edfa in edfas and edfa in fiber_neighbors: path.append(edfas[edfa]) # Calculate effects of physical layer impairments for el in path: si = el(si) destination_node = path[-1] return destination_node.snr
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 # 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 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 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) # 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.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: 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']: 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'\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'\tWarning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' print(msg) logger.info(msg) return [], None
def propagate2(path, req, equipment): si = create_input_spectral_information( req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) infos = {} for el in path: before_si = si after_si = si = el(si) infos[el] = before_si, after_si path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) return infos
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) for el in path: si = el(si) if show: print(el) return path
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.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for el in path: si = el(si) if show: print(el) path[-1].update_snr(req.tx_osnr, equipment['Roadms']['default'].add_drop_osnr) return path
def propagate(path, req, equipment): si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): next_el = path[i + 1] si = el(si, degree=next_el.uid) else: si = el(si) print(el) path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) return path
def propagate2(path, req, equipment): si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) infos = {} for i, el in enumerate(path): before_si = si if isinstance(el, Roadm): next_el = path[i + 1] after_si = si = el(si, degree=next_el.uid) else: after_si = si = el(si) infos[el] = before_si, after_si path[-1].update_snr(req.tx_osnr, equipment['Roadm']['default'].add_drop_osnr) return infos
import gnpy.core.info as gn import matplotlib.pyplot as plt import Mylib as ml import json as js # Exercise 3 # Create and plot a spectral information representing a comb of 120 WDM channels. # Each channel has a -3 dB bandwidth of 32 Gbaud, a roll-off of 0.2 and a # power equal to 0 dBm. This comb is centered around 193 THz and the spacing # is 45 GHz. data = {"f center": 194.5e12, "roll off": 0.2, "baud rate": 32e9, "power": 1e-3, "spacing": 45e9} # write json data into a file with open("Parameters3.json", "w") as write_file: js.dump(data, write_file, indent=4) with open("Parameters3.json", "r") as read_file: data = js.load(read_file) f_min = data["f center"]-(data["spacing"]*60) f_max = data["f center"]+(data["spacing"]*60) si = gn.create_input_spectral_information(f_min, f_max, data["roll off"], data["baud rate"], data["power"], data["spacing"]) ml.plot_spectrum(si) plt.show()
def propagate_and_optimize_mode(path, req, equipment): # if mode is unknown : loops on the modes starting from the highest baudrate fiting in the # step 1: create an ordered list of modes based on baudrate baudrate_to_explore = list( set([ this_mode['baud_rate'] for this_mode in equipment['Transceiver'][req.tsp].mode if float(this_mode['min_spacing']) <= req.spacing ])) # 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 for this_br in baudrate_to_explore: modes_to_explore = [ this_mode for this_mode in equipment['Transceiver'][req.tsp].mode if this_mode['baud_rate'] == this_br and float(this_mode['min_spacing']) <= req.spacing ] 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 spc_info = create_input_spectral_information( req.f_min, req.f_max, equipment['SI']['default'].roll_off, this_br, req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): next_el = path[i + 1] spc_info = el(spc_info, degree=next_el.uid) else: spc_info = el(spc_info) for this_mode in modes_to_explore: if path[-1].snr is not None: path[-1].update_snr( this_mode['tx_osnr'], equipment['Roadm']['default'].add_drop_osnr) if round(min(path[-1].snr + lin2db(this_br / (12.5e9))), 2) > this_mode['OSNR']: found_a_feasible_mode = True return path, this_mode else: last_explored_mode = this_mode else: req.blocking_reason = 'NO_COMPUTED_SNR' return path, None # 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' print(msg) LOGGER.info(msg) req.blocking_reason = 'NO_FEASIBLE_MODE' return path, last_explored_mode else: # no baudrate satisfying spacing msg = f'\tWarning! Request {req.request_id}: no baudrate satisfies spacing requirement.\n' print(msg) LOGGER.info(msg) req.blocking_reason = 'NO_FEASIBLE_BAUDRATE_WITH_SPACING' return [], None
import gnpy.core.info as gn import matplotlib.pyplot as plt import Mylib as ml # 1. Create a json file with the following parameters (in fundamental units, Hz, # Baud, W): data = { "f min": 191.5e12, "f max": 194.5e12, "roll off": 0.2, "baud rate": 32e9, "power": 1e-3, "spacing": 50e9 } # write json data into a file with open("Parameters.json", "w") as write_file: js.dump(data, write_file, indent=4) # read json data from a file with open("Parameters.json", "r") as read_file: data = js.load(read_file) # Create spetral information si = gn.create_input_spectral_information(data['f min'], data['f max'], data['roll off'], data['baud rate'], data['power'], data['spacing']) ml.plot_spectrum(si) plt.show()
def test_roadm_target_power(prev_node_type, effective_pch_out_db): ''' Check that egress power of roadm is equal to target power if input power is greater than target power else, that it is equal to input power. Use a simple two hops A-B-C topology for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target power can not be met in this last case. ''' equipment = load_equipment(EQPT_LIBRARY_NAME) json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json') prev_node = next(n for n in json_network['elements'] if n['uid'] == 'west edfa in node B to ila2') json_network['elements'].remove(prev_node) if prev_node_type == 'edfa': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'} elif prev_node_type == 'fused': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'} prev_node['params'] = {'loss': 0} json_network['elements'].append(prev_node) network = network_from_json(json_network, equipment) # Build the network once using the default power defined in SI in eqpt config p_db = equipment['SI']['default'].power_dbm p_total_db = p_db + lin2db( automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, p_db, p_total_db) params = {} params['request_id'] = 0 params['trx_type'] = '' params['trx_mode'] = '' params['source'] = 'trx node A' params['destination'] = 'trx node C' params['bidir'] = False params['nodes_list'] = ['trx node C'] params['loose_list'] = ['strict'] params['format'] = '' params['path_bandwidth'] = 100e9 trx_params = trx_mode_params(equipment) params.update(trx_params) req = PathRequest(**params) path = compute_constrained_path(network, req) si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): carriers_power_in_roadm = min([ c.power.signal + c.power.nli + c.power.ase for c in si.carriers ]) si = el(si, degree=path[i + 1].uid) if el.uid == 'roadm node B': print('input', carriers_power_in_roadm) assert el.effective_pch_out_db == effective_pch_out_db for carrier in si.carriers: print(carrier.power.signal + carrier.power.nli + carrier.power.ase) power = carrier.power.signal + carrier.power.nli + carrier.power.ase if prev_node_type == 'edfa': # edfa prev_node sets input power to roadm to a high enough value: # Check that egress power of roadm is equal to target power assert power == pytest.approx( db2lin(effective_pch_out_db - 30), rel=1e-3) elif prev_node_type == 'fused': # fused prev_node does reamplfy power after fiber propagation, so input power # to roadm is low. # Check that egress power of roadm is equalized to the min carrier input power. assert power == pytest.approx(carriers_power_in_roadm, rel=1e-3) else: si = el(si)
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)
#print(Edfa) # 5. instantiate a noiseless WDM comb according to the parameters described # in eqpt.json file # read json data from a file with open("eqp2.json", "r") as read_file: data = js.load(read_file) # From the json data, extract only the values needed for the spectral info (SI) si_data = data['SI'][0] # Compute the WDM for the interested Spectral info si = gn.create_input_spectral_information( si_data['f_min'], si_data['f_max'], si_data['roll_off'], si_data['baud_rate'], 10**(si_data['power_dbm'] / 10) / 1000, si_data['spacing']) # 6. propagate the WDM comb through the EDFA. # Hint: As Edfa has the method call (self, spectral info), it can be used # as function. So, the command edfa(spectral information) will return the # spectral information propagated through the EDFA. ml.plot_signal(si) new_si = EDFA(si) # 7. plot the signal and ASE noise power before and after the propagation. ml.plot_signal_ASE(new_si) #print(new_si)
def test_roadm_target_power(prev_node_type, effective_pch_out_db, power_dbm): ''' Check that egress power of roadm is equal to target power if input power is greater than target power else, that it is equal to input power. Use a simple two hops A-B-C topology for the test where the prev_node in ROADM B is either an amplifier or a fused, so that the target power can not be met in this last case. ''' equipment = load_equipment(EQPT_LIBRARY_NAME) json_network = load_json(TEST_DIR / 'data/twohops_roadm_power_test.json') prev_node = next(n for n in json_network['elements'] if n['uid'] == 'west edfa in node B to ila2') json_network['elements'].remove(prev_node) if prev_node_type == 'edfa': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Edfa'} elif prev_node_type == 'fused': prev_node = {'uid': 'west edfa in node B to ila2', 'type': 'Fused'} prev_node['params'] = {'loss': 0} json_network['elements'].append(prev_node) network = network_from_json(json_network, equipment) p_total_db = power_dbm + lin2db( automatic_nch(equipment['SI']['default'].f_min, equipment['SI']['default'].f_max, equipment['SI']['default'].spacing)) build_network(network, equipment, power_dbm, p_total_db) params = { 'request_id': 0, 'trx_type': '', 'trx_mode': '', 'source': 'trx node A', 'destination': 'trx node C', 'bidir': False, 'nodes_list': ['trx node C'], 'loose_list': ['strict'], 'format': '', 'path_bandwidth': 100e9, 'effective_freq_slot': None, } trx_params = trx_mode_params(equipment) params.update(trx_params) req = PathRequest(**params) req.power = db2lin(power_dbm - 30) path = compute_constrained_path(network, req) si = create_input_spectral_information(req.f_min, req.f_max, req.roll_off, req.baud_rate, req.power, req.spacing) for i, el in enumerate(path): if isinstance(el, Roadm): carriers_power_in_roadm = min([ c.power.signal + c.power.nli + c.power.ase for c in si.carriers ]) si = el(si, degree=path[i + 1].uid) if el.uid == 'roadm node B': print('input', carriers_power_in_roadm) # if previous was an EDFA, power level at ROADM input is enough for the ROADM to apply its # target power (as specified in equipment ie -20 dBm) # if it is a Fused, the input power to the ROADM is smaller than the target power, and the # ROADM cannot apply this target. In this case, it is assumed that the ROADM has 0 dB loss # so the output power will be the same as the input power, which for this particular case # corresponds to -22dBm + power_dbm # next step (for ROADM modelling) will be to apply a minimum loss for ROADMs ! if prev_node_type == 'edfa': assert el.effective_pch_out_db == effective_pch_out_db if prev_node_type == 'fused': # then output power == input_power == effective_pch_out_db + power_dbm assert effective_pch_out_db + power_dbm == \ pytest.approx(lin2db(carriers_power_in_roadm * 1e3), rel=1e-3) assert el.effective_pch_out_db == effective_pch_out_db + power_dbm for carrier in si.carriers: print(carrier.power.signal + carrier.power.nli + carrier.power.ase) power = carrier.power.signal + carrier.power.nli + carrier.power.ase if prev_node_type == 'edfa': # edfa prev_node sets input power to roadm to a high enough value: # Check that egress power of roadm is equal to target power assert power == pytest.approx( db2lin(effective_pch_out_db - 30), rel=1e-3) elif prev_node_type == 'fused': # fused prev_node does reamplfy power after fiber propagation, so input power # to roadm is low. # Check that egress power of roadm is equalized to the min carrier input power. assert power == pytest.approx(carriers_power_in_roadm, rel=1e-3) else: si = el(si)