Refactored ionization rate
This commit is contained in:
@@ -847,18 +847,14 @@ class Plasma(Operator):
|
|||||||
|
|
||||||
def __init__(self, dt: float, w: np.ndarray, gas_op: AbstractGas):
|
def __init__(self, dt: float, w: np.ndarray, gas_op: AbstractGas):
|
||||||
self.gas_op = gas_op
|
self.gas_op = gas_op
|
||||||
self.mat_plasma = plasma.Plasma(
|
self.mat_plasma = plasma.Plasma(dt, self.gas_op.material_dico["ionization_energy"])
|
||||||
dt,
|
|
||||||
self.gas_op.material_dico["ionization_energy"],
|
|
||||||
plasma.IonizationRateADK(self.gas_op.material_dico["ionization_energy"]),
|
|
||||||
)
|
|
||||||
self.factor_out = -w / (2.0 * units.c ** 2 * units.epsilon0)
|
self.factor_out = -w / (2.0 * units.c ** 2 * units.epsilon0)
|
||||||
|
|
||||||
def __call__(self, state: CurrentState) -> np.ndarray:
|
def __call__(self, state: CurrentState) -> np.ndarray:
|
||||||
N0 = self.gas_op.number_density(state)
|
N0 = self.gas_op.number_density(state)
|
||||||
plasma_info = self.mat_plasma(state.field, N0)
|
plasma_info = self.mat_plasma(state.field, N0)
|
||||||
self.ionization_fraction = plasma_info.electron_density[-1] / N0
|
self.ionization_fraction = plasma_info.electron_density[-1] / N0
|
||||||
return self.factor_out * np.fft.rfft(plasma_info.dp_dt)
|
return self.factor_out * np.fft.rfft(plasma_info.polarization)
|
||||||
|
|
||||||
def values(self) -> dict[str, float]:
|
def values(self) -> dict[str, float]:
|
||||||
return dict(ionization_fraction=self.ionization_fraction)
|
return dict(ionization_fraction=self.ionization_fraction)
|
||||||
|
|||||||
@@ -1,77 +1,78 @@
|
|||||||
from dataclasses import dataclass
|
from typing import Any, Callable, NamedTuple, TypeVar
|
||||||
from typing import TypeVar
|
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import scipy.special
|
import scipy.special
|
||||||
|
from scipy.interpolate import interp1d
|
||||||
from numpy.core.numeric import zeros_like
|
from numpy.core.numeric import zeros_like
|
||||||
|
|
||||||
from ..math import cumulative_simpson, expm1_int
|
from ..math import cumulative_simpson, expm1_int
|
||||||
from .units import e, hbar, me
|
from .units import e, hbar, me
|
||||||
|
|
||||||
T_a = TypeVar("T_a", np.floating, np.ndarray, float)
|
|
||||||
|
|
||||||
|
class PlasmaInfo(NamedTuple):
|
||||||
@dataclass
|
polarization: np.ndarray
|
||||||
class PlasmaInfo:
|
|
||||||
electron_density: np.ndarray
|
electron_density: np.ndarray
|
||||||
dp_dt: np.ndarray
|
|
||||||
rate: np.ndarray
|
rate: np.ndarray
|
||||||
dn_dt: np.ndarray
|
debug: Any = None
|
||||||
debug: np.ndarray
|
|
||||||
|
|
||||||
|
|
||||||
class IonizationRate:
|
def ion_rate_adk(
|
||||||
ionization_energy: float
|
field_abs: np.ndarray, ion_energy: float, Z: float
|
||||||
|
) -> Callable[[np.ndarray], np.ndarray]:
|
||||||
|
|
||||||
def __init__(self, ionization_energy: float):
|
nstar = Z * np.sqrt(2.1787e-18 / ion_energy)
|
||||||
self.Z = 1
|
omega_p = ion_energy / hbar
|
||||||
self.ionization_energy = ionization_energy
|
Cnstar = 2 ** (2 * nstar) / (scipy.special.gamma(nstar + 1) ** 2)
|
||||||
self.nstar = self.Z * np.sqrt(2.1787e-18 / ionization_energy)
|
omega_pC = omega_p * Cnstar
|
||||||
|
omega_t = e * field_abs / np.sqrt(2 * me * ion_energy)
|
||||||
def __call__(self, field_abs: T_a) -> T_a:
|
opt4 = 4 * omega_p / omega_t
|
||||||
...
|
return omega_pC * opt4 ** (2 * nstar - 1) * np.exp(-opt4 / 3)
|
||||||
|
|
||||||
|
|
||||||
class IonizationRateADK(IonizationRate):
|
def cache_ion_rate(
|
||||||
def __init__(self, ionization_energy: float):
|
ion_energy, rate_func: Callable[[np.ndarray, float, float], np.ndarray]
|
||||||
super().__init__(ionization_energy)
|
) -> Callable[[np.ndarray], np.ndarray]:
|
||||||
self.omega_p = ionization_energy / hbar
|
Z = 1
|
||||||
|
E_max = barrier_suppression(ion_energy, Z) * 2
|
||||||
|
E_min = E_max / 5000
|
||||||
|
field = np.linspace(E_min, E_max, 4096)
|
||||||
|
interp = interp1d(
|
||||||
|
field,
|
||||||
|
rate_func(field, ion_energy, Z),
|
||||||
|
"cubic",
|
||||||
|
assume_sorted=True,
|
||||||
|
fill_value=0,
|
||||||
|
bounds_error=False,
|
||||||
|
)
|
||||||
|
|
||||||
Cnstar = 2 ** (2 * self.nstar) / (scipy.special.gamma(self.nstar + 1) ** 2)
|
def compute(field_abs: np.ndarray) -> np.ndarray:
|
||||||
self.omega_pC = self.omega_p * Cnstar
|
if field_abs.max() > E_max or field_abs.min() < -E_max:
|
||||||
|
raise ValueError("E field is out of bounds")
|
||||||
|
return interp(field_abs)
|
||||||
|
|
||||||
def omega_t(self, field_abs: T_a) -> T_a:
|
return compute
|
||||||
return e * field_abs / np.sqrt(2 * me * self.ionization_energy)
|
|
||||||
|
|
||||||
def __call__(self, field_abs: T_a) -> T_a:
|
|
||||||
ot = self.omega_t(field_abs)
|
|
||||||
opt4 = 4 * self.omega_p / ot
|
|
||||||
return self.omega_pC * opt4 ** (2 * self.nstar - 1) * np.exp(-opt4 / 3)
|
|
||||||
|
|
||||||
|
|
||||||
class IonizationRatePPT(IonizationRate):
|
def create_ion_rate_func(
|
||||||
def __init__(self, ionization_energy: float) -> None:
|
ionization_energy: float, model="ADK"
|
||||||
self.ionization_energy = ionization_energy
|
) -> Callable[[np.ndarray], np.ndarray]:
|
||||||
self.numerator = 2 * (2 * self.ionization_energy) ** 1.5
|
if model == "ADK":
|
||||||
|
func = ion_rate_adk
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Ionization model {model!r} unrecognized")
|
||||||
|
|
||||||
def __call__(self, field_abs: T_a) -> T_a:
|
return cache_ion_rate(ionization_energy, func)
|
||||||
|
|
||||||
return (
|
|
||||||
self.factor
|
|
||||||
* (self.numerator / field_abs) ** (2 * self.nstart - 1)
|
|
||||||
* np.exp(-self.numerator / (3 * field_abs))
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Plasma:
|
class Plasma:
|
||||||
dt: float
|
dt: float
|
||||||
ionization_energy: float
|
ionization_energy: float
|
||||||
rate: IonizationRate
|
rate: Callable[[np.ndarray], np.ndarray]
|
||||||
|
|
||||||
def __init__(self, dt: float, ionization_energy: float, rate: IonizationRate):
|
def __init__(self, dt: float, ionization_energy: float):
|
||||||
self.dt = dt
|
self.dt = dt
|
||||||
self.ionization_energy = ionization_energy
|
self.ionization_energy = ionization_energy
|
||||||
self.rate = rate
|
self.rate = create_ion_rate_func(self.ionization_energy)
|
||||||
|
|
||||||
def __call__(self, field: np.ndarray, N0: float) -> PlasmaInfo:
|
def __call__(self, field: np.ndarray, N0: float) -> PlasmaInfo:
|
||||||
"""returns the number density of free electrons as function of time
|
"""returns the number density of free electrons as function of time
|
||||||
@@ -100,9 +101,9 @@ class Plasma:
|
|||||||
|
|
||||||
phase_term = self.dt * e ** 2 / me * cumulative_simpson(electron_density * field)
|
phase_term = self.dt * e ** 2 / me * cumulative_simpson(electron_density * field)
|
||||||
|
|
||||||
# polarization = -loss_integrated + phase_integrated
|
dp_dt = loss_term + phase_term
|
||||||
dp_dt = loss_term
|
polarization = self.dt * cumulative_simpson(dp_dt)
|
||||||
return PlasmaInfo(electron_density, dp_dt, rate, dn_dt, phase_term)
|
return PlasmaInfo(polarization, electron_density, rate)
|
||||||
|
|
||||||
|
|
||||||
def adiabadicity(w: np.ndarray, I: float, field: np.ndarray) -> np.ndarray:
|
def adiabadicity(w: np.ndarray, I: float, field: np.ndarray) -> np.ndarray:
|
||||||
@@ -111,3 +112,9 @@ def adiabadicity(w: np.ndarray, I: float, field: np.ndarray) -> np.ndarray:
|
|||||||
|
|
||||||
def free_electron_density(rate: np.ndarray, dt: float, N0: float) -> np.ndarray:
|
def free_electron_density(rate: np.ndarray, dt: float, N0: float) -> np.ndarray:
|
||||||
return N0 * expm1_int(rate, dt)
|
return N0 * expm1_int(rate, dt)
|
||||||
|
|
||||||
|
|
||||||
|
def barrier_suppression(ionpot, Z):
|
||||||
|
Ip_au = ionpot / 4.359744650021498e-18
|
||||||
|
ns = Z / np.sqrt(2 * Ip_au)
|
||||||
|
return Z ** 3 / (16 * ns ** 4) * 5.14220670712125e11
|
||||||
|
|||||||
Reference in New Issue
Block a user