Skip to content

Commit a8f0c90

Browse files
Copilotivan-pi
andauthored
Add optional solver statistics output (nfev, njev, nlu) to stiff3 (#20)
* Add optional stiff3 statistics counters and test Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/5ea33038-53d1-472f-8bea-c63bb8576909 Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Refine stats test robustness and initialize optional stats Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/5ea33038-53d1-472f-8bea-c63bb8576909 Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Address validation feedback on stats implementation Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/5ea33038-53d1-472f-8bea-c63bb8576909 Co-authored-by: ivan-pi <21085643+ivan-pi@users.noreply.github.com> * Strengthen stats test with callback count verification Agent-Logs-Url: https://github.com/ivan-pi/stiff3/sessions/5ea33038-53d1-472f-8bea-c63bb8576909 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 08c6104 commit a8f0c90

4 files changed

Lines changed: 93 additions & 8 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This repository provides a refactored version with a simplified procedural inter
1010

1111
- Third-order Rosenbrock-type (semi-implicit Runge-Kutta) integrator suitable for stiff ODE systems
1212
- Adaptive stepsize control with error tolerance per component
13+
- Optional runtime statistics output: rhs evaluations (`nfev`), Jacobian evaluations (`njev`), and LU decompositions (`nlu`)
1314
- Requires an exact user-supplied Jacobian
1415
- Depends on BLAS and LAPACK for linear algebra operations
1516
- Supports two build systems: [CMake](https://cmake.org/) and [Fortran Package Manager (fpm)](https://github.com/fortran-lang/fpm)
@@ -77,7 +78,7 @@ program vanpol
7778
integer, parameter :: n = 2
7879
real(wp), parameter :: mu = 10.0_wp
7980
real(wp) :: y(n), w(n), x0, x1, h0, eps
80-
integer :: irtrn
81+
integer :: irtrn, stats(3)
8182
8283
! initial value
8384
y = [1.0_wp, 1.0_wp]
@@ -93,7 +94,8 @@ program vanpol
9394
irtrn = 0
9495
call out(0,x0,x0,y,0,0.0_wp,irtrn)
9596
! integrate system of ODEs
96-
call stiff3(n,fun,jac,x0,x1,h0,eps,w,y,solout=out)
97+
call stiff3(n,fun,jac,x0,x1,h0,eps,w,y,solout=out,stats=stats)
98+
print '(A,3(I0,1X))', 'nfev njev nlu: ', stats
9799
98100
contains
99101

src/stiff3_solver.f90

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ subroutine output_sub(nr,xold,x,y,iha,qa,irtrn)
7474

7575
!> Semi-implicit Runge-Kutta integrator routine
7676
!
77-
subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
77+
subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout,stats)
7878
integer, intent(in) :: n
7979
!! Number of equations to be integrated.
8080
procedure(rhs_sub) :: fun
@@ -95,6 +95,8 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
9595
!! dependent variables at `x1`.
9696
procedure(output_sub), optional :: solout
9797
!! User supplied subprogram for output.
98+
integer, intent(out), optional :: stats(3)
99+
!! Statistics array with `[nfev, njev, nlu]`.
98100

99101
real(wp), dimension(n) :: yk1, yk2, ya, yold, yold1, f, fold
100102
!! Workspace for solution vector and right-hand side
@@ -104,6 +106,7 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
104106
!! Workspace for the pivot array
105107

106108
integer :: icon, iha, i, j, nr, irtrn
109+
integer :: nfev, njev, nlu
107110
real(wp) :: x, xold, h, e, es, q, qa
108111

109112
! icon = 0 except for last step which ends exactly at x1
@@ -112,6 +115,9 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
112115
nr = 0
113116
x = x0
114117
h = h0
118+
nfev = 0
119+
njev = 0
120+
nlu = 0
115121

116122
outer: do
117123

@@ -131,7 +137,9 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
131137
! evaluate function and jacobian
132138

133139
call fun(n,y,f)
140+
nfev = nfev + 1
134141
call dfun(n,y,df)
142+
njev = njev + 1
135143

136144
! keep values which are used in half-step integration
137145

@@ -145,7 +153,7 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
145153

146154
! perform full integration step
147155

148-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,2*h)
156+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,2*h,nfev,nlu)
149157

150158
do i = 1, n
151159
ya(i) = y(i)
@@ -163,13 +171,15 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
163171
inner: do
164172
iha = iha + 1
165173

166-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h)
174+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu)
167175
call fun(n,y,f)
176+
nfev = nfev + 1
168177
call dfun(n,y,df)
178+
njev = njev + 1
169179

170180
yold1 = y
171181

172-
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h)
182+
call sirk3(n,fun,ip,f,y,yk1,yk2,df,h,nfev,nlu)
173183

