LCOV - code coverage report
Current view: top level - src - interface.jl (source / functions) Hit Total Coverage
Test: on branch nothing Lines: 59 65 90.8 %
Date: 2025-02-26 15:13:49 Functions: 0 0 -

          Line data    Source code
       1             : """
       2             : Represent an ODE problem (without initial conditions) as
       3             : - [`Solver`](@ref)
       4             : - function `dy(t, y)` (see [Evaluation and solver state](@ref))
       5             : - [`RK43.AbstractOutput`](@ref), possibly [`pipeline`](@ref)
       6             : 
       7             : See also [`problem`](@ref)
       8             : """
       9             : struct Problem{N, T, S, Dy, Out}
      10             :     solver::Solver{N, T, S}
      11             :     dy::WrapDy{Dy, N, T}
      12             :     output::Out
      13             : 
      14        5865 :     function Problem{N, T, S, Dy, Out}(solver::Solver{N, T, S},
      15             :         dy::WrapDy{Dy, N, T},
      16             :         output::Out) where {N, T, S, Dy, Out}
      17        5865 :         new(solver, dy, output)
      18             :     end
      19             : end
      20             : 
      21           1 : function Base.show(io::IO,
      22             :                    p::Problem{N, T, S, Dy, Out}) where {N, T, S, Dy, Out}
      23           1 :     print(io, "Problem{")
      24           2 :     show(io, p.solver)
      25           1 :     print(io, ", ")
      26           1 :     show(io, p.output)
      27           1 :     print(io, "}")
      28             : end
      29             : 
      30          45 : function problem(solver::Solver{N, T, S},
      31             :                  dy::Dy,
      32             :                  output::Out=nooutput()) where {N, T, S, Dy, Out}
      33          45 :     Problem{N, T, S, Dy, Out}(solver, wrap(dy, SVector{N, T}), output)
      34             : end
      35             : 
      36           0 : function problem(solver::Solver{N, T, S},
      37             :                  dy::WrapDy{Dy, N, T},
      38             :                  output::Out=nooutput()) where {N, T, S, Dy, Out}
      39           0 :     Problem{N, T, S, Dy, Out}(solver, dy, output)
      40             : end
      41             : 
      42         108 : function problem(p::Problem{N, T, S, Dy, _Out};
      43             :                  replace::Out) where {N, T, S, Dy, _Out, Out}
      44          54 :     Problem{N, T, S, Dy, Out}(p.solver, p.dy, replace)
      45             : end
      46             : 
      47        4889 : function problem(p::Problem{N, T, S, Dy, Out0},
      48             :                  out::Out) where {N, T, S, Dy, Out0, Out}
      49        4889 :     output = pipeline(p.output, out)
      50        4889 :     Problem{N, T, S, Dy, typeof(output)}(p.solver, p.dy, output)
      51             : end
      52             : 
      53             : """
      54             :     p = problem(solver, dy[, output])
      55             :     p = problem(p0::Problem, ouput)           # adds output to pipeline
      56             :     p = problem(p0::Problem; replace=output)  # replaces output
      57             : 
      58             : See also [`RK43.Solver`](@ref), [`RK43.AbstractOutput`](@ref),
      59             : [`pipeline`](@ref), [`Problem`](@ref)
      60             : 
      61             : !!! note
      62             :     `replace=output` exchanges the full [`pipeline`](@ref) including
      63             :     any [`callback`](@ref)s or predicates!
      64             : 
      65             : !!! warning
      66             :     This method does **not** copy the `solver` instance, i.e.,
      67             :     problem may share the same solver state. Use [`Base.similar`](@ref)
      68             :     in case an independent solver (state) is required (e.g., for
      69             :     [`solve!`](@ref) in *multiple threads*).
      70             : 
      71             : See also [`Base.similar`](@ref)
      72             : """ problem
      73             : 
      74             : """
      75             :     p = similar(p0::Problem)
      76             : 
      77             : Construct a "copy" of `p0` without associated output
      78             : ([`nooutput`](@ref)).
      79             : 
      80             : !!! note
      81             :     This method provides a new and uninitialized solver instance, which
      82             :     may be required, e.g., for [`solve!`](@ref) in *multiple threads*.
      83             : 
      84             : !!! warning
      85             :     This method does **not** (and cannot) provide a "copy" of the
      86             :     function `dy`: The user is responsible for defining `dy`
      87             :     **without side effects** (or taking into account, e.g.,
      88             :     `Threads.threadid()` "within" `dy`).
      89             : 
      90             : See also [`problem`](@ref)
      91             : """
      92         894 : Base.similar(p::Problem{N, T, S, Dy}) where {N, T, S, Dy} =
      93             :     Problem{N, T, S, Dy, NoOutput}(similar(p.solver), p.dy, NoOutput())
      94             : 
      95           1 : set!(p::Problem{N, T, S, Dy}, opts::SolverOptions{T}) where {N, T, S, Dy} =
      96             :     set!(p.solver, opts)
      97             : 
      98             : """
      99             :     opts = options(...)
     100             :     set!(solver, opts)
     101             :     set!(problem, opts)
     102             : 
     103             : Change [`SolverOptions`](@ref)
     104             : 
     105             : See also [`options`](@ref)
     106             : """ set!
     107             : 
     108        1732 : getsolver(p::Problem{N, T, S, Dy}) where {N, T, S, Dy} = p.solver
     109             : 
     110             : """
     111             :     solver = getsolver(problem)
     112             : 
     113             : Get solver from `problem`.
     114             : 
     115             : See also [`problem`](@ref)
     116             : """ getsolver
     117             : 
     118           0 : getoutput(p::Problem{N, T, S, Dy}) where {N, T, S, Dy} = p.output
     119             : 
     120             : """
     121             :     output = getoutput(problem)
     122             : 
     123             : Get output from `problem`.
     124             : 
     125             : See also [`problem`](@ref)
     126             : """ getoutput
     127             : 
     128       13213 : issuccess(p::Problem) = issuccess(p.solver)
     129             : 
     130             : """
     131             :     issuccess(solver)
     132             :     issuccess(problem)
     133             : 
     134             : Does `solver` or solver associated with `problem` have [`RK43.State`](@ref)
     135             : `AcceptStep` (equals `RK43.Ok`), i.e., can we continue with the next
     136             : step (or [`isdone`](@ref))?
     137             : 
     138             : See also [`Solver`](@ref), [`problem`](@ref)
     139             : """ issuccess
     140             : 
     141           2 : isinitialized(p::Problem) = isinitialized(p.solver)
     142             : 
     143             : """
     144             :     isinitialized(problem)
     145             : 
     146             : Has `solver` or solver associated with `problem` been initialized
     147             : by [`initialize!`](ref)?
     148             : 
     149             : See also [`problem`](@ref), [`Solver`](@ref), [`initialize!`](ref)
     150             : """
     151             : isinitialized
     152             : 
     153       12261 : initialize!(p::Problem{N, T, S, Dy, Out},
     154             :            t0::T, t1::T, y0::SVector{N, T}) where {N, T, S, Dy, Out} =
     155             :     initialize!(p.solver, p.dy, t0, t1, y0)
     156             : 
     157             : """
     158             :     state = initialize!(solver, dy, t0, t1, y0)
     159             :     state = initialize!(problem, t0, t1, y0)
     160             : 
     161             : Initialize `solver` or solver associated with `problem` for
     162             : subsequent call(s) to [`integrate`](@ref) or [`step`](@ref) or
     163             : [`solve!`](@ref), respectively.
     164             : 
     165             : See also [`problem`](@ref), [`Solver`](@ref), [`isinitialized`](@ref),
     166             : [`RK43.State`](@Ref)
     167             : """
     168             : initialize!
     169             : 
     170           3 : state(p::Problem{N, T, S, Dy, Out}) where {N, T, S, Dy, Out} = state(p.solver)
     171             : 
     172             : """
     173             :     s = state(solver)
     174             :     s = state(problem)
     175             : 
     176             : Query [`RK43.State`](@ref) of `solver` or solver associated with `problem`.
     177             : 
     178             : See also [`problem`](@ref), [`Solver`](@ref), [`RK43.State`](@ref),
     179             : [`issuccess`](@ref)
     180             : """ state
     181             : 
     182         204 : isforward(p::Problem) = isforward(p.solver)
     183             : 
     184             : """
     185             :      isforward(solver)
     186             :      isforward(problem)
     187             : 
     188             : Is integration in *forward direction* (i.e., increasing time) for `solver` or
     189             : solver associated with `problem`?
     190             : 
     191             : !!! warning "precondition"
     192             :     The solver must be initialized (i.e., have `t0` and `t1` defined),
     193             :     or the result is undefined.
     194             : 
     195             : See also [`problem`](@ref), [`Solver`](@ref)
     196             : """ isforward
     197             : 
     198        2051 : isdone(p::Problem) = isdone(p.solver)
     199             : 
     200             : """
     201             :      isdone(solver)
     202             :      isdone(problem)
     203             : 
     204             : Is integration finished (i.e., reached destination) for `solver` or
     205             : solver associated with `problem`?
     206             : 
     207             : !!! warning "precondition"
     208             :     The solver must be initialized (i.e., have `t0` and `t1` defined),
     209             :     or the result is undefined.
     210             : 
     211             : See also [`problem`](@ref), [`Solver`](@ref)
     212             : """ isdone
     213             : 
     214        7755 : pos(p::Problem) = pos(p.solver)
     215             : 
     216             : """
     217             :      pos(solver)
     218             :      pos(problem)
     219             : 
     220             : Get position tuple `t, y, dy` ,for `solver` or solver associated with
     221             : `problem`.
     222             : 
     223             : See also [`problem`](@ref), [`Solver`](@ref)
     224             : """ pos
     225             : 
     226        1064 : emptysolution(::Solver{N, T, S}) where {N, T, S} = hermitespline(SVector{N, T})
     227             : 
     228        1050 : emptysolution(p::Problem{N, T, S, Dy, Out}) where {N, T, S, Dy, Out} =
     229             :     emptysolution(p.solver)
     230             : 
     231             : """
     232             :     sol = emptysolution(solver)
     233             :     sol = emptysolution(problem)
     234             : 
     235             : Construct empty solution for `solver` or solvler associated with
     236             : `problem`.
     237             : 
     238             : Solutions are represented as splines, i.e., piecewise polynomial curves,
     239             : like [`HermiteSpline`](https://visual2.cs.ovgu.de/roessl/HermiteSpline.jl)
     240             : 
     241             : !!! note
     242             :     `emptysolution` determines the (data) *type* of the solution from
     243             :     a solver (or problem).
     244             : 
     245             : See also [`problem`](@ref), [`Solver`](@ref)
     246             : """ emptysolution
     247             : 
     248       12259 : function solve!(p::Problem{N, T, S, Dy, Out},
     249             :                 t0::T, t1::T, y0::SVector{N, T}) where {N, T, S, Dy, Out}
     250       12259 :     initialize!(p, t0, t1, y0)
     251       12259 :     solve!(p)
     252             : end
     253             : 
     254        4886 : function solve!(p0::Problem{N, T, S, Dy, Out0},
     255             :                 t0::T, t1::T, y0::SVector{N, T},
     256             :                 out::Out) where {N, T, S, Dy, Out0, Out}
     257        4886 :     solve!(problem(p0, out), t0, t1, y0)
     258             : end
     259             : 
     260             : const MAXSTEPS = 10000 # fallback if opts.maxsteps == 0
     261             : 
     262       12260 : function solve!(p::Problem{N, T, S, Dy, Out}) where {N, T, S, Dy, Out}
     263       12260 :     @massert isinitialized(p) "missing initialization"
     264             : 
     265       12260 :     issuccess(p) || return state(p)
     266             : 
     267       12260 :     solver = p.solver
     268             : 
     269       12260 :     maxsteps = max(MAXSTEPS, solver.opts.maxsteps)
     270       17136 :     ostate = _check_output(p.output(solver))
     271             : 
     272           0 :     @massert issuccess(ostate) "output() must succeed for initial point ($(solver.t),$(solver.y))"
     273             : 
     274      881961 :     while !isdone(solver) && (solver.nsteps < maxsteps)
     275      869953 :         newstate = step!(solver, p.dy)
     276             : 
     277      870192 :         if issuccess(newstate) || (newstate == OutOfDomain)
     278     1061549 :           ostate = _check_output(p.output(solver))
     279             : 
     280      869953 :           if !issuccess(ostate)
     281         325 :               if ostate == Stopped
     282           4 :                   newstate = Stopped
     283             :                   # TODO [alg:upstream] really commit after Stopped
     284             :                   # TODO [alg:upstream] enable callback to modify step
     285         321 :               elseif ostate == OutOfDomain
     286             :                   # NOTE: step! handles bisection as well, p.output may "recheck"
     287         320 :                   solver.absh = bisectedstepsize(solver)
     288         320 :                   if solver.absh > hmin(solver)
     289         312 :                       solver.nsteps -= 1
     290         312 :                       solver.rejected = true
     291         312 :                       continue                    # repeat step
     292             :                   end
     293             : 
     294           8 :                   newstate = ostate
     295             :               else
     296           1 :                   @error "invalid state $(ostate): output() may indicate Stop or OutOfDomain!"
     297           1 :                   return (solver.state = Failed)
     298             :               end
     299             :           end
     300             : 
     301      869640 :           commit!(solver)
     302             :         end
     303             : 
     304      869891 :         issuccess(newstate) || return (solver.state = newstate)
     305      869701 :     end #  while !done
     306             : 
     307             :     # Note: special case for output if done !?!
     308             : 
     309       12008 :     if solver.nsteps > maxsteps
     310           0 :         @warn "exceeded maximum number of $(maxsteps) steps"
     311           0 :         return (solver.state = Stopped)
     312             :     end
     313             : 
     314       12008 :     solver.state
     315             : end
     316             : 
     317             : """
     318             :     state = solve!(problem)                # requires initialize!(problem,...)
     319             :     state = solve!(problem, t0, t1, y0)
     320             :     state = solve!(problem, t0, t1, y0, output) # solve with additional output
     321             : 
     322             : Solve `problem` and return [`RK43.State`](@ref). The solution is constructed
     323             : (as a "side-effect") from the [`RK43.AbstractOutput`](@ref) specified by `problem`.
     324             : 
     325             : !!! note
     326             :     If the solver stops before reaching `t1` (e.g., due to an `OutOfDomain`),
     327             :     There will be *no output* after the `tend = first(pos(solver))`.
     328             : 
     329             : See also [`problem`](@ref), [`RK43.state`](@ref), [`initialize!`](@ref)
     330             : """ solve!

Generated by: LCOV version 1.16