Skip to content

Commit 0b299ec

Browse files
Copilotivan-pi
andauthored
Expand ODE solver stats and align solout grid-point callback semantics (#36)
* Expand solver stats to include steps and back-substitutions Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/562c29e2-5602-4cc7-965b-83224e87db8c Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Refine stats test expectation for linear-system solve counter Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/562c29e2-5602-4cc7-965b-83224e87db8c Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Adjust solout initial callback and reorder solver stats Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/8e92236a-2f09-487c-9169-cb3dc8700e1a Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Refine stats test readability and README stats output format Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/ccb23e84-4807-42ce-a5c1-5b2811603ce3 Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Normalize README stats label spacing Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/ccb23e84-4807-42ce-a5c1-5b2811603ce3 Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com>
1 parent c250de6 commit 0b299ec

11 files changed

Lines changed: 82 additions & 78 deletions

README.md

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This repository provides a heavily refactored version of the original implementa
1616
- Third-order Rosenbrock-type (semi-implicit Runge-Kutta) integrator suitable for stiff ODE systems
1717
- Adaptive stepsize control with error tolerance per component
1818
- Optional maximum absolute half-step size (`hmax`) to cap step growth
19-
- Optional runtime statistics output: rhs evaluations (`nfev`), Jacobian evaluations (`njev`), and LU decompositions (`nlu`)
19+
- Optional runtime statistics output: successful steps (`nacc`), rejected steps (`nrej`), rhs evaluations (`nfev`), Jacobian evaluations (`njev`), LU decompositions (`nlu`), and linear-system solves (`nsol`)
2020
- Optional explicit workspace interface via `stiff3(..., rwork, iwork, ...)` for caller-managed memory
2121
- Dense output helper `stiff3_interp` for accepted steps in `solout`
2222
- Requires an exact user-supplied Jacobian
@@ -95,7 +95,7 @@ program vanpol
9595
integer, parameter :: n = 2
9696
real(wp), parameter :: mu = 10.0_wp
9797
real(wp) :: y(n), w(n), x0, x1, h0, eps, hmax
98-
integer :: irtrn, stats(3)
98+
integer :: stats(6)
9999
100100
! initial value
101101
y = [1.0_wp, 1.0_wp]
@@ -109,12 +109,15 @@ program vanpol
109109
! time interval
110110
x0 = 0.0_wp
111111
x1 = 100.0_wp
112-
! output initial condition
113-
irtrn = 0
114-
call out(0,x0,x0,y,0,0.0_wp,irtrn)
115112
! integrate system of ODEs
116113
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out,stats=stats,hmax=hmax)
117-
print '(A,3(I0,1X))', 'nfev njev nlu: ', stats
114+
print '(A)', 'Solver statistics'
115+
print '(A,I0)', ' nacc: ', stats(1)
116+
print '(A,I0)', ' nrej: ', stats(2)
117+
print '(A,I0)', ' nfev: ', stats(3)
118+
print '(A,I0)', ' njev: ', stats(4)
119+
print '(A,I0)', ' nlu: ', stats(5)
120+
print '(A,I0)', ' nsol: ', stats(6)
118121
119122
contains
120123

example/lorenz.f90

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ program lorenz
77
integer, parameter :: n = 3
88
real(wp), parameter :: sigma = 10.0_wp, rho = 28.0_wp, beta = 8.0_wp/3.0_wp
99
real(wp) :: y(n), w(n), x0, x1, h0, eps
10-
integer :: irtrn
1110

1211
! initial value
1312
y = [1.0_wp, 1.0_wp, 1.0_wp]
@@ -19,9 +18,6 @@ program lorenz
1918
! time interval
2019
x0 = 0.0_wp
2120
x1 = 40.0_wp
22-
! output initial condition
23-
irtrn = 0
24-
call out(0,x0,x0,y,0,0.0_wp,irtrn)
2521
! integrate system of ODEs
2622
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out)
2723

example/pendant_drop.f90

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ program pendant_drop
77
integer, parameter :: n = 3
88
real(wp) :: y(n), w(n), x0, x1, h0, eps
99
real(wp) :: pL, drho, ds
10+
integer :: stats(6)
1011

1112
! dimensionless pressure offset and density difference
1213
pL = 3.3888_wp
@@ -27,10 +28,10 @@ program pendant_drop
2728
eps = 1.0e-6_wp
2829
w = 1.0_wp
2930

