mirror of
https://github.com/Telecominfraproject/oopt-gnpy.git
synced 2025-11-02 11:07:57 +00:00
add class RamanFiber
This commit is contained in:
@@ -417,6 +417,215 @@ class Fiber(Node):
|
|||||||
self.carriers_out = carriers
|
self.carriers_out = carriers
|
||||||
return spectral_info.update(carriers=carriers, pref=pref)
|
return spectral_info.update(carriers=carriers, pref=pref)
|
||||||
|
|
||||||
|
RamanFiberParams = namedtuple('RamanFiberParams', 'type_variety length loss_coef length_units \
|
||||||
|
att_in con_in con_out dispersion gamma')
|
||||||
|
|
||||||
|
class RamanFiber(Node):
|
||||||
|
def __init__(self, *args, params=None, **kwargs):
|
||||||
|
if params is None:
|
||||||
|
params = {}
|
||||||
|
if 'con_in' not in params:
|
||||||
|
# if not defined in the network json connector loss in/out
|
||||||
|
# the None value will be updated in network.py[build_network]
|
||||||
|
# with default values from eqpt_config.json[Spans]
|
||||||
|
params['con_in'] = None
|
||||||
|
params['con_out'] = None
|
||||||
|
if 'att_in' not in params:
|
||||||
|
#fixed attenuator for padding
|
||||||
|
params['att_in'] = 0
|
||||||
|
|
||||||
|
super().__init__(*args, params=RamanFiberParams(**params), **kwargs)
|
||||||
|
self.type_variety = self.params.type_variety
|
||||||
|
self.length = self.params.length * UNITS[self.params.length_units] # in m
|
||||||
|
self.loss_coef = self.params.loss_coef * 1e-3 # lineic loss dB/m
|
||||||
|
self.lin_loss_coef = self.params.loss_coef / (20 * log10(exp(1)))
|
||||||
|
self.att_in = self.params.att_in
|
||||||
|
self.con_in = self.params.con_in
|
||||||
|
self.con_out = self.params.con_out
|
||||||
|
self.dispersion = self.params.dispersion # s/m/m
|
||||||
|
self.gamma = self.params.gamma # 1/W/m
|
||||||
|
self.pch_out_db = None
|
||||||
|
self.carriers_in = None
|
||||||
|
self.carriers_out = None
|
||||||
|
# TODO|jla: discuss factor 2 in the linear lineic attenuation
|
||||||
|
|
||||||
|
@property
|
||||||
|
def to_json(self):
|
||||||
|
return {'uid' : self.uid,
|
||||||
|
'type' : type(self).__name__,
|
||||||
|
'type_variety' : self.type_variety,
|
||||||
|
'params' : {
|
||||||
|
#have to specify each because namedtupple cannot be updated :(
|
||||||
|
'type_variety' : self.type_variety,
|
||||||
|
'length' : self.length/UNITS[self.params.length_units],
|
||||||
|
'loss_coef' : self.loss_coef*1e3,
|
||||||
|
'length_units' : self.params.length_units,
|
||||||
|
'att_in' : self.att_in,
|
||||||
|
'con_in' : self.con_in,
|
||||||
|
'con_out' : self.con_out
|
||||||
|
},
|
||||||
|
'metadata' : {
|
||||||
|
'location': self.metadata['location']._asdict()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'{type(self).__name__}(uid={self.uid!r}, length={round(self.length*1e-3,1)!r}km, loss={round(self.loss,1)!r}dB)'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '\n'.join([f'{type(self).__name__} {self.uid}',
|
||||||
|
f' type_variety: {self.type_variety}',
|
||||||
|
f' length (km): {round(self.length*1e-3):.2f}',
|
||||||
|
f' pad att_in (dB): {self.att_in:.2f}',
|
||||||
|
f' total loss (dB): {self.loss:.2f}',
|
||||||
|
f' (includes conn loss (dB) in: {self.con_in:.2f} out: {self.con_out:.2f})',
|
||||||
|
f' (conn loss out includes EOL margin defined in eqpt_config.json)',
|
||||||
|
f' pch out (dBm): {self.pch_out_db!r}'])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fiber_loss(self):
|
||||||
|
# dB fiber loss, not including padding attenuator
|
||||||
|
return self.loss_coef * self.length + self.con_in + self.con_out
|
||||||
|
|
||||||
|
@property
|
||||||
|
def loss(self):
|
||||||
|
#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
|
||||||
|
|
||||||
|
@property
|
||||||
|
def passive(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lin_attenuation(self):
|
||||||
|
return db2lin(self.length * self.loss_coef)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def effective_length(self):
|
||||||
|
_, alpha = self.dbkm_2_lin()
|
||||||
|
leff = (1 - exp(-2 * alpha * self.length)) / (2 * alpha)
|
||||||
|
return leff
|
||||||
|
|
||||||
|
@property
|
||||||
|
def asymptotic_length(self):
|
||||||
|
_, alpha = self.dbkm_2_lin()
|
||||||
|
aleff = 1 / (2 * alpha)
|
||||||
|
return aleff
|
||||||
|
|
||||||
|
def carriers(self, loc, attr):
|
||||||
|
"""retrieve carriers information
|
||||||
|
loc = (in, out) of the class element
|
||||||
|
attr = (ase, nli, signal, total) power information"""
|
||||||
|
if not (loc in ('in', 'out') and attr in ('nli', 'signal', 'total', 'ase')):
|
||||||
|
yield None
|
||||||
|
return
|
||||||
|
power_dict = {
|
||||||
|
'nli': 'nonlinear_interference',
|
||||||
|
'ase': 'amplified_spontaneous_emission'
|
||||||
|
}
|
||||||
|
attr = power_dict.get(attr, attr)
|
||||||
|
loc_attr = 'carriers_'+loc
|
||||||
|
for c in getattr(self, loc_attr) :
|
||||||
|
if attr == 'total':
|
||||||
|
yield c.power.ase+c.power.nli+c.power.signal
|
||||||
|
else:
|
||||||
|
yield c.power._asdict().get(attr, None)
|
||||||
|
|
||||||
|
def beta2(self, ref_wavelength=None):
|
||||||
|
""" Returns beta2 from dispersion parameter.
|
||||||
|
Dispersion is entered in ps/nm/km.
|
||||||
|
Disperion can be a numpy array or a single value. If a
|
||||||
|
value ref_wavelength is not entered 1550e-9m will be assumed.
|
||||||
|
ref_wavelength can be a numpy array.
|
||||||
|
"""
|
||||||
|
# TODO|jla: discuss beta2 as method or attribute
|
||||||
|
wl = 1550e-9 if ref_wavelength is None else ref_wavelength
|
||||||
|
D = abs(self.dispersion)
|
||||||
|
b2 = (wl ** 2) * D / (2 * pi * c) # 10^21 scales [ps^2/km]
|
||||||
|
return b2 # s/Hz/m
|
||||||
|
|
||||||
|
def dbkm_2_lin(self):
|
||||||
|
""" calculates the linear loss coefficient
|
||||||
|
"""
|
||||||
|
# alpha_pcoef is linear loss coefficient in dB/km^-1
|
||||||
|
# alpha_acoef is linear loss field amplitude coefficient in m^-1
|
||||||
|
alpha_pcoef = self.loss_coef
|
||||||
|
alpha_acoef = alpha_pcoef / (2 * 10 * log10(exp(1)))
|
||||||
|
return alpha_pcoef, alpha_acoef
|
||||||
|
|
||||||
|
def _psi(self, carrier, interfering_carrier):
|
||||||
|
""" Calculates eq. 123 from arXiv:1209.0394.
|
||||||
|
"""
|
||||||
|
if carrier.num_chan == interfering_carrier.num_chan: # SCI
|
||||||
|
psi = arcsinh(0.5 * pi**2 * self.asymptotic_length
|
||||||
|
* abs(self.beta2()) * carrier.baud_rate**2)
|
||||||
|
else: # XCI
|
||||||
|
delta_f = carrier.freq - interfering_carrier.freq
|
||||||
|
psi = arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||||
|
* carrier.baud_rate * (delta_f + 0.5 * interfering_carrier.baud_rate))
|
||||||
|
psi -= arcsinh(pi**2 * self.asymptotic_length * abs(self.beta2())
|
||||||
|
* carrier.baud_rate * (delta_f - 0.5 * interfering_carrier.baud_rate))
|
||||||
|
|
||||||
|
return psi
|
||||||
|
|
||||||
|
def _gn_analytic(self, carrier, *carriers):
|
||||||
|
""" Computes the nonlinear interference power on a single carrier.
|
||||||
|
The method uses eq. 120 from arXiv:1209.0394.
|
||||||
|
:param carrier: the signal under analysis
|
||||||
|
:param carriers: the full WDM comb
|
||||||
|
:return: carrier_nli: the amount of nonlinear interference in W on the under analysis
|
||||||
|
"""
|
||||||
|
|
||||||
|
g_nli = 0
|
||||||
|
for interfering_carrier in carriers:
|
||||||
|
psi = self._psi(carrier, interfering_carrier)
|
||||||
|
g_nli += (interfering_carrier.power.signal/interfering_carrier.baud_rate)**2 \
|
||||||
|
* (carrier.power.signal/carrier.baud_rate) * psi
|
||||||
|
|
||||||
|
g_nli *= (16 / 27) * (self.gamma * self.effective_length)**2 \
|
||||||
|
/ (2 * pi * abs(self.beta2()) * self.asymptotic_length)
|
||||||
|
|
||||||
|
carrier_nli = carrier.baud_rate * g_nli
|
||||||
|
return carrier_nli
|
||||||
|
|
||||||
|
def propagate(self, *carriers):
|
||||||
|
|
||||||
|
# apply connector_att_in on all carriers before computing gn analytics premiere partie pas bonne
|
||||||
|
attenuation = db2lin(self.con_in + self.att_in)
|
||||||
|
|
||||||
|
chan = []
|
||||||
|
for carrier in carriers:
|
||||||
|
pwr = carrier.power
|
||||||
|
pwr = pwr._replace(signal=pwr.signal/attenuation,
|
||||||
|
nonlinear_interference=pwr.nli/attenuation,
|
||||||
|
amplified_spontaneous_emission=pwr.ase/attenuation)
|
||||||
|
carrier = carrier._replace(power=pwr)
|
||||||
|
chan.append(carrier)
|
||||||
|
|
||||||
|
carriers = tuple(f for f in chan)
|
||||||
|
|
||||||
|
# propagate in the fiber and apply attenuation out
|
||||||
|
attenuation = db2lin(self.con_out)
|
||||||
|
for carrier in carriers:
|
||||||
|
pwr = carrier.power
|
||||||
|
carrier_nli = self._gn_analytic(carrier, *carriers)
|
||||||
|
pwr = pwr._replace(signal=pwr.signal/self.lin_attenuation/attenuation,
|
||||||
|
nonlinear_interference=(pwr.nli+carrier_nli)/self.lin_attenuation/attenuation,
|
||||||
|
amplified_spontaneous_emission=pwr.ase/self.lin_attenuation/attenuation)
|
||||||
|
yield carrier._replace(power=pwr)
|
||||||
|
|
||||||
|
def update_pref(self, pref):
|
||||||
|
self.pch_out_db = round(pref.pi - self.loss, 2)
|
||||||
|
return pref._replace(p_span0=pref.p0, p_spani=self.pch_out_db)
|
||||||
|
|
||||||
|
def __call__(self, spectral_info):
|
||||||
|
self.carriers_in = spectral_info.carriers
|
||||||
|
carriers = tuple(self.propagate(*spectral_info.carriers))
|
||||||
|
pref = self.update_pref(spectral_info.pref)
|
||||||
|
self.carriers_out = carriers
|
||||||
|
return spectral_info.update(carriers=carriers, pref=pref)
|
||||||
|
|
||||||
|
|
||||||
class EdfaParams:
|
class EdfaParams:
|
||||||
def __init__(self, **params):
|
def __init__(self, **params):
|
||||||
self.update_params(params)
|
self.update_params(params)
|
||||||
|
|||||||
Reference in New Issue
Block a user