API

The exported types and functions are the public API. The internal helpers are listed for reference and may change without notice.

NSDETimeParallel.AbstractPararealBackend — Type
AbstractPararealBackend

How the fine sweep of Parareal executes. The algorithm lives ONCE in parareal/parareal.jl; a backend supplies only these primitives:

  • is_root(backend) — whether this process runs the serial parts (coarse init, correction, convergence check). true everywhere except non-root MPI ranks.
  • sync_starts!(backend, cache, k) — make the chunk starting values U[n] available wherever chunk n will be solved. No-op off MPI.
  • fine_map!(backend, cache, solution, problem, parareal, k; …) — solve chunks k:N with the fine solver; deposit boundary values into cache.F[n+1] on the root and chunk solutions into solution.lastiterate (MPI ranks keep their chunk local until collect_chunks!).
  • snapshot!(backend, solution, k; …) — record iterate k when saveiterates is on. In memory by default; on MPI the ranks have already written their chunk to the run directory inside fine_map!.
  • sync_converged(backend, flag) — agree the convergence decision.
  • collect_chunks!(backend, solution, cache; …) — bring final chunks (and, under MPI with saveiterates, the per-iterate history) to the root.
source
NSDETimeParallel.PararealCache — Type
PararealCache <: AbstractTimeParallelCache

Pre-allocated state for one Parareal run: the chunk boundary values U, the fine and coarse boundary results F and G, the previous-iterate snapshot U_ for the error function, the chunk time grid T, the chunk problems (built once, their u0 slots referencing U so the correction updates them in place), and reusable solver caches. Method-dependent fields can be replaced when switching solver, RHS, or weight-rule types; the state shape and chunk count stay fixed. Each run refreshes the initial state from the supplied problem. Moving a cache to a different span requires shiftwindow!. RHS changes rebuild the chunk caches; do not mutate RHS internals in place while retaining their solver caches.

source
NSDETimeParallel.PararealIterate — Type
PararealIterate <: AbstractTimeParallelIterate

A composite type for a single iterate in a PararealSolution: one fine chunk solution per time chunk. The chunk storage is CONCRETE — chunks are built eagerly from the fine solver, so eltype(iterate.chunks) is the fine solver's concrete solution type, not an abstract box. This matters: these objects cross thread and process boundaries in the hot path.

Constructors

PararealIterate(chunks::AbstractVector{𝕊}) where 𝕊<:AbstractInitialValueSolution
PararealIterate(problem::AbstractInitialValueProblem, parareal::Parareal)

Functions

Methods

(iterate::PararealIterate)(t::Real)

returns the value of iterate at t via interpolation of the owning chunk.

source
NSDETimeParallel.PararealParameters — Type
PararealParameters <: AbstractPararealParameters

A composite type for the basic parameters of Parareal.

Constructors

PararealParameters(N, K)
PararealParameters(; N=10, K=N)

Arguments

  • N :: Integer : number of time chunks/processors.
  • K :: Integer : maximum number of fine batches. A bulk-synchronous return uses at most K-1 correction sweeps because its fine trajectories are evaluated from the returned starts.
source
NSDETimeParallel.PararealSolution — Type
PararealSolution <: AbstractTimeParallelSolution

A composite type for an AbstractTimeParallelSolution obtained using Parareal.

Constructors

PararealSolution(lastiterate, errors)
PararealSolution(problem::AbstractInitialValueProblem, parareal::Parareal)

Arguments

  • lastiterate :: PararealIterate
  • errors :: AbstractVector{ℝ} where ℝ<:Real : iteration errors.

Functions

Methods

(solution::PararealSolution)(t::Real)

returns the value of solution at t via interpolation.

source
NSDETimeParallel.PipelinedMPIBackend — Type
PipelinedMPIBackend()

MPI frontier scheduling with one rank per chunk. Supported criteria are WeightedDefect(p=Inf, weight=FixedWeight(...)), FrontierRelativeUpdate, and NoStop. Other criteria are rejected; the bulk all-interface update rule and changing secant weights require synchronized iterates.

Maximum-defect acceptance waits for the upstream start to freeze before accepting its outgoing defect. Thus the assembled accepted starts satisfy the selected defect tolerance. Converting that tolerance to a trajectory error additionally requires bounds on the fine-map amplification.

Call MPI.Init() first and launch at least N ranks. Global histories are unsupported. Diagnostics report actual total fine/coarse calls and per-chunk local_sweeps; sweeps and fine_batches are nothing because this schedule has no synchronized batches. residuals contains the maximum diagnostic among ranks active at each local iteration index, not a bulk residual.

The final gather returns each fine trajectory from its accepted start. With NoStop() and K >= N, completion reproduces reference(problem, solver).

source
Base.firstindex — Method
firstindex(solution::PararealSolution)

returns the first index of solution.

source
Base.getindex — Method
getindex(iterate::PararealIterate, n::Integer)

returns the n-th chunk of iterate.

source
Base.lastindex — Method
lastindex(iterate::PararealIterate)

