def set(self, waves: Array[cst.NPFT], h: float, z: float) -> None: self._step = int(round(z / h)) self._call_counter += 1 # Set parameters ----------------------------------------------- if (self._sigma_e_mccumber): omega = FFT.ifftshift( self._omega) # could also change in abstract equation for i in range(len(self._center_omega_s)): self._sigma_e_s[i] = McCumber.calc_cross_section_emission( self._sigma_a_s[i], omega, 0.0, self.N_0[self._step - 1], self.N_1[self._step - 1], self._T) for i in range(len(self._center_omega_p)): self._sigma_e_p[i] = McCumber.calc_cross_section_emission( self._sigma_a_p[i], omega, 0.0, self.N_0[self._step - 1], self.N_1[self._step - 1], self._T) # Add pump and signal power space step ------------------------- if (not self._iter): # first iteration to_add_s = np.zeros((1, ) + self._shape_step_s) to_add_p = np.zeros((1, ) + self._shape_step_p) self._power_s_f = np.vstack((self._power_s_f, to_add_s)) self._power_s_b = np.vstack((to_add_s, self._power_s_b)) self._power_p_f = np.vstack((self._power_p_f, to_add_p)) self._power_p_b = np.vstack((to_add_p, self._power_p_b)) self._power_ase_f = np.vstack((self._power_ase_f, to_add_s)) self._power_ase_b = np.vstack((to_add_s, self._power_ase_b)) if (self._coprop): # First iteration forward self._N_1 = np.hstack((self._N_1, [0.0])) else: # First iteration backward -> self._forward is False self._N_1 = np.hstack(([0.0], self._N_1)) self._step = 0 # construct backward array in bottom-up # Set signal power --------------------------------------------- # Truth table: # if coprop : iter |forward| to do # 0 | T | set pump # 1 | F | set pump # 2 | T | set pump & set signal # if counterprop: iter|forward| to do # 0 | T | set pump # 1 | F | set pump & set signal if (self._step_update): if (self._forward): if (self._signal_on): self._power_s_f[self._step - 1] = self._get_power_s(waves) self._power_p_f[self._step - 1] = self._get_power_p(waves) else: if (self._signal_on): self._power_s_b[self._step + 1] = self._get_power_s(waves) self._power_p_b[self._step + 1] = self._get_power_p(waves)
def op_approx_cross( self, waves: Array[cst.NPFT], id: int, corr_wave: Optional[Array[cst.NPFT]] = None) -> Array[cst.NPFT]: """The approximation operator of the cross terms of the Raman effect.""" res = np.zeros(waves[id].shape, dtype=cst.NPFT) if (self._approx_type == cst.approx_type_1 or self._approx_type == cst.approx_type_2 or self._approx_type == cst.approx_type_3): for i in range(len(waves)): if (i != id): res += waves[i] * np.conj(waves[i]) res = FFT.dt_to_fft(res, self._omega, 1) return -1j * self._T_R * self._eta * res
def calc_raman_gain(omega_bw: float, h_R: Array[float], center_omega: float, n_0: float, f_R: float, n_2: float) -> Array[float]: r"""Calculate the Raman gain. Parameters ---------- omega_bw : The angular frequency bandwidth. :math:`[ps^{-1}]` h_R : The raman response function. :math:`[ps^{-1}]` center_omega : The center angular frequency. :math:`[ps^{-1}]` n_0 : The refractive index. f_R : The fractional contribution of the delayed Raman response. :math:`[]` n_2 : The non-linear index. :math:`[m^2\cdot W^{-1}]` Returns ------- : The Raman gain. Notes ----- .. math:: g_R(\Delta \omega) = \frac{\omega_0}{c n_0} f_R \chi^{(3)} \Im{\hat{h}_R(\Delta \omega)} where: .. math:: \chi^{(3)} = \frac{4\epsilon_0 c n^2}{3} n_2 :math:`\chi^{(3)}` is in :math:`[m^2 \cdot V^{-2}]`. """ n_2 *= 1e36 # m^2 W^{-1} = s^3 kg^-1 -> ps^3 kg^-1 chi_3 = 4 * cst.EPS_0 * cst.LIGHT_SPEED * n_0**2 * n_2 / 3 return (center_omega / cst.LIGHT_SPEED / n_0 * f_R * chi_3 * np.imag(FFT.fft(h_R)))
def open(self, domain: Domain, *fields: List[Field]) -> None: """This function is called once before a Stepper began the computation. Pass the time, wavelength and center wavelength to all the effects in the equation. """ self._init_eq_ids(list(fields)) self._center_omega = np.array([]) for field_list in fields: for field in field_list: self._center_omega = np.hstack( (self._center_omega, field.center_omega)) self._omega = FFT.fftshift(domain.omega) effects = ["lin", "non_lin", "all"] for effect in effects: effect_list = getattr(self, "_effects_{}".format(effect)) for i in range(len(effect_list)): effect_list[i].omega = self._omega effect_list[i].time = domain.time effect_list[i].center_omega = self._center_omega
def op_non_lin(self, waves: Array[cst.NPFT], id: int, corr_wave: Optional[Array[cst.NPFT]] = None ) -> Array[cst.NPFT]: r"""Represent the non linear effects of the approximated NLSE. Parameters ---------- waves : The wave packet propagating in the fiber. id : The ID of the considered wave in the wave packet. corr_wave : Correction wave, use for consistency. Returns ------- : The non linear term for the considered wave. Notes ----- .. math:: \hat{N} = \mathcal{F}^{-1}\bigg\{i \gamma \Big(1+\frac{\omega}{\omega_0}\Big) \mathcal{F}\Big\{ (1-f_R) |A|^2 + f_R \mathcal{F}^{-1}\big\{\mathcal{F}\{h_R\} \mathcal{F}\{|A|^2\}\big\}\Big\}\bigg\} """ res = FFT.ifft(self.op_non_lin_rk4ip(waves, id) * waves[id]) if (corr_wave is None): corr_wave = waves[id] res = np.zeros_like(res) res = np.divide(res, corr_wave, out=res, where=corr_wave!=0) return res
def rk4ip_gnlse(f: AbstractFieldEquation, waves: np.ndarray, z: float, h: float) -> np.ndarray: r"""Optimized Runge-Kutta interaction picture method. Parameters ---------- f : The function to compute. waves : The value of the unknown (waves) at the considered space step. z : The current value of the space variable. h : The step size. Returns ------- : The one step euler computation results. Notes ----- Implementation: .. math:: \begin{align} &A^L_j = \exp\Big(\frac{h}{2}\hat{\mathcal{D}}\Big) \mathcal{F}\{A_j(z)\} &\forall j=1,\ldots,K\\ &k_0 = h \exp\Big(\frac{h}{2}\hat{\mathcal{D}}\Big) \hat{\mathcal{N}}_0\big(A_1(z), \ldots, A_j(z), \ldots, A_K(z)\big)&\forall j=1,\ldots,K\\ &k_1 = h \hat{\mathcal{N}}_0\Big(\mathcal{F}^{-1} \Big\{A^L_{1} +\frac{k_{0,1}}{2},\ldots, A^L_{j}+\frac{k_{0,j}}{2},\ldots, A^L_{K} + \frac{k_{0,K}}{2}\Big\} \Big) &\forall j=1,\ldots,K\\ &k_2 = h \hat{\mathcal{N}}_0\Big(\mathcal{F}^{-1} \Big\{A^L_{1} +\frac{k_{1,1}}{2},\ldots, A^L_{j} +\frac{k_{1,j}}{2},\ldots, A^L_{K} + \frac{k_{1,K}}{2}\Big\} \Big) &\forall j=1,\ldots,K\\ &k_3 = h \hat{\mathcal{N}}_0\Big(\mathcal{F}^{-1} \Big\{\exp\Big(\frac{h}{2}\hat{\mathcal{D}}\Big) (A^L_1 + k_{2, 1}) \Big\},\ldots, \nonumber \\ & \qquad \qquad \quad \mathcal{F}^{-1}\Big\{\exp \Big(\frac{h}{2}\hat{\mathcal{D}}\Big)(A^L_j + k_{2,j}) \Big\}, \ldots,\nonumber\\ & \qquad \qquad \quad \mathcal{F}^{-1}\Big\{\exp\Big( \frac{h}{2}\hat{\mathcal{D}}\Big)(A^L_K + k_{2,K}) \Big\}\Big)&\forall j=1,\ldots,K\\ &A(z+h) = \mathcal{F}^{-1}\Big\{\frac{k_{3,j}}{6} +\exp\Big(\frac{h}{2}\hat{\mathcal{D}}\Big) \big(A^L_{j}+\frac{k_{0,j}}{6}+\frac{k_{1,j}}{3} +\frac{k_{2,j}}{3}\big)\Big\} &\forall j=1,\ldots,K \end{align} where :math:`K` is the number of channels. """ if (isinstance(f, GNLSE) or isinstance(f, AmpGNLSE)): h_h = 0.5 * h exp_op_lin = np.zeros_like(waves) A_L = np.zeros_like(waves) k_0 = np.zeros_like(waves) k_1 = np.zeros_like(waves) k_2 = np.zeros_like(waves) k_3 = np.zeros_like(waves) for i in range(len(waves)): exp_op_lin[i] = f.exp_op_lin(waves, i, h_h) for i in range(len(waves)): A_L[i] = exp_op_lin[i] * FFT.fft(waves[i]) for i in range(len(waves)): k_0[i] = h * exp_op_lin[i] * f.term_rk4ip_non_lin(waves, i, z) for i in range(len(waves)): waves[i] = FFT.ifft(A_L[i] + 0.5*k_0[i]) for i in range(len(waves)): k_1[i] = h * f.term_rk4ip_non_lin(waves, i, z) for i in range(len(waves)): waves[i] = FFT.ifft(A_L[i] + 0.5*k_1[i]) for i in range(len(waves)): k_2[i] = h * f.term_rk4ip_non_lin(waves, i, z) for i in range(len(waves)): waves[i] = FFT.ifft(exp_op_lin[i] * (A_L[i] + k_2[i])) for i in range(len(waves)): k_3[i] = h * f.term_rk4ip_non_lin(waves, i, z) for i in range(len(waves)): waves[i] = ((k_3[i]/6.0) + (exp_op_lin[i] * (A_L[i] + k_0[i]/6.0 + (k_1[i]+k_2[i])/3.0))) waves[i] = FFT.ifft(waves[i]) else: raise RK4IPGNLSEError("Only the the gnlse can be computed with " "the rk4ip_gnlse method.") return waves