Source code for pynlo.utility.chi3

# -*- coding: utf-8 -*-
"""
Conversion functions and other calculators relevant to the 3rd-order nonlinear
susceptibility.

"""

__all__ = ["gamma_to_g3", "g3_to_gamma", "g3_split", "g3_spm", "raman"]


# %% Imports

import numpy as np
from scipy.constants import pi, c, epsilon_0 as e0

from pynlo.utility import fft


#%% Converters

#---- Effective Nonlinearities g3 and gamma
[docs] def gamma_to_g3(v_grid, gamma, t_shock=None): """ Convert from the gamma to the g3 nonlinear parameter. The `t_shock` parameter is used to determine the strength of the self-steepening effect when gamma is only given at a single point. If an array is given for gamma, it is assumed to already contain the self-steepening effect. Parameters ---------- v_grid : array_like of float The frequency grid. gamma : array_like The effective nonlinear parameter. t_shock : float, optional The characteristic time scale of optical shock formation. This time scale is typically equal to ``1/(2*pi*v0)``, where `v0` is the frequency at which `gamma` is defined, but it may also contain corrections to `gamma` due to the frequency dependence of the optical mode. This parameter is only valid if `gamma` is given at a single point. Returns ------- g3 """ gamma = np.asarray(gamma) if t_shock is not None: assert gamma.size == 1, "t_shock is only valid when a gamma is given at a single point." v0_eff = 1/(2*pi*t_shock) gamma = gamma * v_grid/v0_eff return gamma / (3/2 * 2*pi*v_grid)
[docs] def g3_to_gamma(v_grid, g3): """ Convert from the g3 to the gamma nonlinear parameter. Parameters ---------- v_grid : array_like of float The frequency grid. g3 : array_like The effective nonlinear parameter. Returns ------- gamma """ return g3 * (3/2 * 2*pi*v_grid)
#---- 3rd-Order Nonlinear Susceptibility chi3 and Kerr Parameter def n2_to_chi3(n, n2): return n2 * (4/3 * e0*c*n**2) def chi3_to_n2(n, chi3): return chi3 / (4/3 * e0*c*n**2) #---- Effective Nonlinearity gamma and Kerr Parameter def n2_to_gamma(v_grid, a_eff, n2): return n2 * (2*pi*v_grid/(c*a_eff)) def gamma_to_n2(v_grid, a_eff, gamma): return gamma / (2*pi*v_grid/(c*a_eff)) # %% Nonlinearity
[docs] def g3_split(n_eff, a_eff, chi3_eff): """ The 3rd-order nonlinear parameter decomposed according to unit analysis. Parameters ---------- n_eff : array_like of float The refractive indices. a_eff : array_like of float The effective areas. chi3_eff : array_like The effective 3rd-order susceptibilities. Returns ------- ndarray (2, n) The unit decomposition of the 3rd-order nonlinear parameter. The first index contains the output factors while the second index contains the input factors. """ return np.array([1/2 * ((e0*a_eff)/(c*n_eff))**0.5 * chi3_eff, 1/(e0*c*n_eff*a_eff)**0.5])
[docs] def g3_spm(n_eff, a_eff, chi3_eff): """ The 3rd-order nonlinear parameter weighted for self-phase modulation. Parameters ---------- n_eff : array_like of float The effective refractive indices. a_eff : array_like of float The effective areas. chi3_eff : array_like The effective 3rd-order susceptibilities. Returns ------- g3 : ndarray """ return 1/2 * chi3_eff/(e0*c**2 * n_eff**2 * a_eff)
[docs] def raman(n, dt, r_weights, b_weights=None, analytic=True): """ Calculate the frequency-domain Raman and instantaneous nonlinear response function. The equations used are the approximated formulations as summarized in section 2.3.3 of Agrawal's Nonlinear Fiber Optics [1]_. More accurate simulations may be obtainable using digitized experimental measurements, such as those shown in figure 2.2 of [1]_. The coefficients listed in Agrawal for silica-based fibers are as follows:: r_weights = [0.245*(1-0.21), 12.2e-15, 32e-15] # resonant contribution b_weights = [0.245*0.21, 96e-15] # boson contribution Parameters ---------- n : int The number of points in the time domain. dt : float The time grid step size. r_weights : array_like of float The contributions due to resonant vibrations. Must be given as ``[fraction, tau_1, tau_2]``, where `fraction` is the fractional contribution of the resonance to the total nonlinear response function, `tau_1` is the period of the vibrational frequency, and `tau_2` is the resonance's characteristic decay time. More than one resonance may be entered using an (n, 3) shaped array. b_weights : array_like of float, optional The contributions due to boson peaks found in amorphous materials. Must be given as ``[fraction, tau_b]``, where `fraction` is the fractional contribution of the boson peak to the total nonlinear response function, and `tau_b` is the boson peak's characteristic decay time. More than one peak may be entered using an (n, 2) shaped array. analytic : bool, optional A flag that sets the proper normalization for use with the analytic or real-valued representation. The default normalizes for the analytic representation, which is the proper format for use with the `NLSE` model. Set this parameter to `False` if using the `UPE` model. Returns ------- rv_grid : ndarray of float The origin-continuous frequency grid associated with the nonlinear response function. nonlinear_v : ndarray of complex The frequency-domain nonlinear response function. This is defined over the frequency grid given by ``dv=1/(n*dt)``. Notes ----- For the carrier-resolved or real-valued representation, an additional factor of 3/2 is necessary to properly normalize the Raman response. The formulas used in this method have been fit to the analytic representation, which is normalized assuming that all three self-phase modulation pathways fold through baseband. In the real-valued domain however, only two pass through baseband. The third pathway is through the second harmonic. Thus, in the real-valued representation the Raman response must be renormalized to produce the same nonlinear response against 2/3 the spectral amplitude. References ---------- .. [1] Agrawal GP. Nonlinear Fiber Optics. Sixth ed. London; San Diego, CA;: Academic Press; 2019. https://doi.org/10.1016/B978-0-12-817042-7.00009-9 """ #---- Grids t_grid = np.arange(n) * dt dv = 1/(n*dt) rv_grid = np.arange(n//2 + 1) * dv #---- Raman Response raman_frac = 0 # initialize def h_r(fraction, tau_1, tau_2): """ Contributions due to vibrational resonances. Parameters ---------- fraction : float tau_1 : float tau_2 : float Returns ------- ndarray of float """ h_r = tau_1*(tau_1**-2 + tau_2**-2)*np.exp(-t_grid/tau_2)*np.sin(t_grid/tau_1) h_r *= fraction return h_r def h_b(fraction, tau_b): """ Contributions due to boson peaks. Parameters ---------- fraction : float tau_b : float Returns ------- ndarray of float """ h_b = (2*tau_b - t_grid)/tau_b**2 * np.exp(-t_grid/tau_b) h_b *= fraction return h_b # Resonant Vibrational Modes raman_t = np.zeros_like(t_grid) r_weights = np.asarray(r_weights, dtype=float) if len(r_weights.shape) == 1: r_weights = r_weights[np.newaxis] for weight in r_weights: raman_frac += weight[0] raman_t += h_r(*weight) # Boson Peaks if b_weights is not None: b_weights = np.asarray(b_weights, dtype=float) if len(b_weights.shape) == 1: b_weights = b_weights[np.newaxis] for weight in b_weights: raman_frac += weight[0] raman_t += h_b(*weight) # Normalize assert raman_frac <= 1, "The total fractional Raman contribution must be less than 1." if raman_frac != 0: raman_t *= raman_frac/(np.sum(raman_t)*dt) if not analytic: raman_t *= 3/2 #---- Instantaneous Response delta_t = np.zeros_like(t_grid) delta_t[0] = 1/dt delta_t *= 1 - raman_frac # remove raman fraction #---- Total Nonlinear Response nonlinear_t = raman_t + delta_t nonlinear_v = fft.rfft(nonlinear_t, fsc=dt) # already in standard fft order return rv_grid, nonlinear_v
# %% Solitons def nonlinear_length(): pass #TODO def soliton_number(): # copy over linear_length, and test against in chi1 # (chi1.linear_length = soliton_number**2 * nonlinear_length) pass #TODO: def soltion_period(): pass #TODO def soliton_fission(): pass #TODO: estimate of soliton fission point, TD compression, and FD expansion def soliton_DW_dk(v_grid, beta, v0=None): pass #TODO def soliton_SFS(gamma, ): pass #TODO: https://doi.org/10.1364/JOSAB.409240