TS

The TS (Time Stepping) module integrates ordinary differential equations and differential algebraic equations in time, and is the layer PETSc builds its implicit, explicit and IMEX methods on.

The interface described here is the high-level one. It owns the @cfunction trampolines, keeps your callbacks rooted for as long as the stepper lives, and frees the object for you on a sequential communicator. The low-level TS bindings remain available and unchanged.

Overview

A problem is given to TS in one of two forms, or in both at once:

Setting both is how an IMEX method splits a problem into the stiff part it treats implicitly and the rest it treats explicitly.

Creating a stepper

ts = PETSc.TS(petsclib, MPI.COMM_WORLD)

# or with options, which are applied when you call solve!
ts = PETSc.TS(petsclib, MPI.COMM_WORLD;
    ts_type = "bdf",
    ts_adapt_type = "basic",
)

Options given here are held and applied inside PETSc.solve! rather than at construction, so a DM and the callbacks attached afterwards are in place before PETSc reads them. Until then the object has no type, and PETSc.type answers nothing.

On a communicator of size 1 the garbage collector calls PETSc.destroy!. On a larger one, destruction is yours to do, since collection is asynchronous and the call is collective.

An explicit problem

Solving $du/dt = -u$ from $u(0) = 1$:

ts = PETSc.TS(petsclib, MPI.COMM_SELF)
PETSc.set_type!(ts, :rk)

u = PETSc.VecSeq(petsclib, 1)
u[1] = 1.0
PETSc.assemble!(u)

PETSc.set_rhs_function!(ts) do F, ts, t, u
    PETSc.withlocalarray!((u, F); read = (true, false), write = (false, true)) do ua, Fa
        Fa[1] = -ua[1]
    end
    return 0
end

PETSc.set_time!(ts, 0.0)
PETSc.set_timestep!(ts, 0.01)
PETSc.set_max_time!(ts, 1.0)

PETSc.solve!(u, ts)

The callback comes first in the argument list, so do block syntax works. Pass it in the other order, set_rhs_function!(ts, f!), when it is already a value.

An implicit problem

The same equation written as $F(t, u, u_t) = u_t + u = 0$, stepped with backward Euler. The Jacobian asked for is $\partial F/\partial u + \sigma\, \partial F/\partial u_t$, where PETSc supplies the shift $\sigma$:

ts = PETSc.TS(petsclib, MPI.COMM_SELF)
PETSc.set_type!(ts, :beuler)

J = PETSc.MatSeqAIJ(petsclib, 1, 1, petsclib.PetscInt(1))

PETSc.set_ifunction!(ts) do F, ts, t, u, u_t
    PETSc.withlocalarray!(
        (u, u_t, F);
        read = (true, true, false),
        write = (false, false, true),
    ) do ua, uta, Fa
        Fa[1] = uta[1] + ua[1]
    end
    return 0
end

PETSc.set_ijacobian!(ts, J) do A, P, ts, t, u, u_t, shift
    A[1, 1] = shift + 1
    PETSc.assemble!(A)
    return 0
end

A Jacobian callback is always handed both the Jacobian A and the preconditioning matrix P. They are the same object unless you passed two, which is worth testing with A.ptr == P.ptr before filling P a second time.

Callback signatures

SetterCallback
PETSc.set_rhs_function!f!(F, ts, t, u)
PETSc.set_rhs_jacobian!updateJ!(A, P, ts, t, u)
PETSc.set_ifunction!f!(F, ts, t, u, u_t)
PETSc.set_ijacobian!updateJ!(A, P, ts, t, u, u_t, shift)
PETSc.set_monitor!f(ts, step, t, u)

Each may return a PETSc error code; any other return value counts as success.

A callback that raises a Julia exception is reported and turned into a PETSc failure rather than being allowed to escape into C, where it would take the process down with it. The error is logged with its backtrace and PETSc.solve! then raises a PetscError.

Precompilation

Each setter builds its @cfunction trampoline when you call it, so nothing here is affected by precompilation, and there is nothing you need to do about it.

It is worth knowing where the hazard is, though, because the low-level examples show the other pattern. A @cfunction yields a pointer valid only for the session that evaluated it, so this at the top level of a package:

