diff --git a/src/clock.jl b/src/clock.jl index c80a32463..1e02a5183 100644 --- a/src/clock.jl +++ b/src/clock.jl @@ -47,30 +47,60 @@ discrete-time systems that assume a fixed sample time, such as PID controllers a filters. """ SolverStepClock +""" + isclock(clock) + +Returns `true` if the object is a valid clock type (specifically a `PeriodicClock`). +This function is used for type checking in clock-dependent logic. +""" isclock(c::Clocks.Type) = @match c begin PeriodicClock() => true _ => false end isclock(::TimeDomain) = false +""" + issolverstepclock(clock) + +Returns `true` if the clock is a `SolverStepClock` that triggers at every solver step. +This is useful for monitoring solver progress or implementing step-dependent logic. +""" issolverstepclock(c::Clocks.Type) = @match c begin SolverStepClock() => true _ => false end issolverstepclock(::TimeDomain) = false +""" + iscontinuous(clock) + +Returns `true` if the clock operates in continuous time (i.e., is a `ContinuousClock`). +Continuous clocks allow events to occur at any real-valued time instant. +""" iscontinuous(c::Clocks.Type) = @match c begin ContinuousClock() => true _ => false end iscontinuous(::TimeDomain) = false +""" + iseventclock(clock) + +Returns `true` if the clock is an `EventClock` that triggers based on specific events. +Event clocks are used for condition-based triggering in hybrid systems. +""" iseventclock(c::Clocks.Type) = @match c begin EventClock() => true _ => false end iseventclock(::TimeDomain) = false +""" + is_discrete_time_domain(clock) + +Returns `true` if the clock operates in discrete time (i.e., is not a continuous clock). +Discrete time domains have specific sampling intervals or event-based triggering. +""" is_discrete_time_domain(c::TimeDomain) = !iscontinuous(c) # workaround for https://github.com/Roger-luo/Moshi.jl/issues/43 diff --git a/src/function_wrappers.jl b/src/function_wrappers.jl index fc5a390f7..214d38cd7 100644 --- a/src/function_wrappers.jl +++ b/src/function_wrappers.jl @@ -1,5 +1,35 @@ +""" + AbstractWrappedFunction{iip} + +Abstract base type for function wrappers used in automatic differentiation and sensitivity analysis. +These wrappers provide specialized interfaces for computing derivatives with respect to different variables. + +# Type Parameter +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +""" abstract type AbstractWrappedFunction{iip} end isinplace(f::AbstractWrappedFunction{iip}) where {iip} = iip + +""" + TimeGradientWrapper{iip, fType, uType, P} <: AbstractWrappedFunction{iip} + +Wraps functions to compute gradients with respect to time. This wrapper is particularly useful for +sensitivity analysis and optimization problems where the time dependence of the solution is critical. + +# Fields +- `f`: The function to wrap +- `uprev`: Previous state value +- `p`: Parameters + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `fType`: Type of the wrapped function +- `uType`: Type of the state variables +- `P`: Type of the parameters + +This wrapper enables automatic differentiation with respect to time by providing a consistent +interface for computing `∂f/∂t` across different AD systems. +""" mutable struct TimeGradientWrapper{iip, fType, uType, P} <: AbstractWrappedFunction{iip} f::fType uprev::uType @@ -20,6 +50,27 @@ end (ff::TimeGradientWrapper{false})(t) = ff.f(ff.uprev, ff.p, t) +""" + UJacobianWrapper{iip, fType, tType, P} <: AbstractWrappedFunction{iip} + +Wraps functions to compute Jacobians with respect to state variables `u`. This is one of the most +commonly used wrappers in the SciML ecosystem for computing the derivative of the right-hand side +function with respect to the state variables. + +# Fields +- `f`: The function to wrap +- `t`: Time value +- `p`: Parameters + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `fType`: Type of the wrapped function +- `tType`: Type of the time variable +- `P`: Type of the parameters + +This wrapper enables efficient computation of `∂f/∂u` for Jacobian calculations in numerical solvers +and automatic differentiation systems. +""" mutable struct UJacobianWrapper{iip, fType, tType, P} <: AbstractWrappedFunction{iip} f::fType t::tType @@ -43,6 +94,26 @@ end (ff::UJacobianWrapper{false})(uprev) = ff.f(uprev, ff.p, ff.t) (ff::UJacobianWrapper{false})(uprev, p, t) = ff.f(uprev, p, t) +""" + TimeDerivativeWrapper{iip, F, uType, P} <: AbstractWrappedFunction{iip} + +Wraps functions to compute derivatives with respect to time. This wrapper is used when you need to +compute `∂f/∂t` for sensitivity analysis or when the function has explicit time dependence. + +# Fields +- `f`: The function to wrap +- `u`: State variables +- `p`: Parameters + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `F`: Type of the wrapped function +- `uType`: Type of the state variables +- `P`: Type of the parameters + +This wrapper provides a consistent interface for time derivative computations across different +automatic differentiation backends. +""" mutable struct TimeDerivativeWrapper{iip, F, uType, P} <: AbstractWrappedFunction{iip} f::F u::uType @@ -60,6 +131,26 @@ end (ff::TimeDerivativeWrapper{true})(du1, t) = ff.f(du1, ff.u, ff.p, t) (ff::TimeDerivativeWrapper{true})(t) = (du1 = similar(ff.u); ff.f(du1, ff.u, ff.p, t); du1) +""" + UDerivativeWrapper{iip, F, tType, P} <: AbstractWrappedFunction{iip} + +Wraps functions to compute derivatives with respect to state variables. This wrapper is used for +computing `∂f/∂u` and is fundamental for Jacobian computations in numerical solvers. + +# Fields +- `f`: The function to wrap +- `t`: Time value +- `p`: Parameters + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `F`: Type of the wrapped function +- `tType`: Type of the time variable +- `P`: Type of the parameters + +This wrapper enables efficient state derivative computations for use in automatic differentiation +and numerical analysis algorithms. +""" mutable struct UDerivativeWrapper{iip, F, tType, P} <: AbstractWrappedFunction{iip} f::F t::tType @@ -75,6 +166,26 @@ UDerivativeWrapper(f::F, t, p) where {F} = UDerivativeWrapper{isinplace(f, 4)}(f (ff::UDerivativeWrapper{true})(du1, u) = ff.f(du1, u, ff.p, ff.t) (ff::UDerivativeWrapper{true})(u) = (du1 = similar(u); ff.f(du1, u, ff.p, ff.t); du1) +""" + ParamJacobianWrapper{iip, fType, tType, uType} <: AbstractWrappedFunction{iip} + +Wraps functions to compute Jacobians with respect to parameters `p`. This wrapper is essential for +parameter estimation, inverse problems, and sensitivity analysis with respect to model parameters. + +# Fields +- `f`: The function to wrap +- `t`: Time value +- `u`: State variables + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `fType`: Type of the wrapped function +- `tType`: Type of the time variable +- `uType`: Type of the state variables + +This wrapper enables efficient computation of `∂f/∂p` for parameter sensitivity analysis and +optimization algorithms. +""" mutable struct ParamJacobianWrapper{iip, fType, tType, uType} <: AbstractWrappedFunction{iip} f::fType t::tType @@ -97,6 +208,24 @@ function (ff::ParamJacobianWrapper{false})(du1, p) du1 .= ff.f(ff.u, p, ff.t) end +""" + JacobianWrapper{iip, fType, pType} <: AbstractWrappedFunction{iip} + +A general-purpose Jacobian wrapper that can be configured for different types of Jacobian computations. +This wrapper provides a unified interface for various Jacobian calculations across the SciML ecosystem. + +# Fields +- `f`: The function to wrap +- `p`: Parameters + +# Type Parameters +- `iip`: Boolean indicating if the function is in-place (`true`) or out-of-place (`false`) +- `fType`: Type of the wrapped function +- `pType`: Type of the parameters + +This wrapper provides a flexible interface for Jacobian computations that can adapt to different +automatic differentiation backends and numerical methods. +""" mutable struct JacobianWrapper{iip, fType, pType} <: AbstractWrappedFunction{iip} f::fType p::pType diff --git a/src/integrator_interface.jl b/src/integrator_interface.jl index 3b1376285..d3c1122ad 100644 --- a/src/integrator_interface.jl +++ b/src/integrator_interface.jl @@ -50,16 +50,50 @@ The length of the tuple is dependent on the method. function get_tmp_cache(i::DEIntegrator) error("get_tmp_cache!: method has not been implemented for the integrator") end +""" + user_cache(integrator::DEIntegrator) + +Returns user-accessible cache components from the integrator. These are cache arrays that users +can safely access and modify without breaking the internal integrator state. +""" function user_cache(i::DEIntegrator) error("user_cache: method has not been implemented for the integrator") end + +""" + u_cache(integrator::DEIntegrator) + +Returns the state variable cache arrays used by the integrator. These contain intermediate +state values during the integration process. +""" function u_cache(i::DEIntegrator) error("u_cache: method has not been implemented for the integrator") end + +""" + du_cache(integrator::DEIntegrator) + +Returns the derivative cache arrays used by the integrator. These contain intermediate +derivative values during the integration process. +""" function du_cache(i::DEIntegrator) error("du_cache: method has not been implemented for the integrator") end + +""" + ratenoise_cache(integrator::DEIntegrator) + +Returns cache arrays for rate noise in stochastic differential equations. +Returns an empty tuple by default for deterministic problems. +""" ratenoise_cache(i::DEIntegrator) = () + +""" + rand_cache(integrator::DEIntegrator) + +Returns cache arrays for random number generation in stochastic differential equations. +Returns an empty tuple by default for deterministic problems. +""" rand_cache(i::DEIntegrator) = () """