returns the last index of iterate.

source
Base.lastindex — Method
lastindex(solution::PararealSolution)

returns the last index of solution.

source
Base.length — Method
length(iterate::PararealIterate)

returns the number of chunks of iterate.

source
Base.length — Method
length(solution::PararealSolution)

returns the number of chunks of solution.

source
Base.setindex! — Method
setindex!(iterate::PararealIterate, value::AbstractInitialValueSolution, n::Integer)

stores value into the n-th chunk of iterate.

source
Base.setindex! — Method
setindex!(solution::PararealSolution, chunk::AbstractInitialValueSolution, n::Integer)

stores an AbstractInitialValueSolution as the n-th chunk of the last iteration of a PararealSolution.

source
NSDEBase.initialize_cache — Method
NSDEBase.initialize_cache(problem, parareal::Parareal) :: PararealCache

returns a reusable PararealCache for problem, honouring the same contract NSDERungeKutta honours for its solvers. Build once, then call parareal(cache, solution, problem; ...) repeatedly — e.g. in timing loops, to report setup costs separately from repeated solve costs. Reuse is not a zero-allocation guarantee.

source
NSDEBase.solve! — Method
solve!(solution::AbstractTimeParallelSolution, problem, solver::AbstractTimeParallelSolver; kwargs...) :: AbstractTimeParallelSolution

computes the solution of problem using solver.

source
NSDEBase.solve — Method
solve(problem, solver::AbstractTimeParallelSolver; kwargs...) :: AbstractTimeParallelSolution

computes the solution of problem using solver.

source
NSDETimeParallel.Wnorm — Method
Wnorm(iterate, reference, weight; base=:time)

Weighted Euclidean distance at the chunk interfaces. reference may be a solution or the vector returned by reference(problem, parareal). The base is per unit time, matching all weight rules. base=:chunk explicitly selects the per-chunk convention. Supply a scalar or FixedWeight; adaptive rule objects contain only their initial settings and are refused. To use a measured weight, pass solution.diagnostics.weights[end], or call Wnorm(solution, reference).

source
NSDETimeParallel.Wnorm — Method
Wnorm(solution::PararealSolution, reference)

Weighted interface error using the last measured per-unit-time weight. A run with no finite fine batch has no measured weight and raises ArgumentError.

source
NSDETimeParallel.boundarytimes — Method
boundarytimes(iterate::PararealIterate)

returns the N + 1 chunk-boundary times of iterate: each chunk's start, plus the final chunk's end. These are the points Parareal iterates on — the grid every convergence measure (Wnorm, per-iterate error studies) is taken over.

source
NSDETimeParallel.boundaryvalues — Method
boundaryvalues(iterate::PararealIterate)

returns the N + 1 chunk-boundary values of iterate: each chunk's first state, plus the final chunk's last state — the U vector of the Parareal iteration, read off the chunk solutions. The returned states ALIAS the chunk storage; copy them before mutating.

source
NSDETimeParallel.chunk_endpoint — Method
chunk_endpoint(sol, t)

returns the value of sol at the chunk endpoint t: the stored final value when the solver stopped exactly at t, otherwise the interpolant (for generic solvers that overshoot).

source
NSDETimeParallel.coarseinit! — Method
coarseinit!(cache, parareal; mpi=false)

runs the serial coarse pass over the chunk grid, filling G and — where makeGs allows — seeding the chunk starting values U. Chunks whose makeGs[n] is false keep whatever U[n] already holds (injected guesses, e.g. from a moving-window driver).

source
NSDETimeParallel.collect_iterates! — Method
collect_iterates!(solution::PararealSolution; directory)

reads the per-iterate chunk files written by the MPI backend under saveiterates back into solution.iterates. Chunks below the diagonal (n < k) are final from earlier iterations and are copied forward.

source
NSDETimeParallel.contractionrate — Method
contractionrate(errors::AbstractVector{<:Real}) :: Float64
contractionrate(solution::AbstractTimeParallelSolution) :: Float64

the observed per-iteration contraction rate β of an error trace: the least-squares slope of log(errors[k]) against k, exponentiated, so that errors[k] ≈ C βᵏ. Non-finite entries and EXACT ZEROS are excluded before fitting — finite termination drives the last error to bitwise zero, which is a property of the algorithm's fixed point, not of its rate. Returns NaN when fewer than two usable points remain: a run that converges in one iteration exhibits no observable rate, and downstream bounds (e.g. the outer radius R = r/βᴷ) must treat that as unbounded rather than fabricate a number.

source
NSDETimeParallel.correct! — Method
correct!(cache, parareal, k; mpi=false)

the serial Parareal correction sweep after fine batch k: re-run the coarse solver from the freshest starts and update U[n+1] = F[n+1] + (Gnew − Gold), writing in place against the U_ snapshot maintained by the iteration loop.