const MY_RHS_PTR = @cfunction(my_rhs!, PetscErrorCode, (CTS, PetscReal, CVec, CVec, Ptr{Cvoid}))

captures a pointer during precompilation and hands PETSc a stale one in every later session. In a script such as examples/ex16.jl it is fine, since the file is evaluated afresh each run. Inside a package it is not: build the pointer inside the function that registers it, or assign it from __init__.

Passing your own data

Anything stored with PETSc.set_user_ctx! is handed back as a trailing argument, to whichever callbacks have a method that accepts one:

PETSc.set_user_ctx!(ts, (; viscosity = 1e-3))

PETSc.set_rhs_function!(ts) do F, ts, t, u, ctx
    # ctx.viscosity is available here
    return 0
end

The object is held on the Julia side by ts, so it stays alive without any pinning of your own.

Watching a solve

PETSc.set_monitor!(ts) do ts, step, t, u
    @printf("step %3d  t = %.4f\n", step, t)
    return 0
end

The monitor runs once before the first step and once after each accepted one. It does not replace the monitors PETSc installs from the options database, such as -ts_monitor.

Solving, and reading the result

PETSc.solve!(u, ts)          # integrate, starting from u
PETSc.solve!(ts)             # or from the vector already set on ts

PETSc.converged_reason(ts)   # why it stopped
PETSc.solve_time(ts)         # the time actually reached
PETSc.step_number(ts)        # steps taken
PETSc.snes_iterations(ts)    # nonlinear iterations, summed over the steps

The final step lands exactly on PETSc.set_max_time! by default. This differs from PETSc, whose own default, TS_EXACTFINALTIME_UNSPECIFIED, integrates past the requested time without saying so; pass exact_final_time to the constructor or call PETSc.set_exact_final_time! to choose otherwise.

For finer control, PETSc.step! takes a single step and PETSc.interpolate! samples the solution inside the step just taken.

Cleaning up

PETSc.destroy!(ts)

Safe to call more than once, and a no-op on a handle left over from a previous initialize/finalize cycle. PETSc.destroy also works, for consistency with the rest of the package.

Functions

PETSc.LibPETSc.TSMethod
TS(petsclib, comm::MPI.Comm; prefix = "", options...)

Create a PETSc time stepper on the communicator comm.

Options are stored and applied in solve! rather than here, so that a DM and callbacks attached after construction are visible to TSSetFromOptions.

exact_final_time defaults to TS_EXACTFINALTIME_MATCHSTEP, so the last step lands on the time set by set_max_time!. PETSc's own default is TS_EXACTFINALTIME_UNSPECIFIED, which integrates to the wrong time without reporting an error.

If comm has size 1 the garbage collector calls destroy!. Otherwise destruction is the caller's responsibility.

External Links

source
PETSc.LibPETSc.TSARKIMEXRegisterMethod
TSARKIMEXRegister(
    petsclib,
    name::String,
    order,
    s,
    At,
    bt,
    ct,
    A,
    b,
    c,
    bembedt,
    bembed,
    pinterp,
    binterpt,
    binterp,
)

Julia-friendly overload for registering a custom TSARKIMEX tableau. Optional PETSc arrays may be passed as nothing, which is translated to NULL.

For the stage tables At and A, PETSc expects flat vectors in row-major order, matching the layout used by C arrays. If you start from a Julia matrix, do not pass vec(A) directly since Julia stores matrices column-major; flatten row-by-row instead, for example with vec(permutedims(A)).

source
PETSc.LibPETSc.TSAdaptSetTypeMethod
TSAdaptSetType(petsclib, adapt, type::String)

Convenience wrapper for setting the TS adaptivity controller using a Julia string such as "none" or "basic".

source
PETSc.LibPETSc.TSGetKSPMethod
TSGetKSP(petsclib, ts)

Return the linear solver held by ts.

The generated three-argument form takes the solver as an input and nulls the caller's handle, so it cannot be used to read the solver back. The KSP is owned by ts and must not be destroyed.

External Links

source
PETSc.LibPETSc.TSGetSNESMethod
TSGetSNES(petsclib, ts)

Return the nonlinear solver held by ts.

The generated three-argument form takes the solver as an input and nulls the caller's handle, so it cannot be used to read the solver back. The SNES is owned by ts and must not be destroyed.