30-
! output initial condition at s = 0
31-
write(*,'(4(E18.12,2X),I4,2X,G0)') 0.0_wp, 0.0_wp, 0.0_wp, 0.0_wp, 0, 0.0_wp
3231
! integrate Young-Laplace system
33-
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out)
32+
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out,stats=stats)
33+
print '(A,3(I0,1X))', 'accepted rejected nfev: ', stats(1), stats(2), stats(3)
34+
print '(A,3(I0,1X))', 'njev nlu nsol: ', stats(4), stats(5), stats(6)
3435

3536
contains
3637

example/predator_prey.f90

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ program predator_prey
88
real(wp), parameter :: alpha = 1.1_wp, beta = 0.4_wp
99
real(wp), parameter :: delta = 0.1_wp, gamma = 0.4_wp
1010
real(wp) :: y(n), w(n), x0, x1, h0, eps
11-
integer :: irtrn
1211

1312
! initial value
1413
y = [10.0_wp, 5.0_wp]
@@ -20,9 +19,6 @@ program predator_prey
2019
! time interval
2120
x0 = 0.0_wp
2221
x1 = 40.0_wp
23-
! output initial condition
24-
irtrn = 0
25-
call out(0,x0,x0,y,0,0.0_wp,irtrn)
2622
! integrate system of ODEs
2723
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out)
2824

example/robertson.f90

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ program main
4949
real(wp) :: y(n), w(n)
5050
real(wp) :: h0, eps, x0, x1
5151

52-
integer :: irtrn
5352

5453
! initial value
5554
y = [1.0_wp, 0.0_wp, 0.0_wp]
@@ -66,8 +65,6 @@ program main
6665
x0 = 0.0_wp
6766
x1 = 10.0_wp
6867

69-
irtrn = 0
70-
call output(0,x0,x0,y,0,0.0_wp,irtrn)
7168
call stiff3(n,fun,x0,y,x1,dfun,h0,eps,w,solout=output)
7269

7370
contains

example/three_equation_system.f90

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ program three_equation_system
88
real(wp), parameter :: k2 = 1000.0_wp
99
real(wp), parameter :: k3 = 2500.0_wp
1010
real(wp) :: y(n), w(n), x0, x1, h0, eps
11-
integer :: irtrn, nsteps
11+
integer :: stats(6)
1212

1313
! initial value
1414
y = [1.0_wp, 1.0_wp, 0.0_wp]
@@ -21,12 +21,9 @@ program three_equation_system
2121
x0 = 0.0_wp
2222
x1 = 50.0_wp
2323

24-
nsteps = 0
25-
irtrn = 0
26-
call out(0,x0,x0,y,0,0.0_wp,irtrn)
27-
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out)
28-
29-
print '(A,I0)', 'Accepted steps required: ', nsteps
24+
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,stats=stats)
25+
print '(A,3(I0,1X))', 'accepted rejected nfev: ', stats(1), stats(2), stats(3)
26+
print '(A,3(I0,1X))', 'njev nlu nsol: ', stats(4), stats(5), stats(6)
3027

3128
contains
3229

@@ -58,16 +55,4 @@ subroutine jac(n,y,df)
5855
df(3,3) = -k2*y(1) - k3*y(2)
5956
end subroutine
6057

61-
subroutine out(nr,told,t,y,ih,qa,irtrn)
62-
integer, intent(in) :: nr
63-
real(wp), intent(in) :: told
64-
real(wp), intent(in) :: t
65-
real(wp), intent(in) :: y(:)
66-
integer, intent(in) :: ih
67-
real(wp), intent(in) :: qa
68-
integer, intent(inout) :: irtrn
69-
70-
nsteps = nr
71-
end subroutine
72-
7358
end program

example/vanpol.f90

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ program vanpol
66
integer, parameter :: n = 2
77
real(wp), parameter :: mu = 10.0_wp
88
real(wp) :: y(n), w(n), x0, x1, h0, eps
9-
integer :: irtrn
109

