|
| 1 | +program oregonator |
| 2 | + |
| 3 | + use stiff3_solver, only: stiff3, wp => stiff3_wp |
| 4 | + |
| 5 | + implicit none |
| 6 | + |
| 7 | + integer, parameter :: n = 3 |
| 8 | + integer, parameter :: ncases = 6 |
| 9 | + real(wp), parameter :: y0(n) = [1.0_wp, 2.0_wp, 3.0_wp] |
| 10 | + real(wp), parameter :: yref(n) = [ & |
| 11 | + 1.000814870318523_wp, 1228.178521549917_wp, 132.0554942846706_wp ] |
| 12 | + real(wp), parameter :: x0 = 0.0_wp |
| 13 | + real(wp), parameter :: x1 = 360.0_wp |
| 14 | + real(wp), parameter :: h0_initial = 1.0e-3_wp |
| 15 | + real(wp), parameter :: verification_tol = 1.0e-8_wp |
| 16 | + real(wp), parameter :: eps_values(ncases) = [ & |
| 17 | + 1.0e-4_wp, 1.0e-5_wp, 1.0e-6_wp, 1.0e-7_wp, 1.0e-8_wp, 1.0e-9_wp ] |
| 18 | + character(len=*), parameter :: csv_file = 'oregonator_work_precision.csv' |
| 19 | + character(len=*), parameter :: svg_file = 'oregonator_work_precision.svg' |
| 20 | + |
| 21 | + integer :: i |
| 22 | + integer :: verification_stats(6) |
| 23 | + integer :: stats(6,ncases) |
| 24 | + real(wp) :: y(n), w(n), errors(ncases), work(ncases), verification_error |
| 25 | + |
| 26 | + w = 1.0_wp |
| 27 | + |
| 28 | + call integrate_case(1.0e-8_wp, y, verification_stats) |
| 29 | + verification_error = max_relative_error(y, yref) |
| 30 | + if (verification_error > verification_tol) then |
| 31 | + print '(A,ES12.4,A,ES12.4)', & |
| 32 | + 'reference verification failed: max relative error=', verification_error, & |
| 33 | + ' exceeds ', verification_tol |
| 34 | + error stop 1 |
| 35 | + end if |
| 36 | + |
| 37 | + print '(A,ES12.4)', 'verified reference solution with max relative error ', verification_error |
| 38 | + print '(A,3(1X,ES24.16))', 'y(360) =', y |
| 39 | + print '(A,6(I0,1X))', 'verification stats [nacc nrej nfev njev nlu nsol]: ', verification_stats |
| 40 | + |
| 41 | + do i = 1, ncases |
| 42 | + call integrate_case(eps_values(i), y, stats(:,i)) |
| 43 | + errors(i) = max_relative_error(y, yref) |
| 44 | + work(i) = real(stats(3,i), wp) |
| 45 | + end do |
| 46 | + |
| 47 | + call write_csv(csv_file, eps_values, errors, stats) |
| 48 | + call write_svg(svg_file, eps_values, errors, work) |
| 49 | + |
| 50 | + print '(A)', 'work-precision data:' |
| 51 | + print '(A)', ' eps error nfev njev nlu nsol' |
| 52 | + do i = 1, ncases |
| 53 | + print '(2(1X,ES10.2),4(1X,I6))', eps_values(i), errors(i), & |
| 54 | + stats(3,i), stats(4,i), stats(5,i), stats(6,i) |
| 55 | + end do |
| 56 | + print '(A,1X,A)', 'wrote', csv_file |
| 57 | + print '(A,1X,A)', 'wrote', svg_file |
| 58 | + |
| 59 | +contains |
| 60 | + |
| 61 | + subroutine integrate_case(eps, y, stats) |
| 62 | + real(wp), intent(in) :: eps |
| 63 | + real(wp), intent(out) :: y(n) |
| 64 | + integer, intent(out) :: stats(6) |
| 65 | + |
| 66 | + real(wp) :: h0 |
| 67 | + |
| 68 | + y = y0 |
| 69 | + h0 = h0_initial |
| 70 | + call stiff3(n, fun, x0, y, x1, jac, h0, eps, w, stats=stats) |
| 71 | + end subroutine |
| 72 | + |
| 73 | + function max_relative_error(y, y_reference) result(err) |
| 74 | + real(wp), intent(in) :: y(n), y_reference(n) |
| 75 | + real(wp) :: err |
| 76 | + |
| 77 | + err = maxval(abs(y - y_reference)/max(1.0_wp, abs(y_reference))) |
| 78 | + end function |
| 79 | + |
| 80 | + subroutine write_csv(filename, eps, err, stats) |
| 81 | + character(len=*), intent(in) :: filename |
| 82 | + real(wp), intent(in) :: eps(:), err(:) |
| 83 | + integer, intent(in) :: stats(:,:) |
| 84 | + |
| 85 | + integer :: i, ios, unit |
| 86 | + |
| 87 | + open(newunit=unit, file=filename, status='replace', action='write', iostat=ios) |
| 88 | + if (ios /= 0) error stop 'failed to open CSV output file' |
| 89 | + |
| 90 | + write(unit,'(A)') 'eps,max_relative_error,nfev,njev,nlu,nsol,nacc,nrej' |
| 91 | + do i = 1, size(eps) |
| 92 | + write(unit,'(ES24.16,'','',ES24.16,'','',I0,'','',I0,'','',I0,'','',I0,'','',I0,'','',I0)') & |
| 93 | + eps(i), err(i), stats(3,i), stats(4,i), stats(5,i), stats(6,i), stats(1,i), stats(2,i) |
| 94 | + end do |
| 95 | + |
| 96 | + close(unit) |
| 97 | + end subroutine |
| 98 | + |
| 99 | + subroutine write_svg(filename, eps, err, work) |
| 100 | + character(len=*), intent(in) :: filename |
| 101 | + real(wp), intent(in) :: eps(:), err(:), work(:) |
| 102 | + |
| 103 | + integer, parameter :: width = 800 |
| 104 | + integer, parameter :: height = 600 |
| 105 | + real(wp), parameter :: left = 100.0_wp |
| 106 | + real(wp), parameter :: right = 30.0_wp |
| 107 | + real(wp), parameter :: top = 50.0_wp |
| 108 | + real(wp), parameter :: bottom = 80.0_wp |
| 109 | + |
| 110 | + character(len=32) :: label |
| 111 | + integer :: i, ios, unit |
| 112 | + real(wp) :: log_work(size(work)), log_err(size(err)) |
| 113 | + real(wp) :: xmin, xmax, ymin, ymax, xpad, ypad |
| 114 | + real(wp) :: x1p, x2p, y1p, y2p, xtick, ytick, tick_value |
| 115 | + |
| 116 | + log_work = log10(max(work, tiny(1.0_wp))) |
| 117 | + log_err = log10(max(err, tiny(1.0_wp))) |
| 118 | + |
| 119 | + xmin = minval(log_work) |
| 120 | + xmax = maxval(log_work) |
| 121 | + ymin = minval(log_err) |
| 122 | + ymax = maxval(log_err) |
| 123 | + xpad = 0.05_wp*max(xmax - xmin, 1.0_wp) |
| 124 | + ypad = 0.08_wp*max(ymax - ymin, 1.0_wp) |
| 125 | + xmin = xmin - xpad |
| 126 | + xmax = xmax + xpad |
| 127 | + ymin = ymin - ypad |
| 128 | + ymax = ymax + ypad |
| 129 | + |
| 130 | + open(newunit=unit, file=filename, status='replace', action='write', iostat=ios) |
| 131 | + if (ios /= 0) error stop 'failed to open SVG output file' |
| 132 | + |
| 133 | + write(unit,'(A)') '<?xml version="1.0" encoding="UTF-8"?>' |
| 134 | + write(unit,'(A,I0,A,I0,A)') '<svg xmlns="http://www.w3.org/2000/svg" width="', width, & |
| 135 | + '" height="', height, '" viewBox="0 0 800 600">' |
| 136 | + write(unit,'(A)') '<rect width="100%" height="100%" fill="white"/>' |
| 137 | + write(unit,'(A)') & |
| 138 | + '<text x="400" y="28" font-family="sans-serif" font-size="22" ' // & |
| 139 | + 'text-anchor="middle">Oregonator work-precision diagram</text>' |
| 140 | + write(unit,'(A,F0.3,A,F0.3,A,F0.3,A,F0.3,A)') & |
| 141 | + '<rect x="', left, '" y="', top, '" width="', real(width,wp) - left - right, & |
| 142 | + '" height="', real(height,wp) - top - bottom, '" fill="none" stroke="black" stroke-width="1.5"/>' |
| 143 | + |
| 144 | + do i = 0, 4 |
| 145 | + xtick = left + real(i,wp)*(real(width,wp) - left - right)/4.0_wp |
| 146 | + tick_value = 10.0_wp**(xmin + real(i,wp)*(xmax - xmin)/4.0_wp) |
| 147 | + write(label,'(ES10.2)') tick_value |
| 148 | + write(unit,'(A,F0.3,A,F0.3,A)') '<line x1="', xtick, '" y1="520" x2="', xtick, & |
| 149 | + '" y2="526" stroke="#666"/>' |
| 150 | + write(unit,'(A,F0.3,A)') '<text x="', xtick, '" y="548" font-family="sans-serif" font-size="12" text-anchor="middle">' |
| 151 | + write(unit,'(A)') trim(adjustl(label))//'</text>' |
| 152 | + end do |
| 153 | + |
| 154 | + do i = 0, 4 |
| 155 | + ytick = real(height,wp) - bottom - real(i,wp)*(real(height,wp) - top - bottom)/4.0_wp |
| 156 | + tick_value = 10.0_wp**(ymin + real(i,wp)*(ymax - ymin)/4.0_wp) |
| 157 | + write(label,'(ES10.2)') tick_value |
| 158 | + write(unit,'(A,F0.3,A,F0.3,A)') '<line x1="94" y1="', ytick, '" x2="100" y2="', ytick, & |
| 159 | + '" stroke="#666"/>' |
| 160 | + write(unit,'(A,F0.3,A)') '<text x="84" y="', ytick + 4.0_wp, '" font-family="sans-serif" font-size="12" text-anchor="end">' |
| 161 | + write(unit,'(A)') trim(adjustl(label))//'</text>' |
| 162 | + end do |
| 163 | + |
| 164 | + write(unit,'(A)') & |
| 165 | + '<text x="435" y="585" font-family="sans-serif" font-size="16" ' // & |
| 166 | + 'text-anchor="middle">rhs evaluations (nfev)</text>' |
| 167 | + write(unit,'(A)') & |
| 168 | + '<text x="24" y="285" font-family="sans-serif" font-size="16" ' // & |
| 169 | + 'text-anchor="middle" transform="rotate(-90 24 285)">' // & |
| 170 | + 'max relative error</text>' |
| 171 | + |
| 172 | + do i = 1, size(work) - 1 |
| 173 | + x1p = xcoord(log_work(i), xmin, xmax, width, left, right) |
| 174 | + y1p = ycoord(log_err(i), ymin, ymax, height, top, bottom) |
| 175 | + x2p = xcoord(log_work(i + 1), xmin, xmax, width, left, right) |
| 176 | + y2p = ycoord(log_err(i + 1), ymin, ymax, height, top, bottom) |
| 177 | + write(unit,'(A,F0.3,A,F0.3,A,F0.3,A,F0.3,A)') '<line x1="', x1p, '" y1="', y1p, & |
| 178 | + '" x2="', x2p, '" y2="', y2p, '" stroke="#1f77b4" stroke-width="2"/>' |
| 179 | + end do |
| 180 | + |
| 181 | + do i = 1, size(work) |
| 182 | + x1p = xcoord(log_work(i), xmin, xmax, width, left, right) |
| 183 | + y1p = ycoord(log_err(i), ymin, ymax, height, top, bottom) |
| 184 | + write(label,'(ES9.1)') eps(i) |
| 185 | + write(unit,'(A,F0.3,A,F0.3,A)') '<circle cx="', x1p, '" cy="', y1p, '" r="4.5" fill="#d62728"/>' |
| 186 | + write(unit,'(A,F0.3,A,F0.3,A)') '<text x="', x1p + 8.0_wp, '" y="', y1p - 8.0_wp, & |
| 187 | + '" font-family="sans-serif" font-size="12">' |
| 188 | + write(unit,'(A)') 'eps='//trim(adjustl(label))//'</text>' |
| 189 | + end do |
| 190 | + |
| 191 | + write(unit,'(A)') '</svg>' |
| 192 | + |
| 193 | + close(unit) |
| 194 | + |
| 195 | + end subroutine |
| 196 | + |
| 197 | + function xcoord(value, xmin, xmax, width, left, right) result(xplot) |
| 198 | + real(wp), intent(in) :: value, xmin, xmax, left, right |
| 199 | + integer, intent(in) :: width |
| 200 | + real(wp) :: xplot |
| 201 | + |
| 202 | + xplot = left + (value - xmin)*(real(width,wp) - left - right)/(xmax - xmin) |
| 203 | + end function |
| 204 | + |
| 205 | + function ycoord(value, ymin, ymax, height, top, bottom) result(yplot) |
| 206 | + real(wp), intent(in) :: value, ymin, ymax, top, bottom |
| 207 | + integer, intent(in) :: height |
| 208 | + real(wp) :: yplot |
| 209 | + |
| 210 | + yplot = real(height,wp) - bottom - & |
| 211 | + (value - ymin)*(real(height,wp) - top - bottom)/(ymax - ymin) |
| 212 | + end function |
| 213 | + |
| 214 | + subroutine fun(n, y, f) |
| 215 | + integer, intent(in) :: n |
| 216 | + real(wp), intent(in) :: y(n) |
| 217 | + real(wp), intent(inout) :: f(n) |
| 218 | + |
| 219 | + f(1) = 77.27_wp*(y(2) + y(1)*(1.0_wp - 8.375e-6_wp*y(1) - y(2))) |
| 220 | + f(2) = (y(3) - (1.0_wp + y(1))*y(2))/77.27_wp |
| 221 | + f(3) = 0.161_wp*(y(1) - y(3)) |
| 222 | + end subroutine |
| 223 | + |
| 224 | + subroutine jac(n, y, df) |
| 225 | + integer, intent(in) :: n |
| 226 | + real(wp), intent(in) :: y(n) |
| 227 | + real(wp), intent(inout) :: df(n,n) |
| 228 | + |
| 229 | + df = 0.0_wp |
| 230 | + df(1,1) = 77.27_wp*(1.0_wp - 2.0_wp*8.375e-6_wp*y(1) - y(2)) |
| 231 | + df(1,2) = 77.27_wp*(1.0_wp - y(1)) |
| 232 | + df(2,1) = -y(2)/77.27_wp |
| 233 | + df(2,2) = -(1.0_wp + y(1))/77.27_wp |
| 234 | + df(2,3) = 1.0_wp/77.27_wp |
| 235 | + df(3,1) = 0.161_wp |
| 236 | + df(3,3) = -0.161_wp |
| 237 | + end subroutine |
| 238 | + |
| 239 | +end program |
0 commit comments