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_name 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.PetscVec(petsclib, 1)
u[1] = 1.0
PETSc.assemble!(u)

PETSc.set_rhs_function!(ts) do F, ts, t, u
    PETSc.with_local_array!((u, F); read = (true, false), write = (false, true)) do ua, Fa
        Fa[1] = -ua[1]
    end
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 as a plain argument, set_rhs_function!(f!, ts), when it is already a value: the subject-first order v0.4 also accepted is gone, and has no shim.

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.PetscMat(petsclib, 1, 1, petsclib.PetscInt(1))

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

PETSc.set_ijacobian!(ts, J) do A, P, ts, t, u, u_t, shift
    A[1, 1] = shift + 1
    PETSc.assemble!(A)
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)
PETSc.set_pre_step!f!(ts)
PETSc.set_post_step!f!(ts)

A callback's return value is ignored; it reports a failure by throwing. Until v0.6, a nonzero Integer returned by one of the first five still fails the call, with a warning.

An exception thrown in a callback never crosses into PETSc's C code. PETSc is told the callback failed, unwinds, and PETSc.solve! or PETSc.step! then rethrows the original exception.

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
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)
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.

Long runs

A monitor only observes. To change the run between steps, for example to update a history variable once a step is accepted, use a post-step hook; a pre-step hook runs before each step is attempted:

PETSc.set_post_step!(ts) do ts
    u = PETSc.solution(ts)       # the accepted solution, which may be changed here
    # ...
end

Both run inside PETSc.solve!, not PETSc.step!.

What stops a run early, and how:

PETSc.set_max_snes_failures!(ts, 5)       # failed nonlinear solves over the run (default 1)
PETSc.set_max_step_rejections!(ts, 20)    # rejected attempts at one step (default 10)
PETSc.set_error_if_step_fails!(ts, false) # return instead of throwing; then read converged_reason

LibPETSc.PETSC_UNLIMITED removes either limit. A domain error reported with PETSc.set_function_domain_error! rejects the step rather than counting as a failed solve.

To continue from a checkpoint, set the clock and the step count before solving:

PETSc.set_time!(ts, t_checkpoint)
PETSc.set_step_number!(ts, step_checkpoint)

For a DAE, PETSc.set_equation_type! tells the integrator the form of the equations, for example LibPETSc.TS_EQ_DAE_IMPLICIT_INDEX1.

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.prev_time(ts)          # the time at the start of the last step
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.

Functions

PETSc.LibPETSc.TS — Method
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. They are applied on every solve!, and override a setting made in code for the same option. step! does not apply them.

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.TSMonitorSet — Method
TSMonitorSet(petsclib, ts, monitor::Ptr{Cvoid}, ctx = C_NULL, mdestroy = C_NULL)

Defaults for the context and destroy-callback arguments of the generated TSMonitorSet.

source
PETSc.LibPETSc.TSSolve — Method
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. Does nothing on a borrowed handle: see owns.

External Links

source
PETSc.dm — Method
dm(ts::AbstractTS)

The DM attached to ts, narrowed to its flavour.

Borrowed handle

The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).

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.ksp — Method
ksp(ts::AbstractTS)

The linear solver ts steps with.

Borrowed handle

The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).

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_equation_type! — Method
set_equation_type!(ts::AbstractTS, type)

Declare the form of the equations, for example LibPETSc.TS_EQ_DAE_IMPLICIT_INDEX1 for an index-1 DAE written as $F(t, u, du/dt) = 0$. Some integrators read it to treat the algebraic components correctly. Returns ts.

External Links

source
PETSc.set_ifunction! — Function
set_ifunction!(f!, ts::AbstractTS, 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.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).

source
PETSc.set_ijacobian! — Function
set_ijacobian!(updateJ!, ts::AbstractTS, 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.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).

source
PETSc.set_max_snes_failures! — Method
set_max_snes_failures!(ts::AbstractTS, n)

Allow n failed nonlinear solves over the whole run before solve! stops with TS_DIVERGED_NONLINEAR_SOLVE in converged_reason. A failed solve makes the adaptor retry the step with a smaller time step. The default is 1; LibPETSc.PETSC_UNLIMITED removes the limit. Returns ts.

A solve stopped by set_function_domain_error! does not count here: it rejects the step, which set_max_step_rejections! limits.

External Links

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

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.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).

source
PETSc.set_post_step! — Function
set_post_step!(f!, ts::AbstractTS)

Call f!(ts) after every step solve! accepts; f!(ts, user_ctx) is used instead when ts.user_ctx is set and that method exists. Unlike a monitor, f! may change the state of the run: the solution from solution, auxiliary fields, the time step. step! does not call it.

A second call replaces the first. Returns ts.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

source
PETSc.set_pre_step! — Function
set_pre_step!(f!, ts::AbstractTS)

Call f!(ts) at the start of every step solve! takes, before the step is attempted; f!(ts, user_ctx) is used instead when ts.user_ctx is set and that method exists. Unlike a monitor, f! may change the state of the run, for example the time step. step! does not call it.

A second call replaces the first. Returns ts.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

source
PETSc.set_rhs_function! — Function
set_rhs_function!(f!, ts::AbstractTS, 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.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).

source
PETSc.set_rhs_jacobian! — Function
set_rhs_jacobian!(updateJ!, ts::AbstractTS, 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.

Callback

The closure is kept with the PETSc object, not with the wrapper passed here, so a borrowed handle is fine to pass. It succeeds by returning and fails by throwing; its return value is ignored, except that a nonzero Integer still fails the call and warns until v0.6. An exception it throws comes out of the solve!, step! or setup! that ran it.

External Links

The callback comes first (docs/src/man/naming.md §8.1), so do block syntax works. v0.4 also accepted the subject-first order; that method is gone in v0.5, and there is no shim for it (§16).

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. It is kept with the PETSc object, so every handle onto it sees it too. Returns ts.

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.snes — Method
snes(ts::AbstractTS)

The nonlinear solver ts steps with.

Borrowed handle

The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).

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.solution — Method
solution(ts::AbstractTS)

The solution vector held by ts, as a PetscVec.

Borrowed handle

The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).

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.tolerances — Method
tolerances(ts::AbstractTS)

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

vatol and vrtol are the per-component tolerance vectors, as PetscVecs holding a null pointer when only the scalar tolerances were set.

Borrowed handle

The returned object is owned by the object it was asked of, not by the caller: it carries no finalizer and must not be destroyed. destroy! on it is a no-op (owns).

External Links

source
PETSc.type_name — Function
type_name(obj)

The name of the PETSc implementation obj currently uses.

The reader is type_name rather than type, because type is the conventional name for the argument of the matching setter and would be shadowed by it, and because what comes back is the name of a PETSc implementation rather than a Julia type (docs/src/man/naming.md §3).

Declared here, with methods on TS, KSP, PetscVec, PetscMat and AbstractPetscDM. On a TS the answer is a Symbol (or nothing before a type is set); the other methods still answer with a String.

External Links

source