1110
! initial value
1211
y = [1.0_wp, 1.0_wp]
@@ -18,9 +17,6 @@ program vanpol
1817
! time interval
1918
x0 = 0.0_wp
2019
x1 = 100.0_wp
21-
! output initial condition
22-
irtrn = 0
23-
call out(0,x0,x0,y,0,0.0_wp,irtrn)
2420
! integrate system of ODEs
2521
call stiff3(n,fun,x0,y,x1,jac,h0,eps,w,solout=out)
2622

src/stiff3_solver.f90

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ subroutine jacobian_sub(n,y,df)
4848
subroutine output_sub(nr,xold,x,y,iha,qa,irtrn)
4949
import wp
5050
integer, intent(in) :: nr
51-
!! Number of successful steps that have been taken
51+
!! Number of the current grid point, starting at 1 for the initial value
5252
real(wp), intent(in) :: xold
5353
!! Previous value of the independent variable
5454
real(wp), intent(in) :: x
@@ -105,8 +105,8 @@ subroutine stiff3_auto(n,fun,x,y,xend,jac,h0,eps,w,solout,stats,hmax)
105105
!! dependent variables at `xend`.
106106
procedure(output_sub), optional :: solout
107107
!! User supplied subprogram for output.
108-
integer, intent(out), optional :: stats(3)
109-
!! Statistics array with `[nfev, njev, nlu]`.
108+
integer, intent(out), optional :: stats(6)
109+
!! Statistics array with `[nacc, nrej, nfev, njev, nlu, nsol]`.
110110
real(wp), intent(in), optional :: hmax
111111
!! Maximum absolute half-step size. If absent or zero, defaults to
112112
!! `abs(xend - x)`.
@@ -153,8 +153,8 @@ subroutine stiff3_work(n,fun,x,y,xend,jac,h0,eps,w,rwork,iwork,solout,stats,hmax
153153
!! Integer workspace of size `n`.
154154
procedure(output_sub), optional :: solout
155155
!! User supplied subprogram for output.
156-
integer, intent(out), optional :: stats(3)
157-
!! Statistics array with `[nfev, njev, nlu]`.
156+
integer, intent(out), optional :: stats(6)
157+
!! Statistics array with `[nacc, nrej, nfev, njev, nlu, nsol]`.
158158
real(wp), intent(in), optional :: hmax
159159
!! Maximum absolute half-step size. If absent or zero, defaults to
160160
!! `abs(xend - x)`.
@@ -191,18 +191,17 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
191191
real(wp), intent(inout) :: df(n,n), dfold(n,n)
192192
integer, intent(inout) :: ip(n)
193193
procedure(output_sub), optional :: solout
194-
integer, intent(out), optional :: stats(3)
194+
integer, intent(out), optional :: stats(6)
195195
real(wp), intent(in), optional :: hmax
196196

197-
integer :: icon, iha, i, j, nr, irtrn
198-
integer :: nfev, njev, nlu
197+
integer :: icon, iha, i, j, irtrn
198+
integer :: nfev, njev, nlu, nacc, nrej, nsol
199199
real(wp) :: x_current, xold, h, e, es, q, qa, hmax_used
200200
logical :: have_f
201201

202202
! icon = 0 except for last step which ends exactly at x1
203203
icon = 0
204204

205-
nr = 0
206205
x_current = x
207206
if (present(hmax)) then
208207
if (hmax < 0.0_wp) error stop 'stiff3: hmax must be a non-negative real value'
@@ -218,8 +217,21 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
218217
nfev = 0
219218
njev = 0
220219
nlu = 0
220+
nacc = 0
221+
nrej = 0
222+
nsol = 0
221223
have_f = .false.
222224

225+
if (present(solout)) then
226+
irtrn = 0
227+
call solout(1,x_current,x_current,y,0,0.0_wp,irtrn)
228+
if (irtrn < 0) then
229+
h0 = h
230+
if (present(stats)) stats = [nacc, nrej, nfev, njev, nlu, nsol]
231+
return
232+
end if
233+
end if
234+
223235
outer: do
224236

225237
! last step - or first step longer than interval
@@ -259,7 +271,7 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
259271

260272
! perform full integration step
261273

262-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,2*h,nfev,nlu)
274+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,2*h,nfev,nlu,nsol)
263275

264276
do i = 1, n
265277
ya(i) = y(i)
@@ -277,15 +289,15 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
277289
inner: do
278290
iha = iha + 1
279291

