EnsembleGPUKernel
API
DiffEqGPU.EnsembleGPUKernel — Type
EnsembleGPUKernel(backend, cpu_offload = 0.0)A massively parallel ensemble algorithm that generates one GPU kernel for the complete fixed-step ODE or SDE solve. It minimizes kernel-launch overhead, at the cost of stricter requirements on the problem and solver.
Fields
dev: theKernelAbstractionsbackend used to launch the fused kernel.cpu_offload: the fraction of trajectories solved on the CPU. The default is0.0.
Arguments
backend: aKernelAbstractionsbackend, such asCUDA.CUDABackend()orKernelAbstractions.CPU().cpu_offload: the fraction of trajectories to offload to CPU execution. The two-argument constructor stores this value as aFloat64; the one-argument constructor defaults to0.0.
Returns
An EnsembleGPUKernel algorithm selector.
Throws
The constructor itself does not validate kernel compatibility. Unsupported state containers, right-hand sides, callbacks, or solver combinations throw when the ensemble is solved.
Limitations
Not all standard Julia
ffunctions are allowed. Only Juliaffunctions which are capable of being compiled into a GPU kernel are allowed. This notably means that certain features of Julia can cause issues inside a kernel, like:- Allocating memory (building arrays)
- Linear algebra (anything that calls BLAS)
- Broadcast
Only out-of-place
fdefinitions are allowed. Coupled with the requirement of not allowing for memory allocations, this means that the ODE must be defined withStaticArrayinitial conditions.Only specific ODE solvers are allowed. This includes:
- GPUTsit5
- GPUVern7
- GPUVern9
To use multiple GPUs over clusters, one must manually set up one process per GPU. See the multi-GPU tutorial for more details.
Examples
using DiffEqGPU, CUDA, OrdinaryDiffEq, StaticArrays
function lorenz(u, p, t)
σ = p[1]
ρ = p[2]
β = p[3]
du1 = σ * (u[2] - u[1])
du2 = u[1] * (ρ - u[3]) - u[2]
du3 = u[1] * u[2] - β * u[3]
return SVector{3}(du1, du2, du3)
end
u0 = @SVector [1.0f0; 0.0f0; 0.0f0]
tspan = (0.0f0, 10.0f0)
p = @SVector [10.0f0, 28.0f0, 8 / 3.0f0]
prob = ODEProblem{false}(lorenz, u0, tspan, p)
prob_func = (prob, ctx) -> remake(prob, p = (@SVector rand(Float32, 3)) .* p)
monteprob = EnsembleProblem(prob; prob_func, safetycopy = false)
@time sol = solve(
monteprob, GPUTsit5(), EnsembleGPUKernel(CUDA.CUDABackend()), trajectories = 10_000,
adaptive = false, dt = 0.1f0
)Specialized Solvers
DiffEqGPU.GPUTsit5 — Type
GPUTsit5()Fifth-order Tsitouras Runge-Kutta method specialized for EnsembleGPUKernel ODE solves.
Use GPUTsit5 as the ODE algorithm when solving an EnsembleProblem with EnsembleGPUKernel:
solve(
ensemble_prob, GPUTsit5(), EnsembleGPUKernel(backend);
trajectories = 10_000, adaptive = false, dt = 0.1f0
)GPUTsit5 supports the EnsembleGPUKernel restrictions, including out-of-place ODE functions over GPU-compatible static state containers. For a similar CPU implementation, see SimpleATsit5 from SimpleDiffEq.jl.
Returns
A GPUTsit5 algorithm selector.
Throws
The constructor has no runtime checks. solve throws when the problem is not compatible with the EnsembleGPUKernel restrictions.
DiffEqGPU.GPUTsit5IController — Type
GPUTsit5IController()Fifth-order Tsitouras method with a current-error-only integral (I) step-size controller for EnsembleGPUKernel. The Runge-Kutta tableau, error estimate, and interpolation are the same as GPUTsit5; fixed-step solves are identical.
For adaptive solves, the next step is multiplied by clamp(0.9 * E^(-1/5), 0.2, 5), where E is the scaled error norm. Zero error uses the maximum growth factor. The same rule applies after rejected steps. Use EnsembleGPUKernel(backend, 0.0); CPU offloading is not supported.
Generally prefer the PI-controlled GPUTsit5, which has more stable step-size control. On simple nonstiff benchmarks such as Lorenz, an I controller can appear faster at identical tolerances partly because it achieves lower accuracy. Compare performance at matched achieved error.
DiffEqGPU.GPUVern7 — Type
GPUVern7()Seventh-order Verner Runge-Kutta method specialized for EnsembleGPUKernel ODE solves.
Use GPUVern7 for non-stiff ODE ensemble solves that satisfy the EnsembleGPUKernel kernel-generation restrictions:
solve(
ensemble_prob, GPUVern7(), EnsembleGPUKernel(backend);
trajectories = 10_000, adaptive = false, dt = 0.1f0
)Returns
A GPUVern7 algorithm selector.
Throws
solve throws when the problem is not compatible with the EnsembleGPUKernel restrictions.
DiffEqGPU.GPUVern9 — Type
GPUVern9()Ninth-order Verner Runge-Kutta method specialized for EnsembleGPUKernel ODE solves.
Use GPUVern9 for high-accuracy non-stiff ODE ensemble solves that satisfy the EnsembleGPUKernel kernel-generation restrictions:
solve(
ensemble_prob, GPUVern9(), EnsembleGPUKernel(backend);
trajectories = 10_000, adaptive = false, dt = 0.1f0
)Returns
A GPUVern9 algorithm selector.
Throws
solve throws when the problem is not compatible with the EnsembleGPUKernel restrictions.
DiffEqGPU.GPUEM — Type
GPUEM()Euler-Maruyama method with weak order 1.0 specialized for EnsembleGPUKernel SDE solves.
solve(
ensemble_prob, GPUEM(), EnsembleGPUKernel(backend);
trajectories = 10_000, adaptive = false, dt = 0.1f0
)Returns
A GPUEM algorithm selector.
Throws
solve throws when the SDE noise structure is not supported by the kernel implementation.
DiffEqGPU.GPUSIEA — Type
GPUSIEA()Weak order 2.0 SIEA method for Ito SDEs specialized for EnsembleGPUKernel SDE solves.
solve(
ensemble_prob, GPUSIEA(), EnsembleGPUKernel(backend);
trajectories = 10_000, adaptive = false, dt = 0.1f0
)Returns
A GPUSIEA algorithm selector.
Throws
solve throws when the SDE noise structure is not diagonal or otherwise unsupported.
DiffEqGPU.GPURosenbrock23 — Type
GPURosenbrock23(; autodiff = Val{true}())Second/third-order Rosenbrock-W method specialized for stiff EnsembleGPUKernel ODE solves.
Keyword Arguments
autodiff: whether automatic differentiation is used for derivative generation. This accepts either aBoolorVal{Bool}; passfalseorVal{false}()when providing the required derivatives manually.
solve(
ensemble_prob, GPURosenbrock23(), EnsembleGPUKernel(backend);
trajectories = 10_000
)Returns
A GPURosenbrock23 algorithm selector.
Throws
solve throws when the problem does not provide GPU-compatible derivative information.
DiffEqGPU.GPURodas4 — Type
GPURodas4(; autodiff = Val{true}())Fourth-order Rosenbrock method specialized for stiff EnsembleGPUKernel ODE solves.
Keyword Arguments
autodiff: whether automatic differentiation is used for derivative generation. This accepts either aBoolorVal{Bool}; passfalseorVal{false}()when providing the required derivatives manually.
solve(ensemble_prob, GPURodas4(), EnsembleGPUKernel(backend); trajectories = 10_000)Returns
A GPURodas4 algorithm selector.
Throws
solve throws when the problem does not provide GPU-compatible derivative information.
DiffEqGPU.GPURodas5P — Type
GPURodas5P(; autodiff = Val{true}())Fifth-order Rosenbrock method specialized for stiff EnsembleGPUKernel ODE solves.
Keyword Arguments
autodiff: whether automatic differentiation is used for derivative generation. This accepts either aBoolorVal{Bool}; passfalseorVal{false}()when providing the required derivatives manually.
solve(ensemble_prob, GPURodas5P(), EnsembleGPUKernel(backend); trajectories = 10_000)Returns
A GPURodas5P algorithm selector.
Throws
solve throws when the problem does not provide GPU-compatible derivative information.
DiffEqGPU.GPUKvaerno3 — Type
GPUKvaerno3(; autodiff = Val{true}())Third-order ESDIRK method specialized for stiff EnsembleGPUKernel ODE solves.
Keyword Arguments
autodiff: whether automatic differentiation is used for derivative generation. This accepts either aBoolorVal{Bool}; passfalseorVal{false}()when providing the required derivatives manually.
solve(ensemble_prob, GPUKvaerno3(), EnsembleGPUKernel(backend); trajectories = 10_000)Returns
A GPUKvaerno3 algorithm selector.
Throws
solve throws when the problem does not provide GPU-compatible derivative information.
DiffEqGPU.GPUKvaerno5 — Type
GPUKvaerno5(; autodiff = Val{true}())Fifth-order ESDIRK method specialized for stiff EnsembleGPUKernel ODE solves.
Keyword Arguments
autodiff: whether automatic differentiation is used for derivative generation. This accepts either aBoolorVal{Bool}; passfalseorVal{false}()when providing the required derivatives manually.
solve(ensemble_prob, GPUKvaerno5(), EnsembleGPUKernel(backend); trajectories = 10_000)Returns
A GPUKvaerno5 algorithm selector.
Throws
solve throws when the problem does not provide GPU-compatible derivative information.
Adaptive controllers and benchmark comparisons
The adaptive GPUTsit5 implementation uses a PI controller. Keep this controller for general use. A pure I controller responds only to the current error estimate and generally provides less stable step-size control, so it is not recommended as a general replacement for the PI controller.
On simple nonstiff benchmarks such as Lorenz, an I-controlled Tsit5 implementation can appear faster at the same abstol and reltol, partly because it delivers lower achieved accuracy. Equal tolerances do not imply equal accuracy. Compare execution time at matched achieved error, and account for rejected steps, before concluding that one controller is more efficient.
Changing the controller preserves the Tsit5 Runge–Kutta tableau but changes step selection and tolerance behavior. Any I-controlled variant should remain an explicit alternative, preserving the existing GPUTsit5 behavior. GPUTsit5IController() provides this opt-in I-controller variant.