Source code for cubie.integrators.step_control.fixed_step_controller

"""Fixed step-size controller.

Published Classes
-----------------
:class:`FixedStepControlConfig`
    Configuration container for fixed-step controllers.

    >>> from numpy import float32
    >>> config = FixedStepControlConfig(precision=float32, dt=1e-3)
    >>> config.dt
    0.001

:class:`FixedStepController`
    Controller that enforces a constant time step.

    >>> from numpy import float64
    >>> ctrl = FixedStepController(precision=float64, dt=0.01)
    >>> ctrl.is_adaptive
    False

See Also
--------
:class:`~cubie.integrators.step_control.base_step_controller.BaseStepController`
    Abstract base class for all controllers.
:class:`~cubie.integrators.step_control.base_step_controller.BaseStepControllerConfig`
    Base configuration class.
"""

from attrs import field, frozen
from cubie.cuda_simsafe import cuda, int32
from cubie.result_codes import CUBIE_RESULT_CODES

from cubie._utils import getype_validator
from cubie.integrators.step_control.base_step_controller import (
    BaseStepControllerConfig,
    BaseStepController,
    ControllerCache,
)

DEFAULT_FIXED_DT = 1e-3
"""Fixed step when none is given."""


[docs] @frozen class FixedStepControlConfig(BaseStepControllerConfig): """Configuration for fixed-step integrator loops. Attributes ---------- precision Precision used for numerical operations. n_states Number of state variables controlled per step. atol Absolute tolerance vector inherited from the base config. The fixed controller never rejects steps, but implicit algorithms derive their inner-solver tolerances from it. rtol Relative tolerance vector, on the same terms as ``atol``. """ _dt: float = field( default=DEFAULT_FIXED_DT, validator=getype_validator(float, 0) ) @property def dt(self) -> float: """Return the fixed step size.""" return self.precision(self._dt) @property def dt_min(self) -> float: """Return the minimum time step size.""" return self.dt @property def dt_max(self) -> float: """Return the maximum step size.""" return self.dt @property def is_adaptive(self) -> bool: """Return ``False`` because the controller is not adaptive.""" return False
[docs] class FixedStepController(BaseStepController): """Controller that enforces a constant time step.""" _config_class = FixedStepControlConfig
[docs] def compile_controller(self) -> ControllerCache: """Return a device function that always accepts with fixed step. Returns ------- ControllerCache Cache containing the compiled fixed-step device function. """ success = int32(CUBIE_RESULT_CODES.SUCCESS) # no cover: start @cuda.jit( device=True, inline=True, **self.jit_kwargs, ) def controller_fixed_step( dt, state, state_prev, error, niters, truncated, accept_out, shared_scratch, persistent_local, ): # pragma: no cover - CUDA """Fixed-step controller device function. Parameters ---------- dt : device array Current integration step size. state : device array Current state vector. state_prev : device array Previous state vector. error : device array Estimated local error vector. niters : int32 Iteration counters from the integrator loop. truncated : bool True when the loop forced the step onto an output boundary. Unused. accept_out : device array Output flag indicating acceptance of the step. shared_scratch : device array Shared memory scratch space. persistent_local : device array Persistent local memory for controller state. Returns ------- int32 Zero, indicating that the current step size should be kept. """ accept_out[0] = int32(1) return success # no cover: end return ControllerCache(step_controller_fn=controller_fixed_step)