280-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu)
292+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu,nsol)
281293
call fun(n,y,f)
282294
nfev = nfev + 1
283295
call jac(n,y,df)
284296
njev = njev + 1
285297

286298
yold1 = y
287299

288-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu)
300+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu,nsol)
289301

290302
! half step integration finished
291303
! compute deviation and compare with tolerance
@@ -314,6 +326,7 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
314326

315327
h = h/2.0_wp
316328
icon = 0
329+
nrej = nrej + 1
317330

318331
end do inner
319332

@@ -338,13 +351,13 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
338351

339352
! perform output if appropriate
340353

341-
nr = nr + 1
354+
nacc = nacc + 1
342355
if (present(solout)) then
343356
irtrn = 0
344-
call solout(nr,xold,x_current,y,iha,qa,irtrn)
357+
call solout(nacc+1,xold,x_current,y,iha,qa,irtrn)
345358
if (irtrn < 0) then
346359
h0 = h
347-
if (present(stats)) stats = [nfev, njev, nlu]
360+
if (present(stats)) stats = [nacc, nrej, nfev, njev, nlu, nsol]
348361
return
349362
end if
350363
end if
@@ -353,7 +366,7 @@ subroutine stiff3_core(n,fun,x,y,xend,jac,h0,eps,w, &
353366

354367
if (icon == 1) then
355368
h0 = h
356-
if (present(stats)) stats = [nfev, njev, nlu]
369+
if (present(stats)) stats = [nacc, nrej, nfev, njev, nlu, nsol]
357370
return
358371
end if
359372

@@ -441,7 +454,7 @@ subroutine stiff3_interp_vector(xold,x,y,rwork,xeval,yeval)
441454

442455
!> Single-step semi-implicit integration
443456
!
444-
subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
457+
subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu,nsol)
445458
integer, intent(in) :: n
446459
!! Size of the system of ODEs
447460
procedure(rhs_sub) :: fun
@@ -464,6 +477,8 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
464477
!! Number of right-hand side evaluations
465478
integer, intent(inout) :: nlu
466479
!! Number of LU decompositions
480+
integer, intent(inout) :: nsol
481+
!! Number of linear-system solves (back substitutions)
467482

468483
integer :: i
469484

@@ -487,6 +502,7 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
487502
call lu(df,ipiv)
488503
nlu = nlu + 1
489504
call back(df,f,ipiv)
505+
nsol = nsol + 1
490506

491507
do i = 1, n
492508
yk1(i) = h*f(i)
@@ -495,6 +511,7 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
495511
call fun(n,yk2,f)
496512
nfev = nfev + 1
497513
call back(df,f,ipiv)
514+
nsol = nsol + 1
498515

499516
!
500517
! evaluate k2
@@ -510,6 +527,7 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
510527
! for convenience stored in yk2
511528
!
512529
call back(df,yk2,ipiv)
530+
nsol = nsol + 1
513531
do i = 1, n
514532
y(i) = y(i) + yk2(i)
515533
end do

test/ode_hmax.f90

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ program ode_hmax
1010
real(wp), parameter :: h0_start = 0.5_wp
1111
real(wp) :: y_default(n), y_zero(n), y_capped(n), w(n), x0, x1, h0, eps_test
1212
real(wp) :: max_h_seen
13-
integer :: stats_default(3), stats_zero(3), stats_capped(3)
13+
integer :: stats_default(6), stats_zero(6), stats_capped(6)
1414

1515
w = 1.0_wp
1616
x0 = 0.0_wp
@@ -26,8 +26,8 @@ program ode_hmax
2626
call stiff3(n,fun,x0,y_zero,x1,jac,h0,eps_test,w,stats=stats_zero,hmax=0.0_wp)
2727

2828
if (any(stats_zero /= stats_default)) then
29-
print '(A,3(I0,1X),A,3(I0,1X))', &
30-
'expected hmax=0 stats [nfev njev nlu] to match default. got ', stats_zero, &
29+
print '(A,6(I0,1X),A,6(I0,1X))', &
30+
'expected hmax=0 stats [nacc nrej nfev njev nlu nsol] to match default. got ', stats_zero, &
3131
' expected ', stats_default
3232
error stop 1
3333
end if

0 commit comments

Comments
 (0)