Working Erk43

This commit is contained in:
Benoît Sierro
2021-11-15 10:03:13 +01:00
parent d18804d812
commit 38b3d063e5
5 changed files with 76 additions and 73 deletions

View File

@@ -378,7 +378,6 @@ default_rules: list[Rule] = [
Rule("n_op", operators.HasanRefractiveIndex), Rule("n_op", operators.HasanRefractiveIndex),
Rule("raman_op", operators.NoRaman, priorities=-1), Rule("raman_op", operators.NoRaman, priorities=-1),
Rule("loss_op", operators.NoLoss, priorities=-1), Rule("loss_op", operators.NoLoss, priorities=-1),
Rule("plasma_op", operators.NoPlasma, priorities=-1),
# solvers # solvers
Rule("integrator", solver.ConstantStepIntegrator, conditions=dict(adapt_step_size=False)), Rule("integrator", solver.ConstantStepIntegrator, conditions=dict(adapt_step_size=False)),
Rule( Rule(
@@ -453,5 +452,7 @@ full_field_rules = default_rules + [
"linear_operator", "linear_operator",
operators.FullFieldLinearOperator, operators.FullFieldLinearOperator,
), ),
Rule("plasma_op", operators.Plasma, conditions=dict(photoionization=True)),
Rule("plasma_op", operators.NoPlasma, priorities=-1),
Rule("nonlinear_operator", operators.FullFieldNonLinearOperator), Rule("nonlinear_operator", operators.FullFieldNonLinearOperator),
] ]

View File

@@ -13,7 +13,7 @@ from scipy.interpolate import interp1d
from . import math from . import math
from .logger import get_logger from .logger import get_logger
from .physics import fiber, materials, pulse, units from .physics import fiber, materials, pulse, units, plasma
from .utils import load_material_dico from .utils import load_material_dico
@@ -198,6 +198,9 @@ class NoOpFreq(Operator):
class AbstractGas(ABC): class AbstractGas(ABC):
gas_name: str
material_dico: dict[str, Any]
@abstractmethod @abstractmethod
def pressure(self, state: CurrentState) -> float: def pressure(self, state: CurrentState) -> float:
"""returns the pressure at the current """returns the pressure at the current
@@ -254,11 +257,12 @@ class ConstantGas(AbstractGas):
ideal_gas: bool, ideal_gas: bool,
wl_for_disp: np.ndarray, wl_for_disp: np.ndarray,
): ):
gas_dico = load_material_dico(gas_name) self.material_dico = load_material_dico(gas_name)
self.gas_name = gas_name
self.pressure_const = pressure self.pressure_const = pressure
if ideal_gas: if ideal_gas:
self.number_density_const = materials.number_density_van_der_waals( self.number_density_const = materials.number_density_van_der_waals(
pressure=pressure, temperature=temperature, material_dico=gas_dico pressure=pressure, temperature=temperature, material_dico=self.material_dico
) )
else: else:
self.number_density_const = self.pressure_const / (units.kB * temperature) self.number_density_const = self.pressure_const / (units.kB * temperature)
@@ -277,11 +281,9 @@ class ConstantGas(AbstractGas):
class PressureGradientGas(AbstractGas): class PressureGradientGas(AbstractGas):
name: str
p_in: float p_in: float
p_out: float p_out: float
temperature: float temperature: float
gas_dico: dict[str, Any]
def __init__( def __init__(
self, self,
@@ -292,10 +294,10 @@ class PressureGradientGas(AbstractGas):
ideal_gas: bool, ideal_gas: bool,
wl_for_disp: np.ndarray, wl_for_disp: np.ndarray,
): ):
self.name = gas_name self.gas_name = gas_name
self.p_in = pressure_in self.p_in = pressure_in
self.p_out = pressure_out self.p_out = pressure_out
self.gas_dico = load_material_dico(gas_name) self.material_dico = load_material_dico(gas_name)
self.temperature = temperature self.temperature = temperature
self.ideal_gas = ideal_gas self.ideal_gas = ideal_gas
self.wl_for_disp = wl_for_disp self.wl_for_disp = wl_for_disp
@@ -310,12 +312,16 @@ class PressureGradientGas(AbstractGas):
return materials.number_density_van_der_waals( return materials.number_density_van_der_waals(
pressure=self.pressure(state), pressure=self.pressure(state),
temperature=self.temperature, temperature=self.temperature,
material_dico=self.gas_dico, material_dico=self.material_dico,
) )
def square_index(self, state: CurrentState) -> np.ndarray: def square_index(self, state: CurrentState) -> np.ndarray:
return materials.fast_n_gas_2( return materials.fast_n_gas_2(
self.wl_for_disp, self.pressure(state), self.temperature, self.ideal_gas, self.gas_dico self.wl_for_disp,
self.pressure(state),
self.temperature,
self.ideal_gas,
self.material_dico,
) )
@@ -793,7 +799,20 @@ class ConstantGamma(AbstractGamma):
class Plasma(Operator): class Plasma(Operator):
pass mat_plasma: plasma.Plasma
gas_op: AbstractGas
def __init__(self, dt: float, gas_op: AbstractGas):
self.gas_op = gas_op
self.mat_plasma = plasma.Plasma(
dt,
self.gas_op.material_dico["ionization_energy"],
self.gas_op.material_dico["atomic_number"],
)
def __call__(self, state: CurrentState) -> np.ndarray:
N0 = self.gas_op.number_density(state)
return self.mat_plasma(state.field, N0)
class NoPlasma(NoOpTime, Plasma): class NoPlasma(NoOpTime, Plasma):

