Liquid argon benchmarks

Sebastian Micluța-Câmpeanu, Mikhail Vaganov

The purpose of these benchmarks is to compare several integrators for use in molecular dynamics simulation. We will use a simulation of liquid argon form the examples of NBodySimulator as test case.

using ProgressLogging
using NBodySimulator, OrdinaryDiffEq, StaticArrays
using Plots, DataFrames, StatsPlots

function setup(t)
    T = 120.0 # K
    kb = 1.38e-23 # J/K
    ϵ = T * kb # J
    σ = 3.4e-10 # m
    ρ = 1374 # kg/m^3
    m = 39.95 * 1.6747 * 1e-27 # kg
    N = 350
    L = (m*N/ρ)^(1/3)
    R = 3.5σ
    v_dev = sqrt(kb * T / m) # m/s

    _L = L / σ
     = 1.0
     = 1.0
    _m = 1.0
    _v = v_dev / sqrt(ϵ / m)
    _R = R / σ

    bodies = generate_bodies_in_cell_nodes(N, _m, _v, _L)
    lj_parameters = LennardJonesParameters(, , _R)
    pbc = CubicPeriodicBoundaryConditions(_L)
    lj_system = PotentialNBodySystem(bodies, Dict(:lennard_jones => lj_parameters));
    simulation = NBodySimulation(lj_system, (0.0, t), pbc, /T)

    return simulation
end
setup (generic function with 1 method)

In order to compare different integrating methods we will consider a fixed simulation time and change the timestep (or tolerances in the case of adaptive methods).

function benchmark(energyerr, rts, bytes, allocs, nt, nf, t, configs)
    simulation = setup(t)
    prob = SecondOrderODEProblem(simulation)
    for config in configs
        alg = config.alg
        sol, rt, b, gc, memalloc = @timed solve(prob, alg();
            save_everystep=false, progress=true, progress_name="$alg", config...)
        result = NBodySimulator.SimulationResult(sol, simulation)
        ΔE = total_energy(result, t) - total_energy(result, 0)
        energyerr[alg] = ΔE
        rts[alg] = rt
        bytes[alg] = b
        allocs[alg] = memalloc
        nt[alg] = sol.destats.naccept
        nf[alg] = sol.destats.nf + sol.destats.nf2
    end
end

function run_benchmark!(results, t, integrators, tol...; c=ones(length(integrators)))
    @progress "Benchmark at t=$t" for τ in zip(tol...)
        runtime = Dict()
        ΔE = Dict()
        nt = Dict()
        nf = Dict()
        b = Dict()
        allocs = Dict()
        cfg = config(integrators, c, τ...)

        GC.gc()
        benchmark(ΔE, runtime, b, allocs, nt, nf, t, cfg)
        get_tol(idx) = haskey(cfg[idx], :dt) ? cfg[idx].dt : (cfg[idx].abstol, cfg[idx].rtol)

        for (idx,i) in enumerate(integrators)
            push!(results, [string(i), runtime[i], get_tol(idx)..., abs(ΔE[i]), nt[i], nf[i], c[idx]])
        end
    end
    return results
end
run_benchmark! (generic function with 1 method)

We will consider symplectic integrators first

symplectic_integrators = [
    VelocityVerlet,
    VerletLeapfrog,
    PseudoVerletLeapfrog,
    McAte2,
    CalvoSanz4,
    McAte5,
    Yoshida6,
    KahanLi8,
    SofSpa10
];

Since for each method there is a different cost for a timestep, we need to take that into account when choosing the tolerances (dts or abstol&reltol) for the solvers. This cost was estimated using the commented code below and the results were hardcoded in order to prevent fluctuations in the results between runs due to differences in callibration times.

The calibration is based on running a simulation with equal tolerances for all solvers and then computing the cost as the runtime / number of timesteps. The absolute value of the cost is not very relevant, so the cost was normalized to the cost of one VelocityVerlet step.

config(integrators, c, τ) = [ (alg=a, dt=τ*cₐ) for (a,cₐ) in zip(integrators, c)]

t = 35.0
τs = 1e-3

