1
+ using OrdinaryDiffEq, BenchmarkTools, DiffEqBase
2
+ using LinearAlgebra, SparseArrays, StaticArrays, StableRNGs
3
+
4
+ const SUITE = BenchmarkGroup ()
5
+
6
+ # =============================================================================
7
+ # Non-Stiff ODE Problems
8
+ # =============================================================================
9
+
10
+ SUITE[" nonstiff" ] = BenchmarkGroup ()
11
+
12
+ # Lotka-Volterra (Predator-Prey) Problem
13
+ function lotka_volterra! (du, u, p, t)
14
+ α, β, γ, δ = p
15
+ x, y = u
16
+ du[1 ] = α* x - β* x* y # dx/dt
17
+ du[2 ] = δ* x* y - γ* y # dy/dt
18
+ nothing
19
+ end
20
+
21
+ function lotka_volterra_prob ()
22
+ p = (1.5 , 1.0 , 3.0 , 1.0 ) # α, β, γ, δ
23
+ u0 = [1.0 , 1.0 ]
24
+ tspan = (0.0 , 10.0 )
25
+ ODEProblem (lotka_volterra!, u0, tspan, p)
26
+ end
27
+
28
+ # Pleiades Problem (7-body celestial mechanics)
29
+ function pleiades! (du, u, p, t)
30
+ # u[1:7] = x positions, u[8:14] = y positions
31
+ # u[15:21] = x velocities, u[22:28] = y velocities
32
+ x = @view u[1 : 7 ]
33
+ y = @view u[8 : 14 ]
34
+ vx = @view u[15 : 21 ]
35
+ vy = @view u[22 : 28 ]
36
+
37
+ # Copy velocities to position derivatives
38
+ du[1 : 7 ] .= vx
39
+ du[8 : 14 ] .= vy
40
+
41
+ # Calculate accelerations
42
+ fill! (du[15 : 21 ], 0.0 )
43
+ fill! (du[22 : 28 ], 0.0 )
44
+
45
+ for i in 1 : 7
46
+ for j in 1 : 7
47
+ if i != j
48
+ dx = x[j] - x[i]
49
+ dy = y[j] - y[i]
50
+ r = sqrt (dx^ 2 + dy^ 2 )
51
+ r3 = r^ 3
52
+ du[14 + i] += j * dx / r3 # mass j = j
53
+ du[21 + i] += j * dy / r3
54
+ end
55
+ end
56
+ end
57
+ nothing
58
+ end
59
+
60
+ function pleiades_prob ()
61
+ # Initial conditions from literature
62
+ u0 = [3.0 ,3.0 ,- 1.0 ,- 3.0 ,2.0 ,- 2.0 ,2.0 ,3.0 ,- 3.0 ,2.0 ,0 ,0 ,- 4.0 ,4.0 ,
63
+ 0 ,0 ,0 ,0 ,0 ,1.75 ,- 1.5 ,0 ,0 ,0 ,- 1.25 ,1 ,0 ,0 ]
64
+ tspan = (0.0 , 3.0 )
65
+ ODEProblem (pleiades!, u0, tspan)
66
+ end
67
+
68
+ # FitzHugh-Nagumo Model
69
+ function fitzhugh_nagumo! (du, u, p, t)
70
+ a, b, c = p
71
+ v, w = u
72
+ du[1 ] = c * (v - v^ 3 / 3 + w) # dv/dt
73
+ du[2 ] = - (v - a + b* w) / c # dw/dt
74
+ nothing
75
+ end
76
+
77
+ function fitzhugh_nagumo_prob ()
78
+ p = (0.7 , 0.8 , 12.5 )
79
+ u0 = [- 1.0 , 1.0 ]
80
+ tspan = (0.0 , 20.0 )
81
+ ODEProblem (fitzhugh_nagumo!, u0, tspan, p)
82
+ end
83
+
84
+ # =============================================================================
85
+ # Stiff ODE Problems
86
+ # =============================================================================
87
+
88
+ SUITE[" stiff" ] = BenchmarkGroup ()
89
+
90
+ # ROBER Problem (Robertson chemical kinetics)
91
+ function rober! (du, u, p, t)
92
+ k1, k2, k3 = p
93
+ y1, y2, y3 = u
94
+ du[1 ] = - k1* y1 + k3* y2* y3 # dy1/dt
95
+ du[2 ] = k1* y1 - k2* y2^ 2 - k3* y2* y3 # dy2/dt
96
+ du[3 ] = k2* y2^ 2 # dy3/dt
97
+ nothing
98
+ end
99
+
100
+ function rober_prob ()
101
+ p = (0.04 , 3e7 , 1e4 ) # k1, k2, k3
102
+ u0 = [1.0 , 0.0 , 0.0 ]
103
+ tspan = (0.0 , 1e5 )
104
+ ODEProblem (rober!, u0, tspan, p)
105
+ end
106
+
107
+ # Van der Pol Oscillator (stiff)
108
+ function van_der_pol! (du, u, p, t)
109
+ μ = p[1 ]
110
+ x, y = u
111
+ du[1 ] = y # dx/dt
112
+ du[2 ] = μ * ((1 - x^ 2 )* y - x) # dy/dt
113
+ nothing
114
+ end
115
+
116
+ function van_der_pol_prob ()
117
+ p = [1e6 ] # very stiff
118
+ u0 = [1.0 , 1.0 ]
119
+ tspan = (0.0 , 6.3 )
120
+ ODEProblem (van_der_pol!, u0, tspan, p)
121
+ end
122
+
123
+ # Pollution Problem (atmospheric chemistry, 20D stiff system)
124
+ const k1= .35e0
125
+ const k2= .266e2
126
+ const k3= .123e5
127
+ const k4= .86e-3
128
+ const k5= .82e-3
129
+ const k6= .15e5
130
+ const k7= .13e-3
131
+ const k8= .24e5
132
+ const k9= .165e5
133
+ const k10= .9e4
134
+ const k11= .22e-1
135
+ const k12= .12e5
136
+ const k13= .188e1
137
+ const k14= .163e5
138
+ const k15= .48e7
139
+ const k16= .35e-3
140
+ const k17= .175e-1
141
+ const k18= .1e9
142
+ const k19= .444e12
143
+ const k20= .124e4
144
+ const k21= .21e1
145
+ const k22= .578e1
146
+ const k23= .474e-1
147
+ const k24= .178e4
148
+ const k25= .312e1
149
+
150
+ function pollution! (dy, y, p, t)
151
+ r1 = k1 * y[1 ]
152
+ r2 = k2 * y[2 ]* y[4 ]
153
+ r3 = k3 * y[5 ]* y[2 ]
154
+ r4 = k4 * y[7 ]
155
+ r5 = k5 * y[7 ]
156
+ r6 = k6 * y[7 ]* y[6 ]
157
+ r7 = k7 * y[9 ]
158
+ r8 = k8 * y[9 ]* y[6 ]
159
+ r9 = k9 * y[11 ]* y[2 ]
160
+ r10 = k10* y[11 ]* y[1 ]
161
+ r11 = k11* y[13 ]
162
+ r12 = k12* y[10 ]* y[2 ]
163
+ r13 = k13* y[14 ]
164
+ r14 = k14* y[1 ]* y[6 ]
165
+ r15 = k15* y[3 ]
166
+ r16 = k16* y[4 ]
167
+ r17 = k17* y[4 ]
168
+ r18 = k18* y[16 ]
169
+ r19 = k19* y[16 ]
170
+ r20 = k20* y[17 ]* y[6 ]
171
+ r21 = k21* y[19 ]
172
+ r22 = k22* y[19 ]
173
+ r23 = k23* y[1 ]* y[4 ]
174
+ r24 = k24* y[19 ]* y[1 ]
175
+ r25 = k25* y[20 ]
176
+
177
+ dy[1 ] = - r1- r10- r14- r23- r24+
178
+ r2+ r3+ r9+ r11+ r12+ r22+ r25
179
+ dy[2 ] = - r2- r3- r9- r12+ r1+ r21
180
+ dy[3 ] = - r15+ r1+ r17+ r19+ r22
181
+ dy[4 ] = - r2- r16- r17- r23+ r15
182
+ dy[5 ] = - r3+ r4+ r4+ r6+ r7+ r13+ r20
183
+ dy[6 ] = - r6- r8- r14- r20+ r3+ r18+ r18
184
+ dy[7 ] = - r4- r5- r6+ r13
185
+ dy[8 ] = r4+ r5+ r6+ r7
186
+ dy[9 ] = - r7- r8
187
+ dy[10 ] = - r12+ r7+ r9
188
+ dy[11 ] = - r9- r10+ r8+ r11
189
+ dy[12 ] = r9
190
+ dy[13 ] = - r11+ r10
191
+ dy[14 ] = - r13+ r12
192
+ dy[15 ] = r14
193
+ dy[16 ] = - r18- r19+ r16
194
+ dy[17 ] = - r20
195
+ dy[18 ] = r20
196
+ dy[19 ] = - r21- r22- r24+ r23+ r25
197
+ dy[20 ] = - r25+ r24
198
+ nothing
199
+ end
200
+
201
+ function pollution_prob ()
202
+ u0 = zeros (20 )
203
+ u0[2 ] = 0.2
204
+ u0[4 ] = 0.04
205
+ u0[7 ] = 0.1
206
+ u0[8 ] = 0.3
207
+ u0[9 ] = 0.01
208
+ u0[17 ] = 0.007
209
+ tspan = (0.0 , 60.0 )
210
+ ODEProblem (pollution!, u0, tspan)
211
+ end
212
+
213
+
214
+ # =============================================================================
215
+ # Scaling Problems (different dimensions)
216
+ # =============================================================================
217
+
218
+ SUITE[" scaling" ] = BenchmarkGroup ()
219
+
220
+ # Linear ODE with varying dimensions
221
+ function linear_ode! (du, u, p, t)
222
+ A = p
223
+ mul! (du, A, u)
224
+ nothing
225
+ end
226
+
227
+ function create_linear_prob (N:: Int )
228
+ rng = StableRNG (123 ) # Fixed seed for reproducibility
229
+ A = randn (rng, N, N)
230
+ A = A - A' # Make skew-symmetric for bounded solutions
231
+ u0 = randn (rng, N)
232
+ tspan = (0.0 , 1.0 )
233
+ ODEProblem (linear_ode!, u0, tspan, A)
234
+ end
235
+
236
+ # Brusselator PDE (2D reaction-diffusion from advanced ODE example)
237
+ # Forcing function - creates a localized disturbance
238
+ brusselator_f (x, y, t) = (((x - 0.3 )^ 2 + (y - 0.6 )^ 2 ) <= 0.1 ^ 2 ) * (t >= 1.1 ) * 5.0
239
+
240
+ # Periodic boundary condition helper
241
+ limit (a, N) = a == N + 1 ? 1 : a == 0 ? N : a
242
+
243
+ function brusselator_2d! (du, u, p, t)
244
+ A, B, α, dx, N = p
245
+ α = α / dx^ 2
246
+
247
+ # Create coordinate arrays for this N
248
+ xyd = range (0 , stop = 1 , length = N)
249
+
250
+ @inbounds for I in CartesianIndices ((N, N))
251
+ i, j = Tuple (I)
252
+ x, y = xyd[i], xyd[j]
253
+ ip1, im1, jp1, jm1 = limit (i + 1 , N), limit (i - 1 , N), limit (j + 1 , N), limit (j - 1 , N)
254
+
255
+ # u equation: ∂u/∂t = 1 + u²v - 4.4u + α∇²u + f(x,y,t)
256
+ du[i, j, 1 ] = α * (u[im1, j, 1 ] + u[ip1, j, 1 ] + u[i, jp1, 1 ] + u[i, jm1, 1 ] - 4 * u[i, j, 1 ]) +
257
+ B + u[i, j, 1 ]^ 2 * u[i, j, 2 ] - (A + 1 ) * u[i, j, 1 ] +
258
+ brusselator_f (x, y, t)
259
+
260
+ # v equation: ∂v/∂t = 3.4u - u²v + α∇²v
261
+ du[i, j, 2 ] = α * (u[im1, j, 2 ] + u[ip1, j, 2 ] + u[i, jp1, 2 ] + u[i, jm1, 2 ] - 4 * u[i, j, 2 ]) +
262
+ A * u[i, j, 1 ] - u[i, j, 1 ]^ 2 * u[i, j, 2 ]
263
+ end
264
+ nothing
265
+ end
266
+
267
+ function init_brusselator_2d (N:: Int )
268
+ xyd = range (0 , stop = 1 , length = N)
269
+ u = zeros (N, N, 2 )
270
+ for I in CartesianIndices ((N, N))
271
+ x = xyd[I[1 ]]
272
+ y = xyd[I[2 ]]
273
+ u[I, 1 ] = 22 * (y * (1 - y))^ (3 / 2 ) # u initial condition
274
+ u[I, 2 ] = 27 * (x * (1 - x))^ (3 / 2 ) # v initial condition
275
+ end
276
+ u
277
+ end
278
+
279
+ function create_brusselator_2d_prob (N:: Int )
280
+ A, B, α = 3.4 , 1.0 , 10.0
281
+ xyd = range (0 , stop = 1 , length = N)
282
+ dx = step (xyd)
283
+ u0 = init_brusselator_2d (N)
284
+ tspan = (0.0 , 11.5 )
285
+ ODEProblem (brusselator_2d!, u0, tspan, (A, B, α, dx, N))
286
+ end
287
+
288
+ # =============================================================================
289
+ # Benchmark Definitions
290
+ # =============================================================================
291
+
292
+ # Non-stiff benchmarks with different solvers
293
+ lv_prob = lotka_volterra_prob ()
294
+ pl_prob = pleiades_prob ()
295
+ fn_prob = fitzhugh_nagumo_prob ()
296
+
297
+ # Explicit RK methods for non-stiff problems
298
+ explicit_solvers = [Tsit5 (), Vern6 (), Vern7 (), DP5 (), BS3 ()]
299
+
300
+ SUITE[" nonstiff" ][" lotka_volterra" ] = BenchmarkGroup ()
301
+ SUITE[" nonstiff" ][" pleiades" ] = BenchmarkGroup ()
302
+ SUITE[" nonstiff" ][" fitzhugh_nagumo" ] = BenchmarkGroup ()
303
+
304
+ for solver in explicit_solvers
305
+ solver_name = string (typeof (solver). name. name)
306
+ SUITE[" nonstiff" ][" lotka_volterra" ][solver_name] = @benchmarkable solve ($ lv_prob, $ solver, reltol= 1e-6 , abstol= 1e-8 )
307
+ SUITE[" nonstiff" ][" pleiades" ][solver_name] = @benchmarkable solve ($ pl_prob, $ solver, reltol= 1e-6 , abstol= 1e-8 )
308
+ SUITE[" nonstiff" ][" fitzhugh_nagumo" ][solver_name] = @benchmarkable solve ($ fn_prob, $ solver, reltol= 1e-6 , abstol= 1e-8 )
309
+ end
310
+
311
+ # Stiff benchmarks with different solvers
312
+ rober_prob_instance = rober_prob ()
313
+ vdp_prob = van_der_pol_prob ()
314
+ pollution_prob_instance = pollution_prob ()
315
+
316
+ # Stiff solvers
317
+ stiff_solvers = [Rosenbrock23 (), Rodas4 (), TRBDF2 (), KenCarp4 (), FBDF ()]
318
+
319
+ SUITE[" stiff" ][" rober" ] = BenchmarkGroup ()
320
+ SUITE[" stiff" ][" van_der_pol" ] = BenchmarkGroup ()
321
+ SUITE[" stiff" ][" pollution" ] = BenchmarkGroup ()
322
+
323
+ for solver in stiff_solvers
324
+ solver_name = string (typeof (solver). name. name)
325
+ SUITE[" stiff" ][" rober" ][solver_name] = @benchmarkable solve ($ rober_prob_instance, $ solver, reltol= 1e-6 , abstol= 1e-8 )
326
+ SUITE[" stiff" ][" van_der_pol" ][solver_name] = @benchmarkable solve ($ vdp_prob, $ solver, reltol= 1e-6 , abstol= 1e-8 )
327
+ SUITE[" stiff" ][" pollution" ][solver_name] = @benchmarkable solve ($ pollution_prob_instance, $ solver, reltol= 1e-6 , abstol= 1e-8 )
328
+ end
329
+
330
+ # Scaling benchmarks
331
+ SUITE[" scaling" ][" linear" ] = BenchmarkGroup ()
332
+ SUITE[" scaling" ][" brusselator_2d" ] = BenchmarkGroup ()
333
+
334
+ # Linear ODE scaling (different problem sizes)
335
+ for N in [10 , 50 , 100 ]
336
+ prob = create_linear_prob (N)
337
+ SUITE[" scaling" ][" linear" ][" N$N " ] = @benchmarkable solve ($ prob, Tsit5 (), reltol= 1e-6 , abstol= 1e-8 )
338
+ end
339
+
340
+ # Brusselator 2D scaling (different grid sizes)
341
+ for N in [8 , 16 , 32 ]
342
+ prob = create_brusselator_2d_prob (N)
343
+ SUITE[" scaling" ][" brusselator_2d" ][" $(N) x$(N) " ] = @benchmarkable solve ($ prob, TRBDF2 (),
344
+ reltol= 1e-4 , abstol= 1e-6 , maxiters= 1000 )
345
+ end
346
+
347
+ # =============================================================================
348
+ # Problem Construction Benchmarks
349
+ # =============================================================================
350
+
351
+ SUITE[" construction" ] = BenchmarkGroup ()
352
+
353
+ # Test problem construction overhead
354
+ SUITE[" construction" ][" lotka_volterra" ] = @benchmarkable lotka_volterra_prob ()
355
+ SUITE[" construction" ][" rober" ] = @benchmarkable rober_prob ()
356
+ SUITE[" construction" ][" linear_N50" ] = @benchmarkable create_linear_prob (50 )
0 commit comments