External Links

source
PETSc.LibPETSc.TSGetSolutionMethod
TSGetSolution(petsclib, ts)

Return the solution vector held by ts.

The generated three-argument form takes the vector as an input and nulls the caller's handle, so it cannot be used to read the solution back. The vector is owned by ts and must not be destroyed.

External Links

source
PETSc.LibPETSc.TSMonitorSetFunction
TSMonitorSet(petsclib, ts, monitor::Ptr{Cvoid}, ctx = C_NULL, mdestroy = C_NULL)

Convenience overload for low-level TS monitor callbacks created with @cfunction.

source
PETSc.LibPETSc.TSSetIFunctionFunction
TSSetIFunction(petsclib, ts, r, fptr::Ptr{Cvoid}, ctx = C_NULL)

Convenience overload for low-level TS implicit-function callbacks created with @cfunction.

source
PETSc.LibPETSc.TSSetIJacobianFunction
TSSetIJacobian(petsclib, ts, A, P, fptr::Ptr{Cvoid}, ctx = C_NULL)

Convenience overload for low-level TS implicit-Jacobian callbacks created with @cfunction.

source
PETSc.LibPETSc.TSSetRHSFunctionFunction
TSSetRHSFunction(petsclib, ts, r, fptr::Ptr{Cvoid}, ctx = C_NULL)

Convenience overload for low-level TS RHS callbacks created with @cfunction.

The generated bindings currently accept the PETSc function-wrapper type directly, while Julia's @cfunction returns a raw pointer. This overload bridges that gap so callback-based TS examples can use the low-level interface naturally.

source
PETSc.LibPETSc.TSSolveMethod
TSSolve(petsclib, ts, ::Nothing)

Integrate ts using the solution vector already set on it.

The generated binding requires a vector. PETSc reads the solution set by TSSetSolution when it is handed NULL instead, which is what this overload passes.

External Links

source
PETSc.destroy!Method
destroy!(ts::AbstractTS)

Destroy ts and release the options database attached to it.

The call is a no-op when the library has been finalized or when ts predates the current initialize/finalize cycle, so a stale handle never reaches TSDestroy.

External Links

source
PETSc.destroyMethod
destroy(ts::AbstractTS)

Destroy ts. Provided so that the spelling used by the rest of the package keeps working; destroy! is the name to prefer, since the call mutates ts.

External Links

source
PETSc.dmMethod
dm(ts::AbstractTS)

The DM attached to ts. The DM is owned by ts.

External Links

source
PETSc.interpolate!Method
interpolate!(u::AbstractPetscVec, ts::AbstractTS, t)

Fill u with the solution interpolated to time t, and return it.

Only the methods that keep a dense output can do this, and t must lie inside the step just taken.

External Links

source
PETSc.kspMethod
ksp(ts::AbstractTS)

The linear solver ts steps with. It is owned by ts, so do not destroy it.

PETSc only offers this for a problem declared TS_LINEAR with set_problem_type!, and raises PETSC_ERR_ARG_WRONG otherwise. The linear solver of a nonlinear problem belongs to its SNES, so reach it through snes.

External Links

source
PETSc.reset!Method
reset!(ts::AbstractTS)

Release the work vectors and matrices ts allocated, keeping the callbacks and the options.

The clock is not part of that state: the time and the step count survive, so set them with set_time! and set_max_time! before integrating again.

External Links

source
PETSc.set_adapt_type!Method
set_adapt_type!(ts::AbstractTS, type::Symbol)

Set the timestep adaptivity controller, for example :none to hold the step size fixed, or :basic for the default error-based controller.

External Links

source
PETSc.set_ifunction!Function
set_ifunction!(f!, ts::AbstractTS, r = nothing)
set_ifunction!(ts::AbstractTS, f!, r = nothing)

Set the residual $F$ of an implicit problem $F(t, u, du/dt) = 0$.

f! is called as f!(F, ts, t, u, u_t), filling the vector F. If ts.user_ctx is set, f!(F, ts, t, u, u_t, user_ctx) is used instead when that method exists.

External Links

source
PETSc.set_ijacobian!Function
set_ijacobian!(updateJ!, ts::AbstractTS, A, P = A)
set_ijacobian!(ts::AbstractTS, updateJ!, A, P = A)

