def nu(self) -> Array[float]: nu = np.zeros((self._nbr_channels, self._samples)) if (self._nbr_channels): omega = self.omega for i in range(omega.shape[0]): nu[i] = Domain.omega_to_nu(omega[i]) return nu
def __call__(self, domain: Domain) -> Tuple[List[int], List[Field]]: output_ports: List[int] = [] output_fields: List[Field] = [] field: Field = Field(domain, cst.OPTI, self.field_name) # Bit rate initialization -------------------------------------- rel_pos: List[np.ndarray] rel_pos = util.pulse_positions_in_time_window(self.channels, self.rep_freq, domain.time_window, self.position) # Check offset ------------------------------------------------- for i in range(len(self.offset_nu)): if (abs(self.offset_nu[i]) > domain.nu_window): self.offset_nu[i] = 0.0 util.warning_terminal( "The offset of channel {} in component " "{} is bigger than half the frequency window, offset will " "be ignored.".format(str(i), self.name)) # Field initialization ----------------------------------------- width: float for i in range(self.channels): # Nbr of channels if (self.fwhm is None): width = self.width[i] else: width = self.fwhm_to_width(self.fwhm[i]) res: np.ndarray = np.zeros(domain.time.shape, dtype=cst.NPFT) for j in range(len(rel_pos[i])): norm_time = domain.get_shift_time(rel_pos[i][j]) / width var_time = np.power(norm_time, 2) phi = (self.init_phi[i] - Domain.nu_to_omega(self.offset_nu[i]) * domain.time - 0.5 * self.chirp[i] * var_time) res += (math.sqrt(self.peak_power[i]) / np.cosh(norm_time) * np.exp(1j * phi)) field.add_channel(res, Domain.lambda_to_omega(self.center_lambda[i]), self.rep_freq[i]) if (self.noise is not None): field.noise = self.noise output_fields.append(field) output_ports.append(0) return output_ports, output_fields
def calc_nl_index(omega, factor, coefficients): r"""Calculate the non linear index by help of fitting formula. The non linear is assumed to be the sum of the electronic and Raman contributions at zero frequency shift. [10]_ Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` factor : Number which will multiply the fitting formula. coefficients : Coefficients of the fitting formula. [(coeff, expo), ..] Returns ------- : Value of the non linear index. :math:`[m^2\cdot W^{-1}]` Notes ----- Considering: .. math:: n_2(\lambda) = 1.000055 (8.30608 - 27.79971\lambda + 59.66014\lambda^2 - 69.24258\lambda^3 + 45.22437\lambda^4 - 15.63666\lambda^5 + 2.22585\lambda^6) with :math:`\lambda` in :math:`nm` and return with factor :math:`10^{-20}` References ---------- .. [10] Salceda-Delgado, G., Martinez-Rios, A., Ilan, B. and Monzon-Hernandez, D., 2012. Raman response function an Raman fraction of phosphosilicate fibers. Optical and Quantum Electronics, 44(14), pp.657-671. """ if (isinstance(omega, float)): res = 0.0 else: res = np.zeros_like(omega) Lambda = Domain.omega_to_lambda(omega) Lambda *= 1e-3 # nm -> um if (isinstance(omega, float)): for elem in coefficients: res += elem[0] * Lambda**elem[1] else: for elem in coefficients: res += elem[0] * np.power(Lambda, elem[1]) res *= factor return res * 1e-20 # from fitting formula to m^2 w^-1
def n(self, omega, n_0, N_1): r"""Compute the resonant index change. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` Returns ------- : The refractive index change. Notes ----- .. math:: \Delta n = Q N_1 S where: .. math:: S = -f_{12}\lambda_{12}g'_{12}(\lambda_s) \big(1 + \frac{g_1}{g_2}\big) - \sum_{j>2} f_{1j} \lambda_{1j}g'_{1j}(\lambda_s) + \sum_{j>2} f_{2j}\lambda_{2j}g'_{2j}(\lambda_s) """ if (isinstance(omega, float)): gsa = 0.0 esa = 0.0 res = 0.0 else: gsa = np.zeros_like(omega) esa = np.zeros_like(omega) res = np.zeros_like(omega) lambda_s = Domain.omega_to_lambda(omega) main_trans = (self._main_trans[0] * self._main_trans[1] * self.lorentzian_lineshape( lambda_s, self._main_trans[1], self._main_trans[2]) * (1 + (self._gs[0] / self._gs[1]))) # Need to verify for the lambda bw to use here # should not change a lot -> very small term for uv_gsa in self._gsa[1]: gsa += (self._gsa[0] * uv_gsa * self.lorentzian_lineshape( lambda_s, uv_gsa, self._main_trans[2])) for uv_esa in self._esa[1]: esa += (self._esa[0] * uv_esa * self.lorentzian_lineshape( lambda_s, uv_esa, self._main_trans[2])) res = self.calc_Q(n_0) * N_1 * (esa - gsa - main_trans) return res
def calc_ref_index(omega, As, lambdas, dopant_slope=0., dopant_concent=0.): r"""Compute the Sellmeier equations. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` As : The A coefficients for the sellmeier equations. lambdas : The wavelength coefficients for the sellmeier equations. dopant_slope : The slope coefficient of the dopant linear fitting. dopant_concent : The concentration of the dopant. [mole%] Returns ------- : The refractive index. Notes ----- .. math:: n^2(\lambda) = 1 + \sum_i A_i \frac{\lambda^2}{\lambda^2 - \lambda_i} If dopant is specified, use linear fitting parameters from empirical data at 1300 nm. .. math:: n_{new}(\lambda, d) = n(\lambda) + a d where :math:`a` is the slope of the linear fitting and :math:`d` is the dopant concentration. """ Lambda = Domain.omega_to_lambda(omega) # nm # Lambda in micrometer in formula Lambda = Lambda * 1e-3 # um if (isinstance(Lambda, float)): res = 1.0 for i in range(len(As)): res += (As[i] * Lambda**2) / (Lambda**2 - lambdas[i]**2) res = math.sqrt(res) else: # numpy.ndarray res = np.ones(Lambda.shape) for i in range(len(As)): res += (As[i] * Lambda**2) / (Lambda**2 - lambdas[i]**2) res = np.sqrt(res) res = res + dopant_slope * dopant_concent return res
def test_same_omega(operator_fixture, op, right_op): """Should not fail if the center omega are the same and in the same order.""" length = 12 field = Field(Domain(samples_per_bit=length), type_op_test) center_omegas = [1030., 1025., 1020.] rep_freqs = [1., 2., 3.] channels = [np.ones(length) * (i + 1) for i in range(len(center_omegas))] for i in range(len(center_omegas)): field.add_channel(channels[i], center_omegas[i], rep_freqs[i]) operands = [field] res = operator_fixture(op, right_op, channels, type_op_test, center_omegas, rep_freqs, length, *operands)
def fct(op, right_op, field_channel, type, center_omega, rep_freq, samples, *operands): res = [] for operand in operands: new_field = Field(Domain(samples_per_bit=samples), type) for i, channel in enumerate(field_channel): new_field.add_channel(channel, center_omega[i], rep_freq[i]) if (right_op): # Right operand operator res.append(op(operand, new_field)) else: # Left operand operator res.append(op(new_field, operand)) return res
def layout(starter, ATT, DISP, SPM, SS, RS, approx): domain = Domain() lt = Layout(domain) dtime = domain.dtime domega = domain.domega out_temporal_power = [] out_spectral_power = [] out_temporal_fwhm = [] out_spectral_fwhm = [] nlse_method = "ssfm_symmetric" flag = True till_nbr = 4 if approx else 5 for i in range(1, till_nbr): if (i == 4): flag = False fiber = Fiber(length=1.0, nlse_method=nlse_method, alpha=[0.046], nl_approx=flag, ATT=ATT, DISP=DISP, SPM=SPM, SS=SS, RS=RS, steps=1000, save=True, approx_type=i) lt.add_link(starter[0], fiber[0]) lt.run(starter) lt.reset() out_temporal_power.append(temporal_power(fiber[1][0].channels)) out_spectral_power.append(spectral_power(fiber[1][0].channels)) out_temporal_fwhm.append(fwhm(out_temporal_power[-1], dtime)) out_spectral_fwhm.append(fwhm(out_spectral_power[-1], domega)) in_temporal_power = temporal_power(starter[0][0].channels) in_spectral_power = spectral_power(starter[0][0].channels) in_temporal_fwhm = fwhm(in_temporal_power, dtime) in_spectral_fwhm = fwhm(in_spectral_power, domega) return (in_temporal_power, in_spectral_power, in_temporal_fwhm, in_spectral_fwhm, out_temporal_power, out_spectral_power, out_temporal_fwhm, out_spectral_fwhm)
def test_no_common_omegas(operator_fixture, op, right_op): """Should not perform math operators if the center omegas are different.""" length = 12 field = Field(Domain(samples_per_bit=length), type_op_test) center_omegas = [1030., 1025., 1020.] rep_freqs = [1., 2., 3.] channels = [np.ones(length) * (i + 1) for i in range(len(center_omegas))] for i in range(len(center_omegas)): field.add_channel(channels[i], center_omegas[i], rep_freqs[i]) operands = [field] center_omegas = [1029., 1021.] channels = [np.ones(length) * (i + 1) for i in range(len(center_omegas))] rep_freqs = [2., 3.] res = operator_fixture(op, right_op, channels, type_op_test, center_omegas, rep_freqs, length, *operands) field_res = res[0] if (len(field_res) == len(field)): assert np.array_equal(field_res[:], field[:]) else: assert np.array_equal(field_res[:], np.asarray(channels))
def layout(nbr_channels_seed, peak_powers_seed, center_lambdas_seed, nbr_channels_pump, peak_powers_pump, center_lambdas_pump, REFL_SEED, REFL_PUMP, NOISE): gssn = Gaussian(channels=nbr_channels_seed, peak_power=peak_powers_seed, center_lambda=center_lambdas_seed) pump = CW(channels=nbr_channels_pump, peak_power=peak_powers_pump, center_lambda=center_lambdas_pump) fiber = FiberYb(length=0.01, steps=10, REFL_SEED=REFL_SEED, REFL_PUMP=REFL_PUMP, BISEED=False, BIPUMP=False, save_all=True, alpha=[0.05], max_nbr_iter=2, NOISE=NOISE) lt = Layout(Domain(samples_per_bit=64, noise_samples=10)) lt.add_unidir_links((gssn[0], fiber[0]), (pump[0], fiber[2])) lt.run_all() return fiber.storages[0]
def calc_cross_section(omega, dopant="Yb", predict=None): r"""Calculate the absorption cross section. Calculate with the fitting formula from ref. [4]_ . Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` dopant : The doping agent. predict : A callable object to predict the cross sections. The variable must be the wavelength. :math:`[nm]` Returns ------- float or numpy.ndarray of float Value of the absorption cross section. :math:`[nm^2]` Notes ----- Considering: .. math:: f(\lambda, \lambda_c, \lambda_w, n) = \exp\Big[-\Big\lvert \frac{\lambda-\lambda_c}{\lambda_w}\Big\rvert^n \Big] With :math:`\lambda`, :math:`\lambda_c` and :math:`\lambda_w` in :math:`[nm]` For Ytterbium: .. math:: \sigma_a^{Yb}(\lambda) = 0.09f(\lambda,913,8,2) + 0.13 f(\lambda,950,40,4) + 0.2 f(\lambda, 968, 40, 2.4) + 1.08 f(\lambda, 975.8,3,1.5) For Erbium: .. math:: \begin{split} \sigma_a^{Er}(\lambda) &= 0.221 f(\lambda, 1493, 16.5, 2.2) + 0.342 f(\lambda, 1534, 4.5, 1.4)\\ &\quad + 0.158f(\lambda, 1534, 30,4) + 0.037 f(\lambda, 1534, 85, 4) + 0.132 f(\lambda, 1541, 8,0.8) \end{split} References ---------- .. [4] Valley, G.C., 2001. Modeling cladding-pumped Er/Yb fiber amplifiers. Optical Fiber Technology, 7(1), pp.21-44. """ Lambda = Domain.omega_to_lambda(omega) if (predict is None): dopant = dopant.lower() if (dopant in dopants): if (isinstance(Lambda, float)): res = 0.0 if (Lambda < dopant_range[dopant][1] and Lambda > dopant_range[dopant][0]): res = Absorption._formula(Lambda, dopant_fit_coeff[dopant]) else: Lambda_down = Lambda[Lambda < dopant_range[dopant][0]] Lambda_up = Lambda[Lambda > dopant_range[dopant][1]] if (len(Lambda_up)): Lambda_in = Lambda[len(Lambda_down):-len(Lambda_up)] else: Lambda_in = Lambda[len(Lambda_down):] res = np.array([]) if (Lambda_in.size): res = Absorption._formula(Lambda_in, dopant_fit_coeff[dopant]) res = np.hstack((np.zeros_like(Lambda_down), res, np.zeros_like(Lambda_up))) res *= 1e-6 # 10^-20 cm^2 -> nm^2 else: util.warning_terminal("The specified doped material is not " "supported, return 0") else: # Interpolate the data in csv file with scipy fct res = predict(Lambda)[0] # Take only zeroth deriv. return res
nlse2 = NLSE(alpha=[0.0], beta=[0.0, 0.0]) nlse3 = NLSE(alpha=[0.0], beta=[0.0, 0.0]) nlse4 = NLSE(alpha=[0.0], beta=[0.0, 0.0]) nlse5 = NLSE(alpha=[0.0], beta=[0.0, 0.0]) eqs = [nlse1, nlse2, nlse3, nlse4, nlse5] method = [ 'ssfm', 'ssfm_super_sym', 'ssfm_symmetric', 'ssfm_reduced', 'ssfm' ] length = 1.0 steps = [10, 9, 8, 7, 6, 5] step_method = [FIXED, ADAPTATIVE, FIXED] solver_sequence = [0, 0, 2, 2, 1] solver_order = ALTERNATING stepper_method = [FORWARD] stepper = Stepper(eqs=eqs, method=method, length=length, steps=steps, step_method=step_method, solver_sequence=solver_sequence, solver_order=solver_order, stepper_method=stepper_method) dummy_domain = Domain() ggsn = Gaussian(channels=4) dummy_ports, dummy_field = ggsn(dummy_domain) res = stepper(dummy_domain, dummy_field)
from optcom.domain import Domain from optcom.utils.utilities_user import CSVFit folder = './data/fiber_amp/cross_section/absorption/' files = ['yb'] file_range = [(900.0, 1050.0)] x_data = [] y_data = [] plot_titles = [] for dopant in dopants: nbr_samples = 1000 lambdas = np.linspace(dopant_range[dopant][0], dopant_range[dopant][1], nbr_samples) omegas = Domain.omega_to_lambda(lambdas) absorp = Absorption(dopant=dopant) sigmas = absorp.get_cross_section(omegas) x_data.append(lambdas) y_data.append(sigmas) dopant_name = dopant[0].upper() + dopant[1:] plot_titles.append( 'Cross sections {} from formula'.format(dopant_name)) for i, file in enumerate(files): nbr_samples = 1000 lambdas = np.linspace(file_range[i][0], file_range[i][1], nbr_samples) omegas = Domain.omega_to_lambda(lambdas) file_name = folder + file + '.txt' predict = CSVFit(file_name=file_name, conv_factor=[1e9, 1e18]) # in nm absorp = Absorption(predict=predict)
res = math.sqrt(res) else: # numpy.ndarray res = np.ones(Lambda.shape) for i in range(len(self._Bs)): res += (self._Bs[i]*Lambda**2) / (Lambda**2 - self._Cs[i]) res = np.sqrt(res) return res if __name__ == "__main__": import optcom.utils.plot as plot from optcom.domain import Domain sellmeier = Sellmeier("sio2") center_omega = Domain.lambda_to_omega(1050.0) print(sellmeier.n(center_omega)) Lambda = np.linspace(120, 2120, 2000) omega = Domain.lambda_to_omega(Lambda) n = sellmeier.n(omega) x_labels = ['Lambda'] y_labels = ['Refractive index'] plot_titles = ["Refractive index of Silica from Sellmeier equations"] plot.plot2d(Lambda, n, x_labels=x_labels, y_labels=y_labels, plot_titles=plot_titles, opacity=0.0)
return Dispersion.calc_dispersion(beta_2, Lambda) * length if __name__ == "__main__": import optcom.utils.plot as plot from optcom.domain import Domain from optcom.equations.sellmeier import Sellmeier center_omega = 1929.97086814 #Domain.lambda_to_omega(976.0) sellmeier = Sellmeier("Sio2") betas = Dispersion.calc_beta_coeffs(center_omega, 13, sellmeier) print('betas: ', betas) Lambda = np.linspace(900, 1600, 1000) omega = Domain.lambda_to_omega(Lambda) beta_2 = Dispersion.calc_beta_deriv(omega, 2, "SiO2") x_data = [Lambda] y_data = [beta_2] x_labels = ['Lambda'] y_labels = ['beta2'] plot_titles = ["Group velocity dispersion coefficients in Silica"] disp = Dispersion.calc_dispersion(Lambda, beta_2) x_data.append(Lambda) y_data.append(disp) x_labels.append('Lambda') y_labels.append('dispersion') plot_titles.append('Dispersion of Silica')
def calc_kappa(omega, v_nbr, a, d, ref_index): r"""Calculate the coupling coefficient for the parameters given for two waveguides. (assuming the two waveguides are symmetric) [3]_ Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` v_nbr : The fiber parameter. a : The core radius. :math:`[\mu m]` d : The center to center spacing between the two cores. :math:`[\mu m]` ref_index : The refractive index outside of the two fiber cores. Returns ------- : Value of the coupling coefficient. :math:`[km^{-1}]` Notes ----- .. math:: \kappa = \frac{\pi V}{2 k_0 n_0 a^2} e^{-(c_0 + c_1 d + c_2 d^2)} This equation is accurate to within 1% for values of the fiber parameter :math:`1.5\leq V\leq 2.5` and of the normalized center-to-center spacing :math:`\bar{d} = d/a`, :math:`2\leq \bar{d}\leq 4.5` . References ---------- .. [3] Govind Agrawal, Chapter 2: Fibers Couplers, Applications of Nonlinear Fiber Optics (Second Edition), Academic Press, 2008, Page 59. """ lambda_0 = Domain.omega_to_lambda(omega) a *= 1e-9 # um -> km d *= 1e-9 # um -> km lambda_0 *= 1e-12 # nm -> km norm_d = d / a k_0 = 2 * cst.PI / lambda_0 c_0 = 5.2789 - 3.663 * v_nbr + 0.3841 * v_nbr**2 c_1 = -0.7769 + 1.2252 * v_nbr - 0.0152 * v_nbr**2 c_2 = -0.0175 - 0.0064 * v_nbr - 0.0009 * v_nbr**2 # Check validity of formula paramater -------------------------- if (np.mean(v_nbr) < 1.5 or np.mean(v_nbr) > 2.5): util.warning_terminal( "Value of the fiber parameter is V = {}, " "kappa fitting formula is valid only for : 1.5 <= V <= 2.5, " "might lead to unrealistic results.".format(np.mean(v_nbr))) if (norm_d < 2.0 or norm_d > 4.5): util.warning_terminal( "Value of the normalized spacing is d = {}, " "kappa fitting formula is valid only for : 2.0 <= d <= 4.5, " "might lead to unrealistic results.".format(norm_d)) # Formula ------------------------------------------------------ if (isinstance(omega, float)): res = (cst.PI * v_nbr / (2 * k_0 * ref_index * a**2) * math.exp(-(c_0 + c_1 * norm_d + c_2 * norm_d**2))) else: res = (cst.PI * v_nbr / (2 * k_0 * ref_index * a**2) * np.exp(-(c_0 + c_1 * norm_d + c_2 * norm_d**2))) return res
return res return fct # ---------------------------------------------------------------------- # Tests ---------------------------------------------------------------- # ---------------------------------------------------------------------- scale = 2 length = 12 type_op_test = 1 center_omega_op_test = [1550.] rep_freq_op_test = [1e3] field_ = Field(Domain(samples_per_bit=length), type_op_test) field_.add_channel(scale * np.ones(length), center_omega_op_test[0], rep_freq_op_test[0]) operand_args = [ int(scale), float(scale), complex(scale), scale * np.ones(length), field_ ] @pytest.mark.field_op @pytest.mark.parametrize("op, field_channel, op_res, operands", [ (operator.__iadd__, [np.arange(1, length + 1)], np.array([np.arange(1, length + 1) + (scale * np.ones(length))]), operand_args), (operator.__isub__, [np.arange(1, length + 1)],
def calc_sigma(omega, coefficients, lambda_range=None): r"""Calculate the absorption cross section. Calculate with the fitting formula from ref. [1]_ . Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` coefficients : The coefficients for the fitting formula. :math:`[\text{factor}, l_c, l_w, n]` lambda_range : The range of wavelength in which the fitting formula is valid. (lower_bound, upper_bound) Returns ------- float or numpy.ndarray of float Value of the absorption cross section. :math:`[nm^2]` Notes ----- Considering: .. math:: f(\lambda, \lambda_c, \lambda_w, n) = \exp\Big[-\Big\lvert \frac{\lambda-\lambda_c}{\lambda_w}\Big\rvert^n \Big] With :math:`\lambda`, :math:`\lambda_c` and :math:`\lambda_w` in :math:`[nm]` For Ytterbium: .. math:: \sigma^{Yb}(\lambda) = 0.09f(\lambda,913,8,2) + 0.13 f(\lambda,950,40,4) + 0.2 f(\lambda, 968, 40, 2.4) + 1.08 f(\lambda, 975.8,3,1.5) For Erbium: .. math:: \begin{split} \sigma^{Er}(\lambda) &= 0.221 f(\lambda, 1493, 16.5, 2.2) + 0.342 f(\lambda, 1534, 4.5, 1.4)\\ &\quad + 0.158f(\lambda, 1534, 30,4) + 0.037 f(\lambda, 1534, 85, 4) + 0.132 f(\lambda, 1541, 8,0.8) \end{split} References ---------- .. [1] Valley, G.C., 2001. Modeling cladding-pumped Er/Yb fiber amplifiers. Optical Fiber Technology, 7(1), pp.21-44. """ Lambda = Domain.omega_to_lambda(omega) res = 0.0 if isinstance(Lambda, float) else np.zeros_like(Lambda) if (isinstance(Lambda, float)): if (lambda_range is None): res = AbsorptionSection._formula(Lambda, coefficients) else: if (Lambda < lambda_range[1] and Lambda > lambda_range[0]): res = AbsorptionSection._formula(Lambda, coefficients) else: if (lambda_range is not None): Lambda_down = Lambda[Lambda < lambda_range[0]] Lambda_up = Lambda[Lambda > lambda_range[1]] if (len(Lambda_up)): Lambda_in = Lambda[len(Lambda_down):-len(Lambda_up)] else: Lambda_in = Lambda[len(Lambda_down):] res = np.array([]) if (Lambda_in.size): res = AbsorptionSection._formula(Lambda_in, coefficients) res = np.hstack((np.zeros_like(Lambda_down), res, np.zeros_like(Lambda_up))) else: res = AbsorptionSection._formula(Lambda, coefficients) res *= 1e-6 # 10^-20 cm^2 -> nm^2 return res
output_fields.append(field) output_ports.append(0) return output_ports, output_fields if __name__ == "__main__": import optcom.utils.plot as plot import optcom.layout as layout import optcom.domain as domain from optcom.utils.utilities_user import temporal_power, spectral_power,\ phase lt = layout.Layout(Domain(samples_per_bit=4096)) channels = 3 center_lambda = [1552.0, 1549.0, 1596.0] peak_power = [1e-3, 2e-3, 6e-3] offset_nu = [0.0, 1.56, -1.6] init_phi = [1.0, 1.0, 0.0] cw = CW(channels=channels, center_lambda=center_lambda, peak_power=peak_power, offset_nu=offset_nu, init_phi=init_phi, save=True) lt.run(cw)
import optcom.utils.constants as cst import optcom.utils.plot as plot from optcom.domain import Domain from optcom.utils.fft import FFT samples = 1000 time, dtime = np.linspace(0.0, 0.3, samples, False, True) # ps freq_window = 1.0 / dtime x_data = [] y_data = [] plot_labels = [] f_R: float = cst.F_R n_0: float = 1.40 center_omega = Domain.lambda_to_omega(1550.0) f_a: float = cst.F_A f_b: float = cst.F_B f_c: float = cst.F_C x_data.append(time) h_R = Raman.calc_h_R(time, f_a=f_a, f_b=f_b, f_c=f_c) y_data.append(h_R) plot_labels.append('Isotropic and anisotropic part') f_a = 1.0 f_b = 0.0 f_c = 1.0 x_data.append(time) h_R = Raman.calc_h_R(time, f_a=f_a, f_b=f_b, f_c=f_c) y_data.append(h_R) plot_labels.append('W/o anisotropic part')
def __call__(self, domain): return ([4], [Field(Domain(), 1)])
# dopant_range = {dopant:(bottom, up), \ldots} # in nm dopant_range = {"yb": (900, 1050), "er": (1400, 1650)} folder = './data/fiber_amp/cross_section/emission/' files = ['yb'] file_range = [(900.0, 1050.0)] x_data = [] y_data = [] plot_titles = [] for dopant in dopants: nbr_samples = 1000 lambdas = np.linspace(dopant_range[dopant][0], dopant_range[dopant][1], nbr_samples) omegas = Domain.lambda_to_omega(lambdas) center_lambda = (dopant_range[dopant][0] + dopant_range[dopant][1]) / 2 center_omega = Domain.lambda_to_omega(center_lambda) N_0 = 0.01 # nm^-3 N_1 = 0.01 * 1.5 # nm^-3 T = 293.5 stimu = StimulatedEmission(dopant=dopant) sigmas = stimu.get_cross_section(omegas, center_omega, N_0, N_1, T) x_data.append(lambdas) y_data.append(sigmas) dopant_name = dopant[0].upper() + dopant[1:] plot_titles.append("Cross sections {} from formula and McCumber " "relations".format(dopant_name)) for i, file in enumerate(files): nbr_samples = 1000
def calc_cross_section(omega, dopant="Yb", predict=None, center_omega=None, N_0=None, N_1=None, T=None): r"""Calculate the emission cross section. There is no analytical formula for the emission cross section. Calculate first the absorption cross sections with the fitting formula from ref. [5]_ and then use the McCumber relations to deduce the emission cross sections. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` dopant : The type of the doped medium. predict : A callable object to predict the cross sections. The variable must be the wavelength. :math:`[nm]` center_omega : The center angular frequency. :math:`[rad\cdot ps^{-1}]` N_0 : The population in ground state. :math:`[nm^{-3}]` N_1 : The population in the excited state. :math:`[nm^{-3}]` T : The temperature. :math:`[K]` Returns ------- : Value of the emission cross section. :math:`[nm^2]` References ---------- .. [5] Valley, G.C., 2001. Modeling cladding-pumped Er/Yb fiber amplifiers. Optical Fiber Technology, 7(1), pp.21-44. """ if (isinstance(omega, float)): res = 0.0 else: res = np.zeros_like(omega) Lambda = Domain.omega_to_lambda(omega) if (predict is None): if (N_0 is not None and N_1 is not None and T is not None and center_omega is not None): sigma_a = Absorption.calc_cross_section(omega, dopant) res = McCumber.calc_cross_section_emission( sigma_a, omega, center_omega, N_0, N_1, T) else: util.warning_terminal( "Not enough information to calculate " "the value of the emission cross sections, will return " "null values.") else: # Interpolate the data from csv file with scipy fct res = predict(Lambda)[0] # Take only zeroth deriv. return res
def test_interplay_dispersion_and_spm(): """Should fail if (i) fwhm of temporal output powers for small N square number is greater than fwhm of initial pulse or (ii) fwhm of temporal output powers for big N square number is greater than initial fwhm in case of positive GVD and smaller than initial fwhm in case of negative GVD. Notes ----- Test case:: gssn ___ fiber """ # Environment creation domain = Domain() lt = Layout(domain) dtime = domain.dtime fwhms_pos_gvd = [] fwhms_neg_gvd = [] time_pos_gvd = [] time_neg_gvd = [] nlse_method = "ssfm_symmetric" # Make sure to have a positive GVD to pass the test gssn = Gaussian(channels=1, peak_power=[1.0], center_lambda=[1030.]) flag = True for i in range(1, 5): if (i == 4): flag = False fiber = Fiber(length=1.0, nlse_method=nlse_method, alpha=[0.46], gamma=2.0, nl_approx=flag, ATT=False, DISP=True, SPM=True, SS=False, RS=False, steps=1000, save=True, approx_type=i, beta=[1e5, 1e3, 20.0]) lt.add_link(gssn[0], fiber[0]) lt.run(gssn) lt.reset() time_pos_gvd.append(fiber[1][0].time) fwhms_pos_gvd.append(fwhm(temporal_power(fiber[1][0].channels), dtime)) flag = True for i in range(1, 5): if (i == 4): flag = False fiber = Fiber(length=1.0, nlse_method=nlse_method, alpha=[0.46], gamma=2.0, nl_approx=flag, ATT=False, DISP=True, SPM=True, SS=False, RS=False, steps=1000, save=True, approx_type=i, beta=[1e5, 1e3, -20.0]) lt.add_link(gssn[0], fiber[0]) lt.run(gssn) lt.reset() time_neg_gvd.append(fiber[1][0].time) fwhms_neg_gvd.append(fwhm(temporal_power(fiber[1][0].channels), dtime)) fwhm_temporal_gssn = fwhm(temporal_power(gssn[0][0].channels), dtime) time_gssn = gssn[0][0].time # Testing for i in range(len(fwhms_neg_gvd)): assert_array_less(time_gssn, time_pos_gvd[i]) assert_array_less(time_gssn, time_neg_gvd[i]) assert fwhm_temporal_gssn[0] < fwhms_pos_gvd[i][0] assert fwhms_neg_gvd[i][0] < fwhm_temporal_gssn[0]
return 1.0 / (power * nl_coeff) if __name__ == "__main__": import numpy as np import optcom.utils.plot as plot from optcom.domain import Domain from optcom.parameters.fiber.effective_area import EffectiveArea from optcom.parameters.fiber.nl_index import NLIndex medium = "SiO2" # With float omega = Domain.lambda_to_omega(1552.0) core_radius = 5.0 n_core = 1.43 n_clad = 1.425 eff_area = EffectiveArea.calc_effective_area(omega, core_radius=core_radius, n_core=n_core, n_clad=n_clad) nl_index = NLIndex.calc_nl_index(omega, medium=medium) print( NLCoefficient.calc_nl_coefficient(omega, nl_index=nl_index, eff_area=eff_area)) # With numpy ndarray
if __name__ == "__main__": """Give an example of GaussianFilter usage. This piece of code is standalone, i.e. can be used in a separate file as an example. """ from typing import Callable, List, Optional import numpy as np import optcom as oc # Plot transfer function domain = Domain(samples_per_bit=2**12) center_lambda = 1030. center_nu = oc.lambda_to_nu(center_lambda) nu = domain.nu + center_nu lambda_bw = 2. nu_bw = oc.lambda_bw_to_nu_bw(lambda_bw, center_lambda) tf = oc.GaussianFilter.transfer_function(nu, center_nu, nu_bw, 0.0) #1.5) lambdas = oc.nu_to_lambda(nu) oc.plot2d(lambdas, tf, x_labels=['nu'], y_labels=['Amplitude (a.u.)'], plot_titles=[ "Transfer function centered at " "{} nm with bandwidth {} nm".format(round(center_lambda, 2), round(lambda_bw))
Lambda: float = 1030.0 pulse: Gaussian = Gaussian(channels=1, peak_power=[1.0, 1.0], center_lambda=[Lambda]) steps: int = int(1e1) beta_01: float = 1e5 beta_02: float = 1e5 beta: List[Union[List[float], Callable, None]] =\ [[beta_01,10.0,-0.0],[beta_02,10.0,-0.0]] v_nbr_value = 2.0 v_nbr: List[Union[float, Callable, None]] = [v_nbr_value] core_radius: List[float] = [5.0] c2c_spacing: List[List[float]] = [[15.0]] n_clad: float = 1.02 omega: float = Domain.lambda_to_omega(Lambda) kappa_: Union[float, Callable] kappa_ = CouplingCoeff.calc_kappa(omega, v_nbr_value, core_radius[0], c2c_spacing[0][0], n_clad) kappa: List[List[Union[List[float], Callable, None]]] = [[None]] delta_a: float = 0.5 * (beta_01 - beta_02) length_c: float = cst.PI / (2 * math.sqrt(delta_a**2 + kappa_**2)) length: float = length_c / 2 for j, method in enumerate(ode_methods): coupler = FiberCoupler(length=length, kappa=kappa, v_nbr=v_nbr, core_radius=core_radius, n_clad=n_clad, c2c_spacing=c2c_spacing,
field.append(res, Domain.lambda_to_omega(self.center_lambda[i])) output_fields.append(field) output_ports.append(0) return output_ports, output_fields if __name__ == "__main__": import optcom.utils.plot as plot import optcom.layout as layout import optcom.domain as domain from optcom.utils.utilities_user import temporal_power, spectral_power lt = layout.Layout(Domain(samples_per_bit=4096)) channels = 3 center_lambda = [1552.0, 1549.0, 1596.0] position = [0.3, 0.5] width = [5.3, 6] peak_power = [1e-3, 2e-3, 6e-3] bit_rate = [0.03, 0.04] offset_nu = [1.56, -1.6] chirp = [0.5, 0.1] init_phi = [1.0, 0.0] sech = Sech(channels=channels, center_lambda=center_lambda, position=position, width=width,
def calc_nl_index(omega, medium): r"""Calculate the non linear index by help of fitting formula. The non linear is assumed to be the sum of the electronic and Raman contributions at zero frequency shift. [5]_ Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` medium : The medium in which to consider the non linear index. Returns ------- : Value of the non linear index. :math:`[m^2\cdot W^{-1}]` Notes ----- Considering: .. math:: n_2(\lambda) = 1.000055 (8.30608 - 27.79971\lambda + 59.66014\lambda^2 - 69.24258\lambda^3 + 45.22437\lambda^4 - 15.63666\lambda^5 + 2.22585\lambda^6) with :math:`\lambda` in :math:`nm` and return with factor :math:`10^{-20}` References ---------- .. [5] Salceda-Delgado, G., Martinez-Rios, A., Ilan, B. and Monzon-Hernandez, D., 2012. Raman response function an Raman fraction of phosphosilicate fibers. Optical and Quantum Electronics, 44(14), pp.657-671. """ medium = medium.lower() if (isinstance(omega, float)): res = 0.0 else: res = np.zeros_like(omega) Lambda = Domain.omega_to_lambda(omega) Lambda *= 1e-3 # nm -> um coeff = POLY_COEFF.get(medium) if (coeff is not None): if (isinstance(omega, float)): for elem in coeff: res += elem[0] * Lambda**elem[1] else: for elem in coeff: res += elem[0] * np.power(Lambda, elem[1]) factor = POLY_FACTOR.get(medium) if (factor is not None): res *= factor else: util.warning_terminal( "The medium provided to calculate the " "non linear index is not recognised. Return null values.") return res * 1e-20 # from fitting formula to m^2 w^-1
def calc_res_index(omega, n_0, N, main_trans, gs, esa, gsa): r"""Compute the resonant index change. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` n_0 : The raw refratvie index of the medium. N : The population of the metastable level. :math:`[nm^{-3}]` main_trans : The main transition parameters. :math:`[nm]` [:math:`f_{12}`, :math:`\lambda_{12}`, FWHM] gs : The degeneracy factor of the metastable and ground state. (:math:`g_{ground}, g_{metastable}`) esa : The transition strength of the metastable state and the relevant UV levels. :math:`[nm]` (:math:`f_{esa}`, [:math:`UV_{esa,1}`, ...]) gsa : The transition strength of the ground state and the relevant UV levels. :math:`[nm]` (:math:`f_{gsa}`, [:math:`UV_{gsa,1}`, ...]) Returns ------- : The refractive index change. Notes ----- .. math:: \Delta n = Q N S where: .. math:: S = -f_{12}\lambda_{12}g'_{12}(\lambda_s) \big(1 + \frac{g_1}{g_2}\big) - \sum_{j>2} f_{1j} \lambda_{1j}g'_{1j}(\lambda_s) + \sum_{j>2} f_{2j}\lambda_{2j}g'_{2j}(\lambda_s) """ if (isinstance(omega, float)): gsa_ = 0.0 esa_ = 0.0 else: gsa_ = np.zeros_like(omega) esa_ = np.zeros_like(omega) lambda_s = Domain.omega_to_lambda(omega) ln = ResonantIndex.lorentzian_lineshape(lambda_s, main_trans[1], main_trans[2]) main_trans_ = main_trans[0] * main_trans[1] * ln * (1 + (gs[0]/gs[1])) # Need to verify for the lambda bw to use here # should not change a lot -> very small term for uv_gsa in gsa[1]: ln = ResonantIndex.lorentzian_lineshape(lambda_s, uv_gsa, main_trans[2]) gsa_ += gsa[0] * uv_gsa * ln for uv_esa in esa[1]: ln = ResonantIndex.lorentzian_lineshape(lambda_s, uv_esa, main_trans[2]) esa_ += esa[0] * uv_esa * ln res = ResonantIndex.calc_Q(n_0) * N * (esa_ - gsa_ - main_trans_) return res