Two details make finite termination exact in floating point, not just in real arithmetic:

  • Order of operations. F + (Gnew − Gold) returns F bitwise whenever the two coarse values cancel bitwise; (Gnew + F) − Gold does not — it rounds F into Gnew first and loses low bits (with Gnew = Gold = 1e16 and F = 1 it returns 0). Do not "simplify" this expression.
  • The newly exact interface is assigned, not computed. After batch k the start U[k] is already exact, so F[k+1] is the exact value of U[k+1] and U[k] has not moved since G[k+1] was last computed: the coarse re-run at n = k would reproduce G[k+1] bitwise. Skip it and copy F[k+1] across — one coarse solve saved per sweep, and no arithmetic on the exact prefix at all.
source
NSDETimeParallel.costratio — Method
costratio(problem, parareal; repeats=3) :: Real

measures ζ, the coarse/fine cost ratio over ONE chunk of problem under parareal's chunking — the input theoretical_speedup needs. Both solvers are timed on the first chunk with @elapsed (best of repeats, after a compile warm-up). Measure ζ rather than guessing it from step-size ratios: those ignore per-step cost differences between the two solvers.

source
NSDETimeParallel.flatten — Method
flatten(iterate::PararealIterate)

concatenates the chunk solutions into one pair of vectors (u, t), dropping each chunk's FIRST point after chunk 1: boundaries are stored twice (chunk n's last point and chunk n+1's corrected start), and keeping both would double-weight every boundary in any downstream statistic. The kept value is the fine endpoint; the dropped corrected start agrees with it only to the solve tolerance. t is monotone.

source
NSDETimeParallel.iteration_budget — Method
iteration_budget(S::Real, N::Integer, ζ::Real; estimate=:wallclock) :: Int

returns the largest fine-batch budget K ∈ 1:N that reaches the target modelled speed-up S; returns zero if none does. A returned K permits K-1 corrections. This is a cost budget, not a prediction that the iteration converges within that budget.

Integer bisection inverts theoretical_speedup with the same cost model and exact-prefix coarse count. Both :wallclock (default) and :work are supported; :naive and :improved are aliases for them, respectively.

source
NSDETimeParallel.maxseam — Method
maxseam(iterate::PararealIterate) :: Real

returns the largest chunk-boundary seam of iterate (see seams), or 0.0 for a single-chunk iterate.

source
NSDETimeParallel.reference — Method
reference(problem, parareal::Parareal) :: Vector

returns the serial fine solution at the N + 1 chunk interfaces, computed on exactly the same absolute-time chunk grid and schedule as parareal.

source
NSDETimeParallel.seams — Method
seams(iterate::PararealIterate) :: Vector{<:Real}

returns the N − 1 chunk-boundary seam sizes of iterate: at each interior boundary, ‖(start of chunk n+1) − (end of chunk n)‖ — the distance between the corrected starting value U[n+1] and the fine endpoint F(U[n]) it should agree with at convergence. This is exactly the discontinuity that flatten hides by dropping the corrected start, so any statistical claim made on flattened output should report maxseam alongside it: under the weighted criterion ψ₂, late-window seams are unconstrained BY DESIGN (the discount forgives them), and only the seam sizes say which regime a run actually exercised. Empty for a single-chunk iterate.

source
NSDETimeParallel.shiftwindow! — Method
shiftwindow!(cache::PararealCache, τ0, τN)

re-targets a cache at the window [τ0, τN]: recomputes the chunk grid T and updates every chunk problem's tspan in place. This is the official seam for window drivers (e.g. NSDEMovingWindow) — chunk problems are built once at cache construction, so their time spans must be moved through this function, never by poking cache.T alone.

source
NSDETimeParallel.theoretical_speedup — Method
theoretical_speedup(K::Integer, N::Integer, ζ::Real; estimate=:wallclock) :: Real
theoretical_speedup(diagnostics::PararealDiagnostics, ζ::Real; estimate=:wallclock) :: Real
theoretical_speedup(solution::PararealSolution, ζ::Real; estimate=:wallclock) :: Real

returns the modelled speed-up over N serial fine chunks. K is the number of fine batches, not corrections: a finite bulk return has s = K-1, with 1 ≤ K ≤ N. The cost ratio ζ is coarse/fine time per chunk; see costratio.

The exact-prefix implementation uses K_G = K*(N-1) - K*(K-1)/2 coarse calls and K*N - K*(K-1)/2 fine calls, including the initial coarse pass and final fine batch. The diagnostics overload uses the recorded call counts directly.

  • :wallclock (default) charges one fine-chunk time per batch and sequential coarse calls: N/(K + ζ*K_G). It assumes at least N workers and equal chunk costs, and excludes communication, synchronization, and load imbalance.
  • :work divides the total fine-call cost by N, retaining the same sequential coarse cost. This assumes perfect redistribution of the shrinking fine work; it is not the critical path of the bulk backend.

These are cost models, not measured speed-ups or unconditional performance bounds. The compatibility keyword wallclock=false selects :work, while an explicit estimate takes precedence. Pipeline diagnostics, nonfinite or unstarted runs, and runs with no fine batch are refused: a pipeline needs its task schedule.

source