Set the Jacobian of the implicit residual $F$.

updateJ! is called as updateJ!(A, P, ts, t, u, u_t, shift) and should fill A with $dF/du + shift * dF/du_t$. If ts.user_ctx is set, the method taking a trailing user_ctx is used instead when it exists.

External Links

source
PETSc.set_monitor!Method
set_monitor!(f, ts::AbstractTS)
set_monitor!(ts::AbstractTS, f)

Call f once after every accepted step.

f is called as f(ts, step, t, u), where step counts the steps taken, t is the time reached and u holds the solution there. If ts.user_ctx is set, f(ts, step, t, u, user_ctx) is used instead when that method exists. u is owned by ts and must not be destroyed.

Only one monitor can be set this way; a second call replaces the first. The monitors PETSc installs from the options database, such as -ts_monitor, are unaffected.

External Links

source
PETSc.set_rhs_function!Function
set_rhs_function!(f!, ts::AbstractTS, r = nothing)
set_rhs_function!(ts::AbstractTS, f!, r = nothing)

Set the right-hand side $G$ of an explicit problem $du/dt = G(t, u)$.

f! is called as f!(F, ts, t, u), filling the vector F. If ts.user_ctx is set, f!(F, ts, t, u, user_ctx) is used instead when that method exists.

r is an optional template vector for the residual.

External Links

source
PETSc.set_rhs_jacobian!Function
set_rhs_jacobian!(updateJ!, ts::AbstractTS, A, P = A)
set_rhs_jacobian!(ts::AbstractTS, updateJ!, A, P = A)

Set the Jacobian of the right-hand side $G$.

updateJ! is called as updateJ!(A, P, ts, t, u), filling the Jacobian A and the preconditioning matrix P. If ts.user_ctx is set, updateJ!(A, P, ts, t, u, user_ctx) is used instead when that method exists.

External Links

source
PETSc.set_tolerances!Method
set_tolerances!(ts::AbstractTS; atol, rtol, vatol, vrtol)

Set the local truncation error tolerances. A keyword left at nothing keeps the value ts currently has.

Pass vatol or vrtol to give per-component tolerances.

External Links

source
PETSc.set_type!Method
set_type!(ts::AbstractTS, type::Symbol)

Set the time-stepping method, for example :bdf, :rk or :arkimex.

External Links

source
PETSc.set_user_ctx!Method
set_user_ctx!(ts::AbstractTS, ctx)

Attach ctx to ts, to be handed back as the last argument of every callback that has a method accepting it.

The object is held by ts on the Julia side, so it is kept alive and needs no pinning.

source
PETSc.setup!Method
setup!(ts::AbstractTS)

Complete the setup of ts. solve! calls this, so it is only needed when the setup must happen at a controlled point.

External Links

source
PETSc.snesMethod
snes(ts::AbstractTS)

The nonlinear solver ts steps with. It is owned by ts, so do not destroy it.

Only the implicit methods build one. Asking an explicit method for its SNES creates an unused solver rather than reporting an error.

External Links

source
PETSc.solve!Method
solve!(u::AbstractPetscVec, ts::AbstractTS)
solve!(ts::AbstractTS)

Integrate ts, starting from u and returning it. The second form uses the solution vector already set on ts and returns ts.

u is registered with TSSetSolution before the solve. Passing it to TSSolve alone leaves the stepper reading an uninitialized solution vector, which the implicit methods see as an initial condition of zero.

Options passed to the TS constructor are applied here, once the DM and the callbacks are attached.

External Links

source
PETSc.tolerancesMethod
tolerances(ts::AbstractTS)

Local truncation error tolerances, as (; atol, rtol, vatol, vrtol).

vatol and vrtol hold per-component tolerances and carry a null pointer when only the scalar tolerances are set. Both are owned by ts.

External Links

source
PETSc.typeFunction
type(ts::AbstractTS)

The time-stepping method currently set on ts, as a Symbol, or nothing when none has been set yet.

PETSc reports an unset type as a null string, which the generated TSGetType cannot convert. Asking a freshly created stepper for its type is reasonable, so it is answered with nothing here.

External Links

source