Refactored ionization rate

This commit is contained in:
Benoît Sierro
2022-09-12 12:17:33 +02:00
parent e6b711cd85
commit 579532a4d3
2 changed files with 57 additions and 54 deletions

View File

@@ -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)

View File

@@ -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