174184
! half step integration finished
175185
! compute deviation and compare with tolerance
@@ -223,6 +233,7 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
223233
call solout(nr,xold,x,y,iha,qa,irtrn)
224234
if (irtrn < 0) then
225235
h0 = h
236+
if (present(stats)) stats = [nfev, njev, nlu]
226237
return
227238
end if
228239
end if
@@ -231,6 +242,7 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
231242

232243
if (icon == 1) then
233244
h0 = h
245+
if (present(stats)) stats = [nfev, njev, nlu]
234246
return
235247
end if
236248

@@ -241,7 +253,7 @@ subroutine stiff3(n,fun,dfun,x0,x1,h0,eps,w,y,solout)
241253

242254
!> Single-step semi-implicit integration
243255
!
244-
subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h)
256+
subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h,nfev,nlu)
245257
integer, intent(in) :: n
246258
!! Size of the system of ODEs
247259
procedure(rhs_sub) :: fun
@@ -260,6 +272,10 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h)
260272
!! On output contains the factorized matrix (I - h a J) = LU
261273
real(wp), intent(in) :: h
262274
!! Step size of the independent variable
275+
integer, intent(inout) :: nfev
276+
!! Number of right-hand side evaluations
277+
integer, intent(inout) :: nlu
278+
!! Number of LU decompositions
263279

264280
integer :: i
265281

@@ -281,13 +297,15 @@ subroutine sirk3(n,fun,ipiv,f,y,yk1,yk2,df,h)
281297
! perform triangular decomposition and evaluate k1
282298
!
283299
call lu(df,ipiv)
300+
nlu = nlu + 1
284301
call back(df,f,ipiv)
285302

286303
do i = 1, n
287304
yk1(i) = h*f(i)
288305
yk2(i) = y(i) + 0.75_wp * yk1(i)
289306
end do
290307
call fun(n,yk2,f)
308+
nfev = nfev + 1
291309
call back(df,f,ipiv)
292310

293311
!

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
foreach(test_program ode_linear_decay ode_order ode_logistic ode_stiff_linear ode_robertson_invariants)
1+
foreach(test_program ode_linear_decay ode_order ode_logistic ode_stiff_linear ode_robertson_invariants ode_stats)
22
add_executable(${test_program} ${test_program}.f90)
33
target_link_libraries(${test_program} PRIVATE stiff3)
44
add_test(NAME ${test_program} COMMAND ${test_program})

test/ode_stats.f90

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module ode_stats_problem
2+
3+
use stiff3_solver, only: wp => stiff3_wp
4+
5+
implicit none
6+
7+
real(wp), parameter :: decay_rate = 2.0_wp
8+
integer :: fun_calls = 0
9+
integer :: jac_calls = 0
10+
11+
contains
12+
13+
subroutine fun(n,y,f)
14+
integer, intent(in) :: n
15+
real(wp), intent(in) :: y(n)
16+
real(wp), intent(inout) :: f(n)
17+
fun_calls = fun_calls + 1
18+
f(1) = -decay_rate*y(1)
19+
end subroutine
20+
21+
subroutine jac(n,y,df)
22+
integer, intent(in) :: n
23+
real(wp), intent(in) :: y(n)
24+
real(wp), intent(inout) :: df(n,n)
25+
jac_calls = jac_calls + 1
26+
df(1,1) = -decay_rate
27+
end subroutine
28+
29+
end module
30+
31+
program ode_stats
32+
33+
use stiff3_solver, only: stiff3, wp => stiff3_wp
34+
use ode_stats_problem, only: fun, jac, fun_calls, jac_calls
35+
36+
implicit none
37+
38+
integer, parameter :: n = 1
39+
real(wp) :: y(n), w(n), x0, x1, h0, eps
40+
integer :: stats(3)
41+
42+
y = [1.0_wp]
43+
w = 1.0_wp
44+
x0 = 0.0_wp
45+
x1 = 1.0_wp
46+
h0 = 0.01_wp
47+
eps = 1.0e-10_wp
48+
fun_calls = 0
49+
jac_calls = 0
50+
51+
call stiff3(n,fun,jac,x0,x1,h0,eps,w,y,stats=stats)
52+
53+
if (stats(1) /= fun_calls .or. stats(2) /= jac_calls) then
54+
print '(A,3(I0,1X),A,2(I0,1X))', &
55+
'stats mismatch [nfev njev nlu]: ', stats, &
56+
' callback counts [fun jac]: ', fun_calls, jac_calls
57+
error stop 1
58+
end if
59+
60+
if (stats(3) <= 0) then
61+
print '(A,3(I0,1X))', 'expected positive nlu in stats [nfev njev nlu], got: ', stats
62+
error stop 1
63+
end if
64+
65+
end program

0 commit comments

Comments
 (0)