# warmup
c_symplectic = ones(length(symplectic_integrators))
benchmark(Dict(), Dict(), Dict(), Dict(), Dict(), Dict(), 10.,
    config(symplectic_integrators, c_symplectic, τs))

# results = DataFrame(:integrator=>String[], :runtime=>Float64[], :τ=>Float64[],
#     :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
# run_benchmark!(results, t, symplectic_integrators, τs)

# c_symplectic .= results[!, :runtime] ./ results[!, :timesteps]
# c_Verlet = c_symplectic[1]
# c_symplectic /= c_Verlet

c_symplectic = [
    1.00,   # VelocityVerlet
    1.05,   # VerletLeapfrog
    0.98,   # PseudoVerletLeapfrog
    1.02,   # McAte2
    2.38,   # CalvoSanz4
    2.92,   # McAte5
    3.74,   # Yoshida6
    8.44,   # KahanLi8
    15.76   # SofSpa10
]
9-element Array{Float64,1}:
  1.0
  1.05
  0.98
  1.02
  2.38
  2.92
  3.74
  8.44
 15.76

Let us now benchmark the solvers for a fixed simulation time and variable timestep

t = 40.0
τs = 10 .^range(-4, -3, length=10)

results = DataFrame(:integrator=>String[], :runtime=>Float64[], =>Float64[],
    :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
run_benchmark!(results, t, symplectic_integrators, τs, c=c_symplectic)

90 rows × 7 columns

integratorruntimeτEnergyErrortimestepsf_evalscost
StringFloat64Float64Float64Int64Int64Float64
1VelocityVerlet2421.640.00010.0006454184000008000021.0
2VerletLeapfrog2315.750.0001059.19241e-538095311428611.05
3PseudoVerletLeapfrog2477.449.8e-50.00070767240816412244940.98
4McAte22384.460.0001020.00028493839215711764731.02
5CalvoSanz42540.640.0002380.0007585416806815126142.38
6McAte52491.720.0002920.0002813213698715068592.92
7Yoshida62600.550.0003740.0035196110695216042823.74
8KahanLi82552.740.0008440.00390624739416587928.44
9SofSpa102737.120.0015760.0004221225381180205315.76
10VelocityVerlet1893.920.0001291550.0002204593097066194141.0
11VerletLeapfrog1792.870.0001356130.001518472949588848761.05
12PseudoVerletLeapfrog1893.310.0001265720.001849843160269480800.98
13McAte21817.680.0001317380.0009662463036339109011.02
14CalvoSanz41961.320.0003073890.00017620713012911711632.38
15McAte51920.20.0003771330.00099969610606411667062.92
16Yoshida61977.240.000483040.00106628280912421373.74
17KahanLi81969.410.001090070.01687583669512843278.44
18SofSpa102136.830.002035480.009290519652139529415.76
19VelocityVerlet1463.220.000166810.001322582397944795901.0
20VerletLeapfrog1381.280.0001751510.000303292283756851271.05
21PseudoVerletLeapfrog1474.350.0001634740.006878612446887340660.98
22McAte21419.580.0001701460.0002788062350927052781.02
23CalvoSanz41515.240.0003970080.000625391007549067882.38
24McAte51488.920.0004870850.00175768821229033442.92
25Yoshida61540.920.000623870.000975349641169617423.74
26KahanLi81533.510.001407880.00722301284129944228.44
27SofSpa101654.230.002628930.0015401515216108033815.76
28VelocityVerlet1105.070.0002154430.001536251856643713301.0
29VerletLeapfrog1062.470.0002262160.0009692161768235304711.05
30PseudoVerletLeapfrog1141.860.0002111350.005943571894535683610.98
31McAte21102.680.0002197520.003230691820245460741.02
32CalvoSanz41177.530.0005127550.00156593780107020922.38
33McAte51145.970.0006290950.00166473635846994262.92
34Yoshida61184.060.0008057590.000453861496437446473.74
35KahanLi81187.030.001818340.00278701219997699678.44
36SofSpa101286.00.003395390.01499661178183645315.76
37VelocityVerlet873.6190.0002782560.0002563061437532875081.0
38VerletLeapfrog832.1110.0002921690.0005058531369084107261.05
39PseudoVerletLeapfrog881.1510.0002726910.01088621466874400630.98
40McAte2856.2830.0002838210.002820551409344228041.02
41CalvoSanz4910.0710.0006622490.0022904604015436112.38
42McAte5887.7140.0008125070.00352153492315415432.92
43Yoshida6924.2750.001040680.0104854384375765573.74
44KahanLi8927.5430.002348480.0059155170335961578.44
45SofSpa10984.2360.004385310.00267222912264766415.76
46VelocityVerlet671.4350.0003593810.003329681113032226081.0
47VerletLeapfrog637.0830.000377350.004578421060033180111.05
48PseudoVerletLeapfrog694.0130.0003521940.02062041135743407240.98
49McAte2654.2220.0003665690.001487441091203273621.02
50CalvoSanz4703.7250.0008553280.00119014467664208962.38
51McAte5687.6680.001049390.00394261381184193002.92
52Yoshida6721.4450.001344090.00695213297604464023.74
53KahanLi8718.8360.003033180.0245536131884615828.44
54SofSpa10776.8040.005663850.00557477706350147515.76
55VelocityVerlet518.3590.0004641590.0100122861781723581.0
56VerletLeapfrog498.8660.0004873670.00116252820742462241.05
57PseudoVerletLeapfrog532.8750.0004548760.0259983879372638130.98
58McAte2513.380.0004734420.0060436844882534661.02
59CalvoSanz4542.5310.00110470.00348148362093258832.38
60McAte5537.6210.001355340.00168782295133246452.92
61Yoshida6553.030.001735950.00309194230433456473.74
62KahanLi8558.0060.00391750.0200092102113573878.44
63SofSpa10593.7890.007315140.00821115546938830115.76
64VelocityVerlet400.3650.0005994840.00377671667251334521.0
65VerletLeapfrog379.8040.0006294580.000189046635471906431.05
66PseudoVerletLeapfrog412.9410.0005874950.0575549680862042600.98
67McAte2393.6450.0006114740.0095366654161962501.02
68CalvoSanz4421.8930.001426770.00582094280362523262.38
69McAte5411.0790.001750490.00455883228512513632.92
70Yoshida6429.7120.002242070.00264513178412676173.74
71KahanLi8423.6910.005059650.0093204479062767128.44
72SofSpa10461.0890.009447870.00269211423430061615.76
73VelocityVerlet309.7110.0007742640.00423553516621033261.0
74VerletLeapfrog297.9490.0008129770.0367738492021476081.05
75PseudoVerletLeapfrog318.4170.0007587780.0852897527171581530.98
76McAte2302.9050.0007897490.0229745506501519521.02
77CalvoSanz4323.2470.001842750.00315543217071953652.38
78McAte5324.0110.002260850.000218032176931946252.92
79Yoshida6329.4510.002895750.0168708138142072123.74
80KahanLi8332.3070.006534790.063876161222142728.44
81SofSpa10357.340.01220240.0195079327923281115.76
82VelocityVerlet241.5580.0010.012542340001800041.0
83VerletLeapfrog230.460.001050.0599366380961142901.05
84PseudoVerletLeapfrog244.790.000980.171365408171224530.98
85McAte2236.8010.001020.0308871392161176501.02
86CalvoSanz4254.5530.002380.00468181168071512652.38
87McAte5246.5050.002920.00849251136991506912.92
88Yoshida6259.5120.003740.0128785106961604423.74
89KahanLi8258.4740.008440.038204747401659028.44
90SofSpa10275.8340.015760.27519253918027115.76

The energy error as a function of runtime is given by

@df results plot(:EnergyError, :runtime, group=:integrator,
    xscale=:log10, yscale=:log10, xlabel="Energy error", ylabel="Runtime (s)")

Looking at the runtime as a function of timesteps, we can observe that we have a linear dependency for each method, and the slope is the previously computed cost per step.

@df results plot(:timesteps, :runtime, group=:integrator,
    xscale=:log10, yscale=:log10, xlabel="Number of timesteps", ylabel="Runtime (s)")

We can also look at the energy error history

function benchmark(energyerr, rts, ts, t, configs)
    simulation = setup(t)
    prob = SecondOrderODEProblem(simulation)
    for config in configs
        alg = config.alg
        sol, rt = @timed solve(prob, alg(); progress=true, progress_name="$alg", config...)
        result = NBodySimulator.SimulationResult(sol, simulation)
        ΔE(t) = total_energy(result, t) - total_energy(result, 0)
        energyerr[alg] = [ΔE(t) for t in sol.t[2:10^2:end]]
        rts[alg] = rt
        ts[alg] = sol.t[2:10^2:end]
    end
end

ΔE = Dict()
rt = Dict()
ts = Dict()
configs = config(symplectic_integrators, c_symplectic, 2.3e-4)
benchmark(ΔE, rt, ts, 40., configs)

plt = plot(xlabel="Rescaled Time", ylabel="Energy error", legend=:bottomleft);
for c in configs
    plot!(plt, ts[c.alg], abs.(ΔE[c.alg]), label="$(c.alg), $(rt[c.alg])s")
end
plt

Now, let us compare some adaptive methods

adaptive_integrators=[
    # Non-stiff ODE methods
    Tsit5,
    Vern7,
    Vern9,
    # DPRKN
    DPRKN6,
    DPRKN8,
    DPRKN12,
];

Similarly to the case of symplectic methods, we will take into account the average cost per timestep in order to have a fair comparison between the solvers.

config(integrators, c, at, rt) = [ (alg=a, abstol=at*2^cₐ, rtol=rt*2^cₐ) for (a,cₐ) in zip(integrators, c)]

t = 35.0
ats = 10 .^range(-14, -4, length=10)
rts = 10 .^range(-14, -4, length=10)

# warmup
c_adaptive = ones(length(adaptive_integrators))
benchmark(Dict(), Dict(), Dict(), Dict(), Dict(), Dict(), 10.,
    config(adaptive_integrators, 1, ats[1], rts[1]))

# results = DataFrame(:integrator=>String[], :runtime=>Float64[], :abstol=>Float64[],
#    :reltol=>Float64[], :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
# run_benchmark!(results, t, adaptive_integrators, ats[1], rts[1])

# c_adaptive .= results[!, :runtime] ./ results[!, :timesteps]
# c_adaptive /= c_Verlet

c_adaptive = [
    3.55,   # Tsit5,
    7.84,   # Vern7,
    11.38,  # Vern9
    3.56,   # DPRKN6,
    5.10,   # DPRKN8,
    8.85    # DPRKN12,
]
6-element Array{Float64,1}:
  3.55
  7.84
 11.38
  3.56
  5.1
  8.85

Let us now benchmark the solvers for a fixed simulation time and variable timestep

t = 40.0

results = DataFrame(:integrator=>String[], :runtime=>Float64[], :abstol=>Float64[],
    :reltol=>Float64[], :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
run_benchmark!(results, t, adaptive_integrators, ats, rts, c=c_adaptive)

60 rows × 8 columns

integratorruntimeabstolreltolEnergyErrortimestepsf_evalscost
StringFloat64Float64Float64Float64Int64Int64Float64
1Tsit583.67811.17127e-131.17127e-13184.4144052273633.55
2Vern7141.6072.29126e-122.29126e-1217.05363379408127.84
3Vern9174.7952.66515e-112.66515e-115.1073826124259411.38
4DPRKN686.78811.17942e-131.17942e-136.451464426320083.56
5DPRKN883.9923.42968e-133.42968e-130.1087842919296445.1
6DPRKN1283.56134.6144e-124.6144e-121.672231536278148.85
7Tsit583.32891.51275e-121.51275e-12185.3984044272433.55
8Vern7125.0062.95928e-112.95928e-1116.71163390412727.84
9Vern9127.3653.44217e-103.44217e-106.0190225694184211.38
10DPRKN683.29241.52327e-121.52327e-126.576524429320643.56
11DPRKN880.334.4296e-124.4296e-120.1726292910295445.1
12DPRKN1279.74035.95973e-115.95973e-111.8011532276708.85
13Tsit582.70811.95379e-111.95379e-11182.934061274833.55
14Vern7124.5773.82206e-103.82206e-1015.90593391411527.84
15Vern9125.9544.44574e-94.44574e-95.4686525634177811.38
16DPRKN683.63251.96738e-111.96738e-116.485544442321413.56
17DPRKN881.27055.72104e-115.72104e-110.1284652917295545.1
18DPRKN1279.44267.69729e-107.69729e-101.476391529275808.85
19Tsit582.96662.52342e-102.52342e-10184.6684034270993.55
20Vern7126.2994.93638e-94.93638e-916.60173382409227.84
21Vern9127.6885.74189e-85.74189e-85.5126425844216211.38
22DPRKN683.82292.54097e-102.54097e-106.652754443321833.56
23DPRKN880.85037.38901e-107.38901e-100.1493362919296145.1
24DPRKN1279.66559.94143e-99.94143e-91.738751534277248.85
25Tsit582.68633.25912e-93.25912e-9184.3524063274473.55
26Vern7121.2036.37558e-86.37558e-816.64533354404527.84
27Vern9126.4487.41593e-77.41593e-76.1000625564158611.38
28DPRKN682.21293.28179e-93.28179e-96.700314418318403.56
29DPRKN880.51529.54327e-99.54327e-90.1302812918295845.1
30DPRKN1277.91811.28398e-71.28398e-71.369111521274908.85
31Tsit583.73644.20932e-84.20932e-8187.2794048273573.55
32Vern7122.1378.23438e-78.23438e-716.37063364406327.84
33Vern9121.9659.57805e-69.57805e-67.1164924784019411.38
34DPRKN683.80524.2386e-84.2386e-87.051474429319523.56
35DPRKN881.08911.23256e-71.23256e-70.1441062923297345.1
36DPRKN1276.96191.65833e-61.65833e-62.312881505271128.85
37Tsit584.93065.43655e-75.43655e-7183.5664057274833.55
38Vern7125.5351.06351e-51.06351e-516.40623390416427.84
39Vern9118.9850.0001237050.00012370511.781523343888211.38
40DPRKN681.6695.47436e-75.47436e-76.448674391314693.56
41DPRKN880.34661.59191e-61.59191e-60.141342905293645.1
42DPRKN1276.17922.14182e-52.14182e-53.21721460263928.85
43Tsit581.74227.02157e-67.02157e-6188.4484009270273.55
44Vern7129.2620.0001373580.00013735817.15833378425927.84
45Vern9121.330.001597710.0015977123.813422003977811.38
46DPRKN680.45187.07041e-67.07041e-67.18874308307203.56
47DPRKN879.08742.05604e-52.05604e-50.174822875291645.1
48DPRKN1271.09730.0002766260.0002766266.972241378248628.85
49Tsit583.01629.06871e-59.06871e-5207.3923920271893.55
50Vern7126.7170.001774040.0017740426.23673267419727.84
51Vern9126.0590.02063530.020635368.192220804137811.38
52DPRKN675.13259.13178e-59.13178e-510.21224087288933.56
53DPRKN877.38190.0002655470.0002655470.08924822765282645.1
54DPRKN1263.58540.003572760.0035727633.52841210220368.85
55Tsit582.49970.001171270.00117127240.8283590268533.55
56Vern7125.2790.02291260.0229126112.5563029415327.84
57Vern955352.50.2665150.266515324.88120284413011.38
58DPRKN664.81370.001179420.0011794226.24893523251273.56
59DPRKN868.66050.003429680.003429680.2311742408250945.1
60DPRKN1253.70460.0461440.046144571.101998185448.85

The energy error as a function of runtime is given by

@df results plot(:EnergyError, :runtime, group=:integrator,
    xscale=:log10, yscale=:log10, xlabel="Energy error", ylabel="Runtime (s)")

If we consider the number of function evaluations instead, we obtain

@df results plot(:EnergyError, :f_evals, group=:integrator,
    xscale=:log10, yscale=:log10, xlabel="Energy error", ylabel="Number of f evals")

We will now compare the best performing solvers

t = 40.0

symplectic_integrators = [
    VelocityVerlet,
    VerletLeapfrog,
    PseudoVerletLeapfrog,
    McAte2,
    CalvoSanz4
]

c_symplectic = [
    1.00,   # VelocityVerlet
    1.05,   # VerletLeapfrog
    0.98,   # PseudoVerletLeapfrog
    1.02,   # McAte2
    2.38,   # CalvoSanz4
]

results1 = DataFrame(:integrator=>String[], :runtime=>Float64[], =>Float64[],
    :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
run_benchmark!(results1, t, symplectic_integrators, τs, c=c_symplectic)

adaptive_integrators=[
    DPRKN6,
    DPRKN8,
    DPRKN12,
]

c_adaptive = [
    3.56,   # DPRKN6,
    5.10,   # DPRKN8,
    8.85    # DPRKN12,
]

results2 = DataFrame(:integrator=>String[], :runtime=>Float64[], :abstol=>Float64[],
    :reltol=>Float64[], :EnergyError=>Float64[], :timesteps=>Int[], :f_evals=>Int[], :cost=>Float64[]);
run_benchmark!(results2, t, adaptive_integrators, ats, rts, c=c_adaptive)

append!(results1, results2, cols=:union)
results1

80 rows × 9 columns

integratorruntimeτEnergyErrortimestepsf_evalscostabstolreltol
StringFloat64Float64?Float64Int64Int64Float64Float64?Float64?
1VelocityVerlet2425.520.00010.0006454184000008000021.0missingmissing
2VerletLeapfrog2322.440.0001059.19241e-538095311428611.05missingmissing
3PseudoVerletLeapfrog2481.629.8e-50.00070767240816412244940.98missingmissing
4McAte22390.80.0001020.00028493839215711764731.02missingmissing
5CalvoSanz42546.970.0002380.0007585416806815126142.38missingmissing
6VelocityVerlet1901.550.0001291550.0002204593097066194141.0missingmissing
7VerletLeapfrog1797.920.0001356130.001518472949588848761.05missingmissing
8PseudoVerletLeapfrog1902.830.0001265720.001849843160269480800.98missingmissing
9McAte21824.080.0001317380.0009662463036339109011.02missingmissing
10CalvoSanz41968.830.0003073890.00017620713012911711632.38missingmissing
11VelocityVerlet1468.180.000166810.001322582397944795901.0missingmissing
12VerletLeapfrog1387.150.0001751510.000303292283756851271.05missingmissing
13PseudoVerletLeapfrog1479.960.0001634740.006878612446887340660.98missingmissing
14McAte21423.680.0001701460.0002788062350927052781.02missingmissing
15CalvoSanz41522.140.0003970080.000625391007549067882.38missingmissing
16VelocityVerlet1108.710.0002154430.001536251856643713301.0missingmissing
17VerletLeapfrog1067.630.0002262160.0009692161768235304711.05missingmissing
18PseudoVerletLeapfrog1145.130.0002111350.005943571894535683610.98missingmissing
19McAte21106.40.0002197520.003230691820245460741.02missingmissing
20CalvoSanz41180.860.0005127550.00156593780107020922.38missingmissing
21VelocityVerlet876.8070.0002782560.0002563061437532875081.0missingmissing
22VerletLeapfrog835.9310.0002921690.0005058531369084107261.05missingmissing
23PseudoVerletLeapfrog884.4560.0002726910.01088621466874400630.98missingmissing
24McAte2856.7150.0002838210.002820551409344228041.02missingmissing
25CalvoSanz4912.4490.0006622490.0022904604015436112.38missingmissing
26VelocityVerlet674.3640.0003593810.003329681113032226081.0missingmissing
27VerletLeapfrog639.3340.000377350.004578421060033180111.05missingmissing
28PseudoVerletLeapfrog696.6830.0003521940.02062041135743407240.98missingmissing
29McAte2656.410.0003665690.001487441091203273621.02missingmissing
30CalvoSanz4707.9790.0008553280.00119014467664208962.38missingmissing
31VelocityVerlet521.650.0004641590.0100122861781723581.0missingmissing
32VerletLeapfrog502.1510.0004873670.00116252820742462241.05missingmissing
33PseudoVerletLeapfrog536.6350.0004548760.0259983879372638130.98missingmissing
34McAte2515.6210.0004734420.0060436844882534661.02missingmissing
35CalvoSanz4543.7710.00110470.00348148362093258832.38missingmissing
36VelocityVerlet401.7980.0005994840.00377671667251334521.0missingmissing
37VerletLeapfrog381.0380.0006294580.000189046635471906431.05missingmissing
38PseudoVerletLeapfrog414.8630.0005874950.0575549680862042600.98missingmissing
39McAte2394.8950.0006114740.0095366654161962501.02missingmissing
40CalvoSanz4423.5720.001426770.00582094280362523262.38missingmissing
41VelocityVerlet310.4690.0007742640.00423553516621033261.0missingmissing
42VerletLeapfrog299.0660.0008129770.0367738492021476081.05missingmissing
43PseudoVerletLeapfrog319.5660.0007587780.0852897527171581530.98missingmissing
44McAte2303.0630.0007897490.0229745506501519521.02missingmissing
45CalvoSanz4323.8480.001842750.00315543217071953652.38missingmissing
46VelocityVerlet242.860.0010.012542340001800041.0missingmissing
47VerletLeapfrog231.9850.001050.0599366380961142901.05missingmissing
48PseudoVerletLeapfrog245.6360.000980.171365408171224530.98missingmissing
49McAte2237.2060.001020.0308871392161176501.02missingmissing
50CalvoSanz4254.9910.002380.00468181168071512652.38missingmissing
51DPRKN683.3292missing6.451464426320083.561.17942e-131.17942e-13
52DPRKN881.186missing0.1087842919296445.13.42968e-133.42968e-13
53DPRKN1279.3687missing1.672231536278148.854.6144e-124.6144e-12
54DPRKN683.1779missing6.576524429320643.561.52327e-121.52327e-12
55DPRKN880.5578missing0.1726292910295445.14.4296e-124.4296e-12
56DPRKN1279.623missing1.8011532276708.855.95973e-115.95973e-11
57DPRKN683.8472missing6.485544442321413.561.96738e-111.96738e-11
58DPRKN881.7604missing0.1284652917295545.15.72104e-115.72104e-11
59DPRKN1279.5775missing1.476391529275808.857.69729e-107.69729e-10
60DPRKN683.6119missing6.652754443321833.562.54097e-102.54097e-10
61DPRKN881.0309missing0.1493362919296145.17.38901e-107.38901e-10
62DPRKN1279.4624missing1.738751534277248.859.94143e-99.94143e-9
63DPRKN682.4053missing6.700314418318403.563.28179e-93.28179e-9
64DPRKN880.9031missing0.1302812918295845.19.54327e-99.54327e-9
65DPRKN1277.5087missing1.369111521274908.851.28398e-71.28398e-7
66DPRKN683.26missing7.051474429319523.564.2386e-84.2386e-8
67DPRKN880.9709missing0.1441062923297345.11.23256e-71.23256e-7
68DPRKN1276.8834missing2.312881505271128.851.65833e-61.65833e-6
69DPRKN681.6245missing6.448674391314693.565.47436e-75.47436e-7
70DPRKN880.8904missing0.141342905293645.11.59191e-61.59191e-6
71DPRKN1275.69missing3.21721460263928.852.14182e-52.14182e-5
72DPRKN680.261missing7.18874308307203.567.07041e-67.07041e-6
73DPRKN879.0331missing0.174822875291645.12.05604e-52.05604e-5
74DPRKN1271.0137missing6.972241378248628.850.0002766260.000276626
75DPRKN675.108missing10.21224087288933.569.13178e-59.13178e-5
76DPRKN877.4294missing0.08924822765282645.10.0002655470.000265547
77DPRKN1263.556missing33.52841210220368.850.003572760.00357276
78DPRKN664.6227missing26.24893523251273.560.001179420.00117942
79DPRKN868.8603missing0.2311742408250945.10.003429680.00342968
80DPRKN1253.5901missing571.101998185448.850.0461440.046144

The energy error as a function of runtime is given by

@df results1 plot(:EnergyError, :runtime, group=:integrator,
    xscale=:log10, yscale=:log10, xlabel="Energy error", ylabel="Runtime (s)")