View File

@@ -422,6 +422,13 @@ class Parameters:
def __repr_list__(self) -> Iterator[str]: def __repr_list__(self) -> Iterator[str]:
yield from (f"{k}={v}" for k, v in self.dump_dict().items()) yield from (f"{k}={v}" for k, v in self.dump_dict().items())
def __getstate__(self) -> dict[str, Any]:
return self.dump_dict(add_metadata=False)
def __setstate__(self, dumped_dict: dict[str, Any]):
self._param_dico = dumped_dict
self.__post_init__()
def dump_dict(self, compute=True, add_metadata=True) -> dict[str, Any]: def dump_dict(self, compute=True, add_metadata=True) -> dict[str, Any]:
if compute: if compute:
self.compute_in_place() self.compute_in_place()

View File

@@ -164,12 +164,11 @@ class RK4IP:
state = self.init_state.copy() state = self.init_state.copy()
yield len(self.stored_spectra) - 1, state yield len(self.stored_spectra) - 1, state
if self.params.adapt_step_size: if self.params.adapt_step_size:
integrator = solver.ConservedQuantityIntegrator( integrator = solver.RK4IPSD(
state, state,
self.params.linear_operator, self.params.linear_operator,
self.params.nonlinear_operator, self.params.nonlinear_operator,
self.params.tolerated_error, self.params.tolerated_error,
self.params.conserved_quantity,
) )
else: else:
integrator = solver.ConstantStepIntegrator( integrator = solver.ConstantStepIntegrator(

View File

@@ -4,9 +4,9 @@ import logging
from abc import abstractmethod from abc import abstractmethod
from typing import Iterator, Type from typing import Iterator, Type
import numba
import numpy as np import numpy as np
from . import math
from .logger import get_logger from .logger import get_logger
from .operators import ( from .operators import (
AbstractConservedQuantity, AbstractConservedQuantity,
@@ -196,7 +196,7 @@ class RK4IPSD(Integrator):
self.nonlinear_operator(new_fine_inter_state), self.nonlinear_operator(new_fine_inter_state),
) )
new_coarse = self.take_step(h, self.state.spectrum, lin, nonlin) new_coarse = self.take_step(h, self.state.spectrum, lin, nonlin)
self.current_error = self.compute_diff(new_coarse, new_fine) self.current_error = compute_diff(new_coarse, new_fine)
if self.current_error > 2 * self.tolerated_error: if self.current_error > 2 * self.tolerated_error:
h_next_step = h * 0.5 h_next_step = h * 0.5
@@ -218,42 +218,14 @@ class RK4IPSD(Integrator):
) -> np.ndarray: ) -> np.ndarray:
return rk4ip_step(self.nonlinear_operator, self.state, spec, h, lin, nonlin) return rk4ip_step(self.nonlinear_operator, self.state, spec, h, lin, nonlin)
def compute_diff(self, coarse_spec: np.ndarray, fine_spec: np.ndarray) -> float:
return np.sqrt(math.abs2(coarse_spec - fine_spec).sum() / math.abs2(fine_spec).sum())
def values(self) -> dict[str, float]: def values(self) -> dict[str, float]:
return dict( return dict(
step=self.state.step,
z=self.state.z, z=self.state.z,
local_error=self.current_error, local_error=self.current_error,
next_h_factor=self.next_h_factor,
) )
class ERK43(Integrator): class ERK43(RK4IPSD):
state: CurrentState
linear_operator: LinearOperator
nonlinear_operator: NonLinearOperator
tolerated_error: float
dt: float
current_error: float
next_h_factor = 1.0
def __init__(
self,
init_state: CurrentState,
linear_operator: LinearOperator,
nonlinear_operator: NonLinearOperator,
tolerated_error: float,
dw: float,
):
self.state = init_state
self.linear_operator = linear_operator
self.nonlinear_operator = nonlinear_operator
self.dw = dw
self.tolerated_error = tolerated_error
self.current_error = 0.0
def __iter__(self) -> Iterator[CurrentState]: def __iter__(self) -> Iterator[CurrentState]:
h_next_step = self.state.current_step_size h_next_step = self.state.current_step_size
k5 = self.nonlinear_operator(self.state) k5 = self.nonlinear_operator(self.state)
@@ -276,13 +248,17 @@ class ERK43(Integrator):
new_coarse = r + h / 30 * (2 * k4 + 3 * tmp_k5) new_coarse = r + h / 30 * (2 * k4 + 3 * tmp_k5)
self.current_error = np.sqrt(self.dw * math.abs2(new_fine - new_coarse).sum()) self.current_error = compute_diff(new_coarse, new_fine)
self.next_h_factor = max( if self.current_error > 0.0:
next_h_factor = max(
0.5, min(2.0, (self.tolerated_error / self.current_error) ** 0.25) 0.5, min(2.0, (self.tolerated_error / self.current_error) ** 0.25)
) )
h_next_step = self.next_h_factor * h else:
if self.current_error <= self.tolerated_error: next_h_factor = 2.0
h_next_step = next_h_factor * h
if self.current_error <= 2 * self.tolerated_error:
break break
h_next_step = min(0.9, next_h_factor) * h
self.logger.info( self.logger.info(
f"step {self.state.step} rejected : {h=}, {self.current_error=}, {h_next_step=}" f"step {self.state.step} rejected : {h=}, {self.current_error=}, {h_next_step=}"
) )
@@ -291,16 +267,8 @@ class ERK43(Integrator):
self.state.spectrum = new_fine self.state.spectrum = new_fine
yield self.accept_step(self.state, h, h_next_step) yield self.accept_step(self.state, h, h_next_step)
def values(self) -> dict[str, float]:
return dict(
step=self.state.step,
z=self.state.z,
local_error=self.current_error,
next_h_factor=self.next_h_factor,
)
class ERK54(RK4IPSD):
class ERK54(ERK43):
def __iter__(self) -> Iterator[CurrentState]: def __iter__(self) -> Iterator[CurrentState]:
self.logger.info("using ERK54") self.logger.info("using ERK54")
h_next_step = self.state.current_step_size h_next_step = self.state.current_step_size
@@ -315,29 +283,31 @@ class ERK54(ERK43):
expD4m = 1 / expD4p expD4m = 1 / expD4p
A_I = expD2 * self.state.spectrum A_I = expD2 * self.state.spectrum
k1 = expD2 * k7 k1 = h * expD2 * k7
k2 = self.nl(A_I + 0.5 * h * k1) k2 = h * self.nl(A_I + 0.5 * k1)
k3 = expD4p * self.nl(expD4m * (A_I + h / 16 * (3 * k1 + k2))) k3 = h * expD4p * self.nl(expD4m * (A_I + 0.0625 * (3 * k1 + k2)))
k4 = self.nl(A_I + h / 4 * (k1 - k2 + 4 * k3)) k4 = h * self.nl(A_I + 0.25 * (k1 - k2 + 4 * k3))
k5 = expD4m * self.nl(expD4p * (A_I + 3 * h / 16 * (k1 + 3 * k4))) k5 = h * expD4m * self.nl(expD4p * (A_I + 0.1875 * (k1 + 3 * k4)))
k6 = self.nl(expD2 * (A_I + h / 7 * (-2 * k1 + k2 + 12 * k3 - 12 * k4 + 8 * k5))) k6 = h * self.nl(
expD2 * (A_I + 1 / 7 * (-2 * k1 + k2 + 12 * k3 - 12 * k4 + 8 * k5))
)
new_fine = ( new_fine = (
expD2 * (A_I + h / 90 * (7 * k1 + 32 * k3 + 12 * k4 + 32 * k5)) expD2 * (A_I + 1 / 90 * (7 * k1 + 32 * k3 + 12 * k4 + 32 * k5)) + 7 / 90 * k6
+ 7 * h / 90 * k6
) )
tmp_k7 = self.nl(new_fine) tmp_k7 = self.nl(new_fine)
new_coarse = ( new_coarse = (
expD2 * (A_I + h / 42 * (3 * k1 + 16 * k3 + 4 * k4 + 16 * k5)) + h / 14 * k7 expD2 * (A_I + 1 / 42 * (3 * k1 + 16 * k3 + 4 * k4 + 16 * k5)) + h / 14 * k7
) )
self.current_error = np.sqrt(self.dw * math.abs2(new_fine - new_coarse).sum()) self.current_error = compute_diff(new_coarse, new_fine)
self.next_h_factor = max( next_h_factor = max(
0.5, min(2.0, (self.tolerated_error / self.current_error) ** 0.25) 0.5, min(2.0, (self.tolerated_error / self.current_error) ** 0.2)
) )
h_next_step = self.next_h_factor * h h_next_step = next_h_factor * h
if self.current_error <= self.tolerated_error: if self.current_error <= 2 * self.tolerated_error:
break break
h_next_step = min(0.9, next_h_factor) * h
self.logger.info( self.logger.info(
f"step {self.state.step} rejected : {h=}, {self.current_error=}, {h_next_step=}" f"step {self.state.step} rejected : {h=}, {self.current_error=}, {h_next_step=}"
) )
@@ -385,3 +355,10 @@ def rk4ip_step(
k4 = h * nonlinear_operator(init_state.replace(expD * (A_I + k3))) k4 = h * nonlinear_operator(init_state.replace(expD * (A_I + k3)))
return expD * (A_I + k1 / 6 + k2 / 3 + k3 / 3) + k4 / 6 return expD * (A_I + k1 / 6 + k2 / 3 + k3 / 3) + k4 / 6
@numba.jit(nopython=True)
def compute_diff(coarse_spec: np.ndarray, fine_spec: np.ndarray) -> float:
diff = coarse_spec - fine_spec
diff2 = diff.imag ** 2 + diff.real ** 2
return np.sqrt(diff2.sum() / (fine_spec.real ** 2 + fine_spec.imag ** 2).sum())