Source code for struphy.propagators.time_dependent_source
import logging
from dataclasses import dataclass
from typing import Literal
import cunumpy as xp
from line_profiler import profile
from struphy.io.options import OptionsBase
from struphy.models.variables import FEECVariable
from struphy.propagators.base import Propagator
from struphy.utils.utils import check_option
logger = logging.getLogger("struphy")
[docs]
class TimeDependentSource(Propagator):
r"""Propagates a source term :math:`S(t) \in V_h^n` of the form
.. math::
S(t) = \sum_{ijk} c_{ijk} \Lambda^n_{ijk} * h(\omega t)\,,
where :math:`h(\omega t)` is one of the functions in Notes.
Notes
-----
* :math:`h(\omega t) = \cos(\omega t)` (default)
* :math:`h(\omega t) = \sin(\omega t)`
"""
[docs]
class Variables:
"""Container for variables advanced by :class:`TimeDependentSource`.
Attributes
----------
source : FEECVariable
Source coefficient field in ``"H1"`` space.
"""
def __init__(self):
self._source: FEECVariable = None
@property
def source(self) -> FEECVariable:
return self._source
@source.setter
def source(self, new):
assert isinstance(new, FEECVariable)
assert new.space == "H1"
self._source = new
def __init__(self):
self.variables = self.Variables()
[docs]
@dataclass(repr=False)
class Options(OptionsBase):
"""Configuration options for :class:`TimeDependentSource`.
Parameters
----------
omega : float, default=2*pi
Angular frequency of the time-dependent modulation.
hfun : {"cos", "sin"}, default="cos"
Time modulation function applied to initial coefficients.
"""
# specific literals
OptsTimeSource = Literal["cos", "sin"]
# propagator options
omega: float = 2.0 * xp.pi
hfun: OptsTimeSource = "cos"
def __post_init__(self):
# checks
check_option(self.hfun, self.OptsTimeSource)
@property
def options(self) -> Options:
if not hasattr(self, "_options"):
self._options = self.Options()
return self._options
@options.setter
def options(self, new):
assert isinstance(new, self.Options)
self._options = new
logger.info(f"\nNew options for propagator '{self.__class__.__name__}':\n{self._options}")
@profile
def allocate(self):
if self.options.hfun == "cos":
def hfun(t):
return xp.cos(self.options.omega * t)
elif self.options.hfun == "sin":
def hfun(t):
return xp.sin(self.options.omega * t)
else:
raise NotImplementedError(f"{self.options.hfun =} not implemented.")
self._hfun = hfun
self._c0 = self.variables.source.spline.vector.copy()
@profile
def __call__(self, dt):
# new coeffs
cn1 = self._c0 * self._hfun(self.time_state[0])
# write new coeffs into self.feec_vars
# max_dc = self.feec_vars_update(cn1)
self.update_feec_variables(source=cn1)