strip whitespace

This commit is contained in:
James Powell
2018-10-05 20:59:25 -04:00
parent 9898dc85a9
commit 83444b329e
7 changed files with 85 additions and 85 deletions

View File

@@ -7,11 +7,11 @@ xls to json parser, that can be called directly from the transmission_main_examp
xls examples are meshTopologyExampleV2.xls and CORONET_Global_Topology.xls xls examples are meshTopologyExampleV2.xls and CORONET_Global_Topology.xls
Require Nodes and Links sheets, Eqpt sheet is optional Require Nodes and Links sheets, Eqpt sheet is optional
*in Nodes sheet, only the 'City' column is mandatory. The column 'Type' is discovered based *in Nodes sheet, only the 'City' column is mandatory. The column 'Type' is discovered based
on the topology: degree 2 = ILA, other degrees = ROADM. The value is also corrected if the user on the topology: degree 2 = ILA, other degrees = ROADM. The value is also corrected if the user
specifies an ILA of degree != 2. specifies an ILA of degree != 2.
*In Links sheet only the 3 first columns (Node A, Node Z and east Distance (km)) are mandatory. *In Links sheet only the 3 first columns (Node A, Node Z and east Distance (km)) are mandatory.
Missing west information are copied from east information so it is possible to input undir data Missing west information are copied from east information so it is possible to input undir data
*in Eqpt sheet *in Eqpt sheet
""" """
from sys import exit from sys import exit
@@ -39,19 +39,19 @@ class Link(namedtuple('Link', 'from_city to_city \
west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \ west_distance west_fiber west_lineic west_con_in west_con_out west_pmd west_cable \
distance_units')): distance_units')):
def __new__(cls, from_city, to_city, def __new__(cls, from_city, to_city,
east_distance, east_fiber='SSMF', east_lineic=0.2, east_distance, east_fiber='SSMF', east_lineic=0.2,
east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='', east_con_in=None, east_con_out=None, east_pmd=0.1, east_cable='',
west_distance='', west_fiber='', west_lineic='', west_distance='', west_fiber='', west_lineic='',
west_con_in='', west_con_out='', west_pmd='', west_cable='', west_con_in='', west_con_out='', west_pmd='', west_cable='',
distance_units='km'): distance_units='km'):
east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out, east_values = [east_distance, east_fiber, east_lineic, east_con_in, east_con_out,
east_pmd, east_cable] east_pmd, east_cable]
west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out, west_values = [west_distance, west_fiber, west_lineic, west_con_in, west_con_out,
west_pmd, west_cable] west_pmd, west_cable]
default_values = [80,'SSMF',0.2,None,None,0.1,''] 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)] 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)] 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) return super().__new__(cls, from_city, to_city, *east_values, *west_values, distance_units)
class Eqpt(namedtuple('Eqpt', 'from_city to_city \ class Eqpt(namedtuple('Eqpt', 'from_city to_city \
egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\ egress_amp_type egress_att_in egress_amp_gain egress_amp_tilt egress_amp_att_out\
@@ -64,7 +64,7 @@ class Eqpt(namedtuple('Eqpt', 'from_city to_city \
ingress_amp_type, ingress_att_in, ingress_amp_gain, ingress_amp_tilt, ingress_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] 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)] values = [x[0] if x[0] != '' else x[1] for x in zip(values,default_values)]
return super().__new__(cls, *values) return super().__new__(cls, *values)
def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city): def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
try : try :
@@ -83,7 +83,7 @@ def sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city):
for city,link in links_by_city.items(): 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' and len(link) != 2:
#wrong input: ILA sites can only be Degree 2 #wrong input: ILA sites can only be Degree 2
# => correct to make it a ROADM and remove entry in links_by_city # => correct to make it a ROADM and remove entry in links_by_city
#TODO : put in log rather than print #TODO : put in log rather than print
print(f'invalid node type ({nodes_by_city[city].node_type})\ print(f'invalid node type ({nodes_by_city[city].node_type})\
@@ -115,7 +115,7 @@ def convert_file(input_filename, filter_region=[]):
global eqpts_by_city global eqpts_by_city
eqpts_by_city = defaultdict(list) eqpts_by_city = defaultdict(list)
for eqpt in eqpts: for eqpt in eqpts:
eqpts_by_city[eqpt.from_city].append(eqpt) eqpts_by_city[eqpt.from_city].append(eqpt)
nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city) nodes = sanity_check(nodes, nodes_by_city, links_by_city, eqpts_by_city)
@@ -148,7 +148,7 @@ def convert_file(input_filename, filter_region=[]):
'latitude': x.latitude, 'latitude': x.latitude,
'longitude': x.longitude}}, 'longitude': x.longitude}},
'type': 'Fused'} 'type': 'Fused'}
for x in nodes_by_city.values() if x.node_type.lower() == '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}{x.to_city})-{x.east_cable}',
'metadata': {'location': midpoint(nodes_by_city[x.from_city], 'metadata': {'location': midpoint(nodes_by_city[x.from_city],
nodes_by_city[x.to_city])}, nodes_by_city[x.to_city])},
@@ -171,7 +171,7 @@ def convert_file(input_filename, filter_region=[]):
'loss_coef': x.west_lineic, 'loss_coef': x.west_lineic,
'con_in':x.west_con_in, 'con_in':x.west_con_in,
'con_out':x.west_con_out} 'con_out':x.west_con_out}
} # missing ILA construction } # missing ILA construction
for x in links] + for x in links] +
[{'uid': f'egress edfa in {e.from_city} to {e.to_city}', [{'uid': f'egress edfa in {e.from_city} to {e.to_city}',
'metadata': {'location': {'city': nodes_by_city[e.from_city].city, 'metadata': {'location': {'city': nodes_by_city[e.from_city].city,
@@ -193,7 +193,7 @@ def convert_file(input_filename, filter_region=[]):
'type_variety': e.ingress_amp_type, 'type_variety': e.ingress_amp_type,
'operational': {'gain_target': e.ingress_amp_gain, 'operational': {'gain_target': e.ingress_amp_gain,
'tilt_target': e.ingress_amp_tilt} 'tilt_target': e.ingress_amp_tilt}
} }
for e in eqpts if e.ingress_amp_type.lower() != ''], for e in eqpts if e.ingress_amp_type.lower() != ''],
'connections': 'connections':
list(chain.from_iterable([eqpt_connection_by_city(n.city) list(chain.from_iterable([eqpt_connection_by_city(n.city)
@@ -205,7 +205,7 @@ def convert_file(input_filename, filter_region=[]):
for x in nodes_by_city.values() if x.node_type.lower()=='roadm'], for x in nodes_by_city.values() if x.node_type.lower()=='roadm'],
[{'from_node': f'roadm {x.city}', [{'from_node': f'roadm {x.city}',
'to_node': f'trx {x.city}'} 'to_node': f'trx {x.city}'}
for x in nodes_by_city.values() if x.node_type.lower()=='roadm']))) for x in nodes_by_city.values() if x.node_type.lower()=='roadm'])))
} }
#print(dumps(data, indent=2)) #print(dumps(data, indent=2))
@@ -235,20 +235,20 @@ def parse_excel(input_filename):
expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude'] expected = ['City', 'State', 'Country', 'Region', 'Latitude', 'Longitude']
if header != expected: if header != expected:
raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}') raise ValueError(f'Malformed header on Nodes sheet: {header} != {expected}')
""" """
nodes = [] nodes = []
for row in all_rows(nodes_sheet, start=5): for row in all_rows(nodes_sheet, start=5):
nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN]))) nodes.append(Node(*(x.value for x in row[0:NODES_COLUMN])))
#check input #check input
expected_node_types = ('ROADM', 'ILA', 'FUSED') expected_node_types = ('ROADM', 'ILA', 'FUSED')
nodes = [n._replace(node_type='ILA') nodes = [n._replace(node_type='ILA')
if not (n.node_type in expected_node_types) else n for n in nodes] if not (n.node_type in expected_node_types) else n for n in nodes]
# sanity check # sanity check
""" """
header = [x.value.strip() for x in links_sheet.row(4)] header = [x.value.strip() for x in links_sheet.row(4)]
expected = ['Node A', 'Node Z', 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',
'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: if header != expected:
@@ -280,7 +280,7 @@ def eqpt_connection_by_city(city_name):
if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'): if nodes_by_city[city_name].node_type.lower() in ('ila', 'fused'):
# Then len(other_cities) == 2 # Then len(other_cities) == 2
direction = ['ingress', 'egress'] direction = ['ingress', 'egress']
for i in range(2): for i in range(2):
from_ = fiber_link(other_cities[i], city_name) from_ = fiber_link(other_cities[i], city_name)
in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i]) in_ = eqpt_in_city_to_city(city_name, other_cities[0],direction[i])
to_ = fiber_link(city_name, other_cities[1-i]) to_ = fiber_link(city_name, other_cities[1-i])
@@ -334,7 +334,7 @@ def fiber_dest_from_source(city_name):
destinations = [] destinations = []
links_from_city = links_by_city[city_name] links_from_city = links_by_city[city_name]
for l in links_from_city: for l in links_from_city:
if l.from_city == city_name: if l.from_city == city_name:
destinations.append(l.to_city) destinations.append(l.to_city)
else: else:
destinations.append(l.from_city) destinations.append(l.from_city)

View File

@@ -109,7 +109,7 @@ class Roadm(Node):
'metadata' : { 'metadata' : {
'location': self.metadata['location']._asdict() 'location': self.metadata['location']._asdict()
} }
} }
def __repr__(self): def __repr__(self):
return f'{type(self).__name__}(uid={self.uid!r}, loss={self.loss!r})' return f'{type(self).__name__}(uid={self.uid!r}, loss={self.loss!r})'
@@ -174,7 +174,7 @@ class Fused(Node):
nonlinear_interference=pwr.nli/attenuation, nonlinear_interference=pwr.nli/attenuation,
amplified_spontaneous_emission=pwr.ase/attenuation) amplified_spontaneous_emission=pwr.ase/attenuation)
yield carrier._replace(power=pwr) yield carrier._replace(power=pwr)
def update_pref(self, pref): def update_pref(self, pref):
return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss) return pref._replace(p_span0=pref.p0, p_spani=pref.pi - self.loss)
@@ -192,7 +192,7 @@ class Fiber(Node):
params = {} params = {}
if 'con_in' not in params: if 'con_in' not in params:
# if not defined in the network json connector loss in/out # if not defined in the network json connector loss in/out
# the None value will be updated in network.py[build_network] # the None value will be updated in network.py[build_network]
# with default values from eqpt_config.json[Spans] # with default values from eqpt_config.json[Spans]
params['con_in'] = None params['con_in'] = None
params['con_out'] = None params['con_out'] = None
@@ -209,8 +209,8 @@ class Fiber(Node):
self.con_in = self.params.con_in self.con_in = self.params.con_in
self.con_out = self.params.con_out self.con_out = self.params.con_out
self.dispersion = self.params.dispersion # s/m/m self.dispersion = self.params.dispersion # s/m/m
self.gamma = self.params.gamma # 1/W/m self.gamma = self.params.gamma # 1/W/m
self.pch_out = None self.pch_out = None
# TODO|jla: discuss factor 2 in the linear lineic attenuation # TODO|jla: discuss factor 2 in the linear lineic attenuation
@property @property
@@ -249,15 +249,15 @@ class Fiber(Node):
def fiber_loss(self): def fiber_loss(self):
# dB fiber loss, not including padding attenuator # dB fiber loss, not including padding attenuator
return self.loss_coef * self.length + self.con_in + self.con_out return self.loss_coef * self.length + self.con_in + self.con_out
@property @property
def loss(self): def loss(self):
#total loss incluiding padding att_in: useful for polymorphism with roadm loss #total loss incluiding padding att_in: useful for polymorphism with roadm loss
return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in return self.loss_coef * self.length + self.con_in + self.con_out + self.att_in
@property @property
def passive(self): def passive(self):
return True return True
@property @property
def lin_attenuation(self): def lin_attenuation(self):
@@ -459,7 +459,7 @@ class Edfa(Node):
if self.pin_db is None or self.pout_db is None: if self.pin_db is None or self.pout_db is None:
return f'{type(self).__name__} {self.uid}' return f'{type(self).__name__} {self.uid}'
nf = mean(self.nf) nf = mean(self.nf)
return '\n'.join([f'{type(self).__name__} {self.uid}', return '\n'.join([f'{type(self).__name__} {self.uid}',
f' type_variety: {self.params.type_variety}', f' type_variety: {self.params.type_variety}',
f' effective gain(dB): {self.effective_gain:.2f}', f' effective gain(dB): {self.effective_gain:.2f}',
f' (before att_in and before output VOA)', f' (before att_in and before output VOA)',
@@ -519,13 +519,13 @@ class Edfa(Node):
g1a = gain_target - self.params.nf_model.delta_p - dg g1a = gain_target - self.params.nf_model.delta_p - dg
nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a)) nf_avg = lin2db(db2lin(self.params.nf_model.nf1) + db2lin(self.params.nf_model.nf2)/db2lin(g1a))
elif self.params.type_def == 'fixed_gain': elif self.params.type_def == 'fixed_gain':
nf_avg = self.params.nf_model.nf0 nf_avg = self.params.nf_model.nf0
else: else:
nf_avg = polyval(self.params.nf_fit_coeff, -dg) nf_avg = polyval(self.params.nf_fit_coeff, -dg)
if avg: if avg:
return nf_avg + pad return nf_avg + pad
else: else:
return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation return self.interpol_nf_ripple + nf_avg + pad # input VOA = 1 for 1 NF degradation
def noise_profile(self, df): def noise_profile(self, df):
""" noise_profile(bw) computes amplifier ase (W) in signal bw (Hz) """ noise_profile(bw) computes amplifier ase (W) in signal bw (Hz)
@@ -704,7 +704,7 @@ class Edfa(Node):
yield carrier._replace(power=pwr) yield carrier._replace(power=pwr)
def update_pref(self, pref): def update_pref(self, pref):
return pref._replace(p_span0=pref.p0, return pref._replace(p_span0=pref.p0,
p_spani=pref.pi + self.effective_gain - self.operational.out_voa) p_spani=pref.pi + self.effective_gain - self.operational.out_voa)
def __call__(self, spectral_info): def __call__(self, spectral_info):

View File

@@ -4,7 +4,7 @@
''' '''
nf model parameters calculation nf model parameters calculation
calculate nf1, nf2 and Delta_P of a 2 coils edfa with internal VOA calculate nf1, nf2 and Delta_P of a 2 coils edfa with internal VOA
from nf_min and nf_max inputs from nf_min and nf_max inputs
''' '''
from numpy import clip, polyval from numpy import clip, polyval
from sys import exit from sys import exit
@@ -31,12 +31,12 @@ AmpBase = namedtuple(
' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design') ' nf_model nf_fit_coeff nf_ripple dgt gain_ripple out_voa_auto allowed_for_design')
class Amp(AmpBase): class Amp(AmpBase):
def __new__(cls, def __new__(cls,
type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None, type_variety, type_def, gain_flatmax, gain_min, p_max, nf_model=None,
nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None, nf_fit_coeff=None, nf_ripple=None, dgt=None, gain_ripple=None,
out_voa_auto=False, allowed_for_design=True): out_voa_auto=False, allowed_for_design=True):
return super().__new__(cls, return super().__new__(cls,
type_variety, type_def, gain_flatmax, gain_min, p_max, type_variety, type_def, gain_flatmax, gain_min, p_max,
nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple, nf_model, nf_fit_coeff, nf_ripple, dgt, gain_ripple,
out_voa_auto, allowed_for_design) out_voa_auto, allowed_for_design)
@classmethod @classmethod
@@ -162,7 +162,7 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
print('Computation stopped.') print('Computation stopped.')
exit() exit()
else: else:
# default transponder charcteristics # default transponder charcteristics
trx_params['frequency'] = {'min': default_si_data.f_min, 'max': default_si_data.f_max} 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['baud_rate'] = default_si_data.baud_rate
trx_params['spacing'] = default_si_data.spacing trx_params['spacing'] = default_si_data.spacing
@@ -178,10 +178,10 @@ def trx_mode_params(equipment, trx_type_variety='', trx_mode='', error_message=F
def automatic_spacing(baud_rate): def automatic_spacing(baud_rate):
"""return the min possible channel spacing for a given 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 spacing_list = [(38e9,50e9), (67e9,75e9), (92e9,100e9)] #list of possible tuples
#[(max_baud_rate, spacing_for_this_baud_rate)] #[(max_baud_rate, spacing_for_this_baud_rate)]
acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list)) acceptable_spacing_list = list(filter(lambda x : x[0]>baud_rate, spacing_list))
if len(acceptable_spacing_list) < 1: if len(acceptable_spacing_list) < 1:
#can't find an adequate spacing from the list, so default to: #can't find an adequate spacing from the list, so default to:
return baud_rate*1.2 return baud_rate*1.2
else: else:

View File

@@ -25,7 +25,7 @@ class ConvenienceAccess:
if abbrev in kwargs: if abbrev in kwargs:
kwargs[field] = kwargs.pop(abbrev) kwargs[field] = kwargs.pop(abbrev)
return self._replace(**kwargs) return self._replace(**kwargs)
#def ptot_dbm(self): #def ptot_dbm(self):
# p = array([c.power.signal+c.power.nli+c.power.ase for c in self.carriers]) # p = array([c.power.signal+c.power.nli+c.power.ase for c in self.carriers])
# return lin2db(sum(p*1e3)) # return lin2db(sum(p*1e3))
@@ -64,7 +64,7 @@ def create_input_spectral_information(f_min, roll_off, baud_rate, power, spacing
pref = lin2db(power * 1e3) pref = lin2db(power * 1e3)
si = SpectralInformation(pref=Pref(pref, pref)) si = SpectralInformation(pref=Pref(pref, pref))
si = si.update(carriers=[ si = si.update(carriers=[
Channel(f, (f_min+spacing*f), 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, 0)) for f in range(1,nb_channel+1)
]) ])
return si return si

View File

@@ -26,7 +26,7 @@ logger = getLogger(__name__)
def load_network(filename, equipment): def load_network(filename, equipment):
json_filename = '' json_filename = ''
if filename.suffix.lower() == '.xls': if filename.suffix.lower() == '.xls':
logger.info('Automatically generating topology JSON file') logger.info('Automatically generating topology JSON file')
json_filename = convert_file(filename) json_filename = convert_file(filename)
elif filename.suffix.lower() == '.json': elif filename.suffix.lower() == '.json':
json_filename = filename json_filename = filename
@@ -95,7 +95,7 @@ def select_edfa(gain_target, power_target, equipment):
power=min( power=min(
pin pin
+edfa.gain_flatmax +edfa.gain_flatmax
+TARGET_EXTENDED_GAIN, +TARGET_EXTENDED_GAIN,
edfa.p_max edfa.p_max
) )
-power_target, -power_target,
@@ -106,7 +106,7 @@ def select_edfa(gain_target, power_target, equipment):
acceptable_gain_list = \ acceptable_gain_list = \
list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list)) list(filter(lambda x : x.gain>-TARGET_EXTENDED_GAIN, edfa_list))
if len(acceptable_gain_list) < 1: if len(acceptable_gain_list) < 1:
#no amplifier satisfies the required gain, so pick the highest gain: #no amplifier satisfies the required gain, so pick the highest gain:
gain_max = max(edfa_list, key=itemgetter(2)).gain gain_max = max(edfa_list, key=itemgetter(2)).gain
#pick up all amplifiers that share this max gain: #pick up all amplifiers that share this max gain:
@@ -127,10 +127,10 @@ def select_edfa(gain_target, power_target, equipment):
def set_roadm_loss(network, equipment, pref_ch_db): def set_roadm_loss(network, equipment, pref_ch_db):
roadms = [roadm for roadm in network if isinstance(roadm, Roadm)] roadms = [roadm for roadm in network if isinstance(roadm, Roadm)]
power_mode = equipment['Spans']['default'].power_mode power_mode = equipment['Spans']['default'].power_mode
default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss default_roadm_loss = equipment['Roadms']['default'].gain_mode_default_loss
pref_roadm_db = equipment['Roadms']['default'].power_mode_pref pref_roadm_db = equipment['Roadms']['default'].power_mode_pref
roadm_loss = pref_ch_db - pref_roadm_db roadm_loss = pref_ch_db - pref_roadm_db
for roadm in roadms: for roadm in roadms:
if power_mode: if power_mode:
@@ -165,7 +165,7 @@ def target_power(dp_from_gain, network, node, equipment): #get_fiber_dp
#print(f'{repr(node)} delta power in:\n{dp}dB') #print(f'{repr(node)} delta power in:\n{dp}dB')
return dp return dp
def prev_node_generator(network, node): def prev_node_generator(network, node):
"""fused spans interest: """fused spans interest:
@@ -187,7 +187,7 @@ def next_node_generator(network, node):
yield next_node yield next_node
yield from next_node_generator(network, next_node) yield from next_node_generator(network, next_node)
else: else:
StopIteration StopIteration
def span_loss(network, node): def span_loss(network, node):
"""Fused span interest: """Fused span interest:
@@ -197,10 +197,10 @@ def span_loss(network, node):
prev_node = next(n for n in network.predecessors(node)) prev_node = next(n for n in network.predecessors(node))
if isinstance(prev_node, Fused): if isinstance(prev_node, Fused):
loss += sum(n.loss for n in prev_node_generator(network, node)) loss += sum(n.loss for n in prev_node_generator(network, node))
except StopIteration: except StopIteration:
pass pass
try: try:
next_node = next(n for n in network.successors(node)) next_node = next(n for n in network.successors(node))
if isinstance(next_node, Fused): if isinstance(next_node, Fused):
loss += sum(n.loss for n in next_node_generator(network, node)) loss += sum(n.loss for n in next_node_generator(network, node))
except StopIteration: except StopIteration:
@@ -209,7 +209,7 @@ def span_loss(network, node):
def find_first_node(network, node): def find_first_node(network, node):
"""Fused node interest: """Fused node interest:
returns the 1st node at the origin of a succession of fused nodes returns the 1st node at the origin of a succession of fused nodes
(aka no amp in between)""" (aka no amp in between)"""
this_node = node this_node = node
for this_node in prev_node_generator(network, node): for this_node in prev_node_generator(network, node):
@@ -218,7 +218,7 @@ def find_first_node(network, node):
def find_last_node(network, node): def find_last_node(network, node):
"""Fused node interest: """Fused node interest:
returns the last node in a succession of fused nodes returns the last node in a succession of fused nodes
(aka no amp in between)""" (aka no amp in between)"""
this_node = node this_node = node
for this_node in next_node_generator(network, node): for this_node in next_node_generator(network, node):
@@ -231,7 +231,7 @@ def set_amplifier_voa(amp, pref_total_db, power_mode):
if power_mode: if power_mode:
gain_target = amp.operational.gain_target gain_target = amp.operational.gain_target
pout = pref_total_db + amp.dp_db pout = pref_total_db + amp.dp_db
voa = min(amp.params.p_max-pout, voa = min(amp.params.p_max-pout,
amp.params.gain_flatmax-amp.operational.gain_target) amp.params.gain_flatmax-amp.operational.gain_target)
voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0 voa = round2float(max(voa, 0), 0.5) - VOA_MARGIN if amp.params.out_voa_auto else 0
amp.dp_db = amp.dp_db + voa amp.dp_db = amp.dp_db + voa
@@ -283,7 +283,7 @@ def set_egress_amplifier(network, roadm, equipment, pref_total_db):
def add_egress_amplifier(network, node): def add_egress_amplifier(network, node):
next_nodes = [n for n in network.successors(node) next_nodes = [n for n in network.successors(node)
if not (isinstance(n, Transceiver) or isinstance(n, Fused) or isinstance(n, Edfa))] if not (isinstance(n, Transceiver) or isinstance(n, Fused) or isinstance(n, Edfa))]
#no amplification for fused spans or TRX #no amplification for fused spans or TRX
for i, next_node in enumerate(next_nodes): for i, next_node in enumerate(next_nodes):
@@ -393,16 +393,16 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
padding = default_span_data.padding padding = default_span_data.padding
#set raodm loss for gain_mode before to build network #set raodm loss for gain_mode before to build network
set_roadm_loss(network, equipment, pref_ch_db) set_roadm_loss(network, equipment, pref_ch_db)
fibers = [f for f in network.nodes() if isinstance(f, Fiber)] fibers = [f for f in network.nodes() if isinstance(f, Fiber)]
add_connector_loss(fibers, con_in, con_out, default_span_data.EOL) add_connector_loss(fibers, con_in, con_out, default_span_data.EOL)
add_fiber_padding(network, fibers, padding) add_fiber_padding(network, fibers, padding)
# don't group split fiber and add amp in the same loop # don't group split fiber and add amp in the same loop
# =>for code clarity (at the expense of speed): # =>for code clarity (at the expense of speed):
for fiber in fibers: for fiber in fibers:
split_fiber(network, fiber, bounds, target_length, equipment) split_fiber(network, fiber, bounds, target_length, equipment)
amplified_nodes = [n for n in network.nodes() amplified_nodes = [n for n in network.nodes()
if isinstance(n, Fiber) or isinstance(n, Roadm)] if isinstance(n, Fiber) or isinstance(n, Roadm)]
for node in amplified_nodes: for node in amplified_nodes:
add_egress_amplifier(network, node) add_egress_amplifier(network, node)
@@ -411,9 +411,9 @@ def build_network(network, equipment, pref_ch_db, pref_total_db):
for roadm in roadms: for roadm in roadms:
set_egress_amplifier(network, roadm, equipment, pref_total_db) set_egress_amplifier(network, roadm, equipment, pref_total_db)
#support older json input topology wo Roadms: #support older json input topology wo Roadms:
if len(roadms) == 0: if len(roadms) == 0:
trx = [t for t in network.nodes() if isinstance(t, Transceiver)] trx = [t for t in network.nodes() if isinstance(t, Transceiver)]
for t in trx: for t in trx:
set_egress_amplifier(network, t, equipment, pref_total_db) set_egress_amplifier(network, t, equipment, pref_total_db)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# TelecomInfraProject/gnpy/examples # TelecomInfraProject/gnpy/examples
# Module name : path_requests_run.py # Module name : path_requests_run.py
# Version : # Version :
# License : BSD 3-Clause Licence # License : BSD 3-Clause Licence
# Copyright (c) 2018, Telecom Infra Project # Copyright (c) 2018, Telecom Infra Project
@@ -10,7 +10,7 @@
@author: jeanluc-auge @author: jeanluc-auge
read json request file in accordance with: read json request file in accordance with:
Yang model for requesting Path Computation Yang model for requesting Path Computation
draft-ietf-teas-yang-path-computation-01.txt. draft-ietf-teas-yang-path-computation-01.txt.
and returns path results in terms of path and feasibility and returns path results in terms of path and feasibility
""" """
@@ -76,8 +76,8 @@ class Result_element(Element):
self.computed_path = computed_path self.computed_path = computed_path
hop_type = [] hop_type = []
for e in computed_path : for e in computed_path :
if isinstance(e, Transceiver) : if isinstance(e, Transceiver) :
hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode])) hop_type.append(' - '.join([path_request.tsp,path_request.tsp_mode]))
else: else:
hop_type.append('not recorded') hop_type.append('not recorded')
self.hop_type = hop_type self.hop_type = hop_type
@@ -203,10 +203,10 @@ class Result_element(Element):
] ]
} }
} }
@property @property
def json(self): def json(self):
return self.pathresult return self.pathresult
def compute_constrained_path(network, req): def compute_constrained_path(network, req):
trx = [n for n in network.nodes() if isinstance(n, Transceiver)] trx = [n for n in network.nodes() if isinstance(n, Transceiver)]
@@ -226,7 +226,7 @@ def compute_constrained_path(network, req):
node = next(el for el in roadm if el.uid == f'roadm {n}') node = next(el for el in roadm if el.uid == f'roadm {n}')
except StopIteration: except StopIteration:
try: try:
node = next(el for el in edfa node = next(el for el in edfa
if el.uid.startswith(f'egress edfa in {n}')) if el.uid.startswith(f'egress edfa in {n}'))
except StopIteration: except StopIteration:
msg = f'could not find node : {n} in network topology: \ msg = f'could not find node : {n} in network topology: \
@@ -257,7 +257,7 @@ def compute_constrained_path(network, req):
# target=next(el for el in trx if el.uid == req.destination)): # target=next(el for el in trx if el.uid == req.destination)):
# print([e.uid for e in p if isinstance(e,Roadm)]) # print([e.uid for e in p if isinstance(e,Roadm)])
return total_path return total_path
def propagate(path, req, equipment, show=False): def propagate(path, req, equipment, show=False):
#update roadm loss in case of power sweep (power mode only) #update roadm loss in case of power sweep (power mode only)
@@ -269,13 +269,13 @@ def propagate(path, req, equipment, show=False):
si = el(si) si = el(si)
if show : if show :
print(el) print(el)
return path return path
def jsontocsv(json_data,equipment,fileout): def jsontocsv(json_data,equipment,fileout):
# read json path result file in accordance with: # read json path result file in accordance with:
# Yang model for requesting Path Computation # Yang model for requesting Path Computation
# draft-ietf-teas-yang-path-computation-01.txt. # draft-ietf-teas-yang-path-computation-01.txt.
# and write results in an CSV file # and write results in an CSV file
mywriter = writer(fileout) mywriter = writer(fileout)
@@ -290,32 +290,32 @@ def jsontocsv(json_data,equipment,fileout):
['path-route-object']['unnumbered-hop']['node-id'] ['path-route-object']['unnumbered-hop']['node-id']
destination = p['path-properties']['path-route-objects'][-1]\ destination = p['path-properties']['path-route-objects'][-1]\
['path-route-object']['unnumbered-hop']['node-id'] ['path-route-object']['unnumbered-hop']['node-id']
pth = ' | '.join([ e['path-route-object']['unnumbered-hop']['node-id'] 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']])
[tsp,mode] = p['path-properties']['path-route-objects'][0]\ [tsp,mode] = p['path-properties']['path-route-objects'][0]\
['path-route-object']['unnumbered-hop']['hop-type'].split(' - ') ['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) # find the min acceptable OSNR, baud rate from the eqpt library based on tsp (tupe) and mode (format)
try: try:
[minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']] [minosnr, baud_rate] = next([m['OSNR'] , m['baud_rate']]
for m in equipment['Transceiver'][tsp].mode if m['format']==mode) for m in equipment['Transceiver'][tsp].mode if m['format']==mode)
# for debug # for debug
# print(f'coucou {baud_rate}') # print(f'coucou {baud_rate}')
except IndexError: except IndexError:
msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library' msg = f'could not find tsp : {self.tsp} with mode: {self.tsp_mode} in eqpt library'
raise ValueError(msg) raise ValueError(msg)
output_snr = next(e['accumulative-value'] output_snr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm') for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@0.1nm')
output_snrbandwidth = next(e['accumulative-value'] output_snrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth') for e in p['path-properties']['path-metric'] if e['metric-type'] == 'SNR@bandwidth')
output_osnr = next(e['accumulative-value'] output_osnr = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm') for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@0.1nm')
output_osnrbandwidth = next(e['accumulative-value'] output_osnrbandwidth = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth') for e in p['path-properties']['path-metric'] if e['metric-type'] == 'OSNR@bandwidth')
power = next(e['accumulative-value'] power = next(e['accumulative-value']
for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power') for e in p['path-properties']['path-metric'] if e['metric-type'] == 'reference_power')
if isinstance(output_snr, str): if isinstance(output_snr, str):
isok = '' isok = ''
@@ -333,5 +333,5 @@ def jsontocsv(json_data,equipment,fileout):
output_osnr, output_osnr,
output_snrbandwidth, output_snrbandwidth,
output_snr, output_snr,
isok isok
)) ))

View File

@@ -61,10 +61,10 @@ def write_csv(obj, filename):
#main header #main header
w.writerow([data_key]) w.writerow([data_key])
#sub headers: #sub headers:
headers = [_ for _ in data_list[0].keys()] headers = [_ for _ in data_list[0].keys()]
w.writerow(headers) w.writerow(headers)
for data_dict in data_list: for data_dict in data_list:
w.writerow([_ for _ in data_dict.values()]) w.writerow([_ for _ in data_dict.values()])
def c(): def c():
""" """