good progress on plasma, no luck for bug

This commit is contained in:
Benoît Sierro
2021-11-01 15:13:20 +01:00
parent 3bef1641d1
commit 8659c6bca9
6 changed files with 279 additions and 135 deletions

View File

@@ -387,9 +387,9 @@ default_rules: list[Rule] = [
Rule("gamma_op", operators.NoGamma, priorities=-1),
Rule("ss_op", operators.SelfSteepening),
Rule("ss_op", operators.NoSelfSteepening, priorities=-1),
Rule("spm_op", operators.SPM),
Rule("spm_op", operators.EnvelopeSPM),
Rule("spm_op", operators.NoSPM, priorities=-1),
Rule("raman_op", operators.Raman),
Rule("raman_op", operators.EnvelopeRaman),
Rule("raman_op", operators.NoRaman, priorities=-1),
Rule("nonlinear_operator", operators.EnvelopeNonLinearOperator),
Rule("loss_op", operators.CustomLoss, priorities=3),
@@ -402,6 +402,6 @@ default_rules: list[Rule] = [
Rule("n_op", operators.HasanRefractiveIndex),
Rule("dispersion_op", operators.ConstantPolyDispersion),
Rule("dispersion_op", operators.DirectDispersion),
Rule("linear_operator", operators.LinearOperator),
Rule("linear_operator", operators.EnvelopeLinearOperator),
Rule("conserved_quantity", operators.conserved_quantity),
]

View File

@@ -321,8 +321,7 @@ class ConstantPolyDispersion(AbstractDispersion):
evaluating on the envelope
"""
coefs: np.ndarray
w_c: np.ndarray
disp_arr: np.ndarray
def __init__(
self,
@@ -336,14 +335,14 @@ class ConstantPolyDispersion(AbstractDispersion):
raise OperatorError(
f"interpolation degree of degree {interpolation_degree} incompatible"
)
self.coefs = fiber.dispersion_coefficients(w_for_disp, beta2_arr, w0, interpolation_degree)
self.w_c = w_c
self.w_power_fact = np.array(
coefs = fiber.dispersion_coefficients(w_for_disp, beta2_arr, w0, interpolation_degree)
w_power_fact = np.array(
[math.power_fact(w_c, k) for k in range(2, interpolation_degree + 3)]
)
self.disp_arr = fiber.fast_poly_dispersion_op(w_c, coefs, w_power_fact)
def __call__(self, state: CurrentState = None) -> np.ndarray:
return fiber.fast_poly_dispersion_op(self.w_c, self.coefs, self.w_power_fact)
return self.disp_arr
class ConstantDirectDispersion(AbstractDispersion):
@@ -413,6 +412,45 @@ class DirectDispersion(AbstractDispersion):
return self.disp_arr
##################################################
################## WAVE VECTOR ###################
##################################################
class AbstractWaveVector(Operator):
@abstractmethod
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the wave vector beta in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
wave vector
"""
class ConstantWaveVector(AbstractWaveVector):
beta_arr: np.ndarray
def __init__(
self,
n_op: ConstantRefractiveIndex,
w_for_disp: np.ndarray,
t_num: int,
dispersion_ind: np.ndarray,
):
self.beta_arr = np.zeros(t_num, dtype=float)
self.beta_arr[dispersion_ind] = fiber.beta(w_for_disp, n_op())[2:-2]
def __call__(self, state: CurrentState) -> np.ndarray:
return self.beta_arr
##################################################
###################### LOSS ######################
##################################################
@@ -477,31 +515,6 @@ class CustomLoss(ConstantLoss):
return self.arr
##################################################
##################### LINEAR #####################
##################################################
class LinearOperator(Operator):
def __init__(self, dispersion_op: AbstractDispersion, loss_op: AbstractLoss):
self.dispersion_op = dispersion_op
self.loss_op = loss_op
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the linear operator to be multiplied by the spectrum in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
linear component
"""
return self.dispersion_op(state) - self.loss_op(state) / 2
##################################################
###################### RAMAN #####################
##################################################
@@ -530,7 +543,7 @@ class NoRaman(NoOp, AbstractRaman):
return self.arr_const
class Raman(AbstractRaman):
class EnvelopeRaman(AbstractRaman):
def __init__(self, raman_type: str, t: np.ndarray):
self.hr_w = fiber.delayed_raman_w(t, raman_type)
self.f_r = 0.245 if raman_type == "agrawal" else 0.18
@@ -539,6 +552,16 @@ class Raman(AbstractRaman):
return self.f_r * np.fft.ifft(self.hr_w * np.fft.fft(state.field2))
class FullFieldRaman(AbstractRaman):
def __init__(self, raman_type: str, t: np.ndarray, chi3: float):
self.hr_w = fiber.delayed_raman_w(t, raman_type)
self.f_r = 0.245 if raman_type == "agrawal" else 0.18
self.multiplier = units.epsilon0 * chi3 * self.f_r
def __call__(self, state: CurrentState) -> np.ndarray:
return self.multiplier * np.fft.ifft(np.fft.fft(state.field2) * self.hr_w)
##################################################
####################### SPM ######################
##################################################
@@ -567,7 +590,15 @@ class NoSPM(NoOp, AbstractSPM):
return self.arr_const
class SPM(AbstractSPM):
class EnvelopeSPM(AbstractSPM):
def __init__(self, raman_op: AbstractRaman):
self.fraction = 1 - raman_op.f_r
def __call__(self, state: CurrentState) -> np.ndarray:
return self.fraction * state.field2
class FullFieldSPM(AbstractSPM):
def __init__(self, raman_op: AbstractRaman):
self.fraction = 1 - raman_op.f_r
@@ -605,7 +636,7 @@ class SelfSteepening(AbstractSelfSteepening):
def __init__(self, w_c: np.ndarray, w0: float):
self.arr = w_c / w0
def __call__(self, state: CurrentState) -> np.ndarray:
def __call__(self, state: CurrentState = None) -> np.ndarray:
return self.arr
@@ -620,6 +651,7 @@ class AbstractGamma(Operator):
"""returns the gamma component
Parameters
----------
state : CurrentState
@@ -655,47 +687,14 @@ class ConstantGamma(AbstractGamma):
##################### PLASMA #####################
##################################################
##################################################
#################### NONLINEAR ###################
##################################################
class Plasma(Operator):
pass
class NonLinearOperator(Operator):
@abstractmethod
class NoPlasma(NoOp, Plasma):
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the nonlinear operator applied on the spectrum in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
nonlinear component
"""
class EnvelopeNonLinearOperator(NonLinearOperator):
def __init__(
self,
gamma_op: AbstractGamma,
ss_op: AbstractSelfSteepening,
spm_op: AbstractSPM,
raman_op: AbstractRaman,
):
self.gamma_op = gamma_op
self.ss_op = ss_op
self.spm_op = spm_op
self.raman_op = raman_op
def __call__(self, state: CurrentState) -> np.ndarray:
return (
-1j
* self.gamma_op(state)
* (1 + self.ss_op(state))
* np.fft.fft(state.field * (self.spm_op(state) + self.raman_op(state)))
)
return self.arr_const
##################################################
@@ -780,3 +779,108 @@ def conserved_quantity(
else:
logger.debug("Conserved quantity : energy without loss")
return EnergyNoLoss(w)
##################################################
##################### LINEAR #####################
##################################################
class EnvelopeLinearOperator(Operator):
def __init__(self, dispersion_op: AbstractDispersion, loss_op: AbstractLoss):
self.dispersion_op = dispersion_op
self.loss_op = loss_op
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the linear operator to be multiplied by the spectrum in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
linear component
"""
return self.dispersion_op(state) - self.loss_op(state) / 2
class FullFieldLinearOperator(Operator):
def __init__(
self,
wave_vector: AbstractWaveVector,
loss_op: AbstractLoss,
reference_velocity: float,
w: np.ndarray,
):
self.delay = w / reference_velocity
self.wave_vector = wave_vector
self.loss_op = loss_op
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the linear operator to be multiplied by the spectrum in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
linear component
"""
return 1j * (self.wave_vector(state) - self.delay) - self.loss_op(state) / 2
##################################################
#################### NONLINEAR ###################
##################################################
class NonLinearOperator(Operator):
@abstractmethod
def __call__(self, state: CurrentState) -> np.ndarray:
"""returns the nonlinear operator applied on the spectrum in the frequency domain
Parameters
----------
state : CurrentState
Returns
-------
np.ndarray
nonlinear component
"""
class EnvelopeNonLinearOperator(NonLinearOperator):
def __init__(
self,
gamma_op: AbstractGamma,
ss_op: AbstractSelfSteepening,
spm_op: AbstractSPM,
raman_op: AbstractRaman,
):
self.gamma_op = gamma_op
self.ss_op = ss_op
self.spm_op = spm_op
self.raman_op = raman_op
def __call__(self, state: CurrentState) -> np.ndarray:
return (
-1j
* self.gamma_op(state)
* (1 + self.ss_op(state))
* np.fft.fft(state.field * (self.spm_op(state) + self.raman_op(state)))
)
class FullFieldNonLinearOperator(NonLinearOperator):
def __init__(self, raman_op: AbstractRaman, spm_op: AbstractSPM, plasma_op: Plasma):
self.raman_op = raman_op
self.spm_op = spm_op
self.plasma_op = plasma_op
def __call__(self, state: CurrentState) -> np.ndarray:
return self.spm_op(state) + self.raman_op(state) + self.plasma_op(state)

View File

@@ -18,7 +18,7 @@ from .const import MANDATORY_PARAMETERS, PARAM_FN, VALID_VARIABLE, __version__
from .errors import EvaluatorError
from .evaluator import Evaluator
from .logger import get_logger
from .operators import AbstractConservedQuantity, LinearOperator, NonLinearOperator
from .operators import AbstractConservedQuantity, EnvelopeLinearOperator, NonLinearOperator
from .utils import fiber_folder, update_path_name
from .variationer import VariationDescriptor, Variationer
@@ -367,7 +367,7 @@ class Parameters:
worker_num: int = Parameter(positive(int))
# computed
linear_operator: LinearOperator = Parameter(type_checker(LinearOperator))
linear_operator: EnvelopeLinearOperator = Parameter(type_checker(EnvelopeLinearOperator))
nonlinear_operator: NonLinearOperator = Parameter(type_checker(NonLinearOperator))
conserved_quantity: AbstractConservedQuantity = Parameter(
type_checker(AbstractConservedQuantity)

View File

@@ -8,7 +8,7 @@ from .. import utils
from ..cache import np_cache
from ..logger import get_logger
from . import units
from .units import NA, c, e, hbar, kB, me
from .units import NA, c, e, hbar, kB, me, epsilon0
@np_cache
@@ -233,63 +233,35 @@ def gas_n2(gas_name: str, pressure: float, temperature: float) -> float:
return non_linear_refractive_index(utils.load_material_dico(gas_name), pressure, temperature)
def adiabadicity(w: np.ndarray, I: float, field: np.ndarray) -> np.ndarray:
return w * np.sqrt(2 * me * I) / (e * np.abs(field))
def free_electron_density(
t: np.ndarray, field: np.ndarray, N0: float, rate_func: Callable[[np.ndarray], np.ndarray]
) -> np.ndarray:
return N0 * (1 - np.exp(-cumulative_trapezoid(rate_func(field), t, initial=0)))
def ionization_rate_ADK(
ionization_energy: float, atomic_number
) -> Callable[[np.ndarray], np.ndarray]:
Z = -(atomic_number - 1) * e
omega_p = ionization_energy / hbar
nstar = Z * np.sqrt(2.1787e-18 / ionization_energy)
def omega_t(field):
return e * np.abs(field) / np.sqrt(2 * me * ionization_energy)
Cnstar = 2 ** (2 * nstar) / (scipy.special.gamma(nstar + 1) ** 2)
omega_pC = omega_p * Cnstar
def rate(field: np.ndarray) -> np.ndarray:
opt4 = 4 * omega_p / omega_t(field)
return omega_pC * opt4 ** (2 * nstar - 1) * np.exp(-opt4 / 3)
return rate
class Plasma:
def __init__(self, t: np.ndarray, ionization_energy: float, atomic_number: int):
self.t = t
self.Ip = ionization_energy
self.atomic_number = atomic_number
self.rate = ionization_rate_ADK(self.Ip, self.atomic_number)
def __call__(self, field: np.ndarray, N0: float) -> np.ndarray:
"""returns the number density of free electrons as function of time
def gas_chi3(gas_name: str, wavelength: float, pressure: float, temperature: float) -> float:
"""returns the chi3 of a particular material
Parameters
----------
field : np.ndarray
electric field in V/m
N0 : float
total number density of matter
gas_name : str
[description]
pressure : float
[description]
temperature : float
[description]
Returns
-------
np.ndarray
number density of free electrons as function of time
float
[description]
"""
Ne = free_electron_density(self.t, field, N0, self.rate)
return cumulative_trapezoid(
np.gradient(Ne, self.t) * self.Ip / field
+ e ** 2 / me * cumulative_trapezoid(Ne * field, self.t, initial=0),
self.t,
initial=0,
mat_dico = utils.load_material_dico(gas_name)
chi = sellmeier(wavelength, mat_dico, pressure=pressure, temperature=temperature)
return n2_to_chi3(
non_linear_refractive_index(mat_dico, pressure, temperature), np.sqrt(chi + 1)
)
def n2_to_chi3(n2: float, n0: float) -> float:
return n2 / 3.0 * 4 * epsilon0 * n0 * c ** 2
def chi3_to_n2(chi3: float, n0: float) -> float:
return 3.0 * chi3 / (4.0 * epsilon0 * c * n0 ** 2)

View File

@@ -0,0 +1,69 @@
import numpy as np
import scipy.special
from scipy.integrate import cumulative_trapezoid
from .units import e, hbar, me
class IonizationRate:
def __call__(self, field: np.ndarray) -> np.ndarray:
...
class IonizationRateADK(IonizationRate):
def __init__(self, ionization_energy: float, atomic_number: int):
self.Z = -(atomic_number - 1) * e
self.ionization_energy = ionization_energy
self.omega_p = ionization_energy / hbar
self.nstar = self.Z * np.sqrt(2.1787e-18 / ionization_energy)
Cnstar = 2 ** (2 * self.nstar) / (scipy.special.gamma(self.nstar + 1) ** 2)
self.omega_pC = self.omega_p * Cnstar
def omega_t(self, field):
return e * np.abs(field) / np.sqrt(2 * me * self.ionization_energy)
def __call__(self, field: np.ndarray) -> np.ndarray:
opt4 = 4 * self.omega_p / self.omega_t(field)
return self.omega_pC * opt4 ** (2 * self.nstar - 1) * np.exp(-opt4 / 3)
class Plasma:
def __init__(self, t: np.ndarray, ionization_energy: float, atomic_number: int):
self.t = t
self.Ip = ionization_energy
self.atomic_number = atomic_number
self.rate = IonizationRateADK(self.Ip, self.atomic_number)
def __call__(self, field: np.ndarray, N0: float) -> np.ndarray:
"""returns the number density of free electrons as function of time
Parameters
----------
field : np.ndarray
electric field in V/m
N0 : float
total number density of matter
Returns
-------
np.ndarray
number density of free electrons as function of time
"""
Ne = free_electron_density(self.t, field, N0, self.rate)
return cumulative_trapezoid(
np.gradient(Ne, self.t) * self.Ip / field
+ e ** 2 / me * cumulative_trapezoid(Ne * field, self.t, initial=0),
self.t,
initial=0,
)
def adiabadicity(w: np.ndarray, I: float, field: np.ndarray) -> np.ndarray:
return w * np.sqrt(2 * me * I) / (e * np.abs(field))
def free_electron_density(
t: np.ndarray, field: np.ndarray, N0: float, rate: IonizationRate
) -> np.ndarray:
return N0 * (1 - np.exp(-cumulative_trapezoid(rate(field), t, initial=0)))

View File

@@ -10,7 +10,7 @@ import numpy as np
from .. import utils
from ..logger import get_logger
from ..operators import AbstractConservedQuantity, CurrentState
from ..operators import CurrentState
from ..parameter import Configuration, Parameters
from ..pbar import PBars, ProgressBarActor, progress_worker
@@ -36,7 +36,6 @@ class RK4IP:
cons_qty: list[float]
state: CurrentState
conserved_quantity: AbstractConservedQuantity
stored_spectra: list[np.ndarray]
def __init__(
@@ -229,7 +228,7 @@ class RK4IP:
store = True
self.state.h = self.z_targets[0] - self.state.z
def take_step(self, step: int) -> tuple[float, float, np.ndarray]:
def take_step(self, step: int) -> float:
"""computes a new spectrum, whilst adjusting step size if required, until the error estimation
validates the new spectrum. Saves the result in the internal state attribute