MRKISS 2025-09-10
A tiny library with zero dependencies that aims to make it easy to use & experiment with explicit Runge-Kutta methods.
Loading...
Searching...
No Matches
mrkiss_utils.f90
Go to the documentation of this file.
1! -*- Mode:F90; Coding:us-ascii-unix; fill-column:129 -*-
2!.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.S.!!
3!>
4!! @file mrkiss_utils.f90
5!! @author Mitch Richling http://www.mitchr.me/
6!! @brief Utilities for MRKISS.@EOL
7!! @std F2023
8!! @see https://github.com/richmit/MRKISS/
9!! @copyright
10!! @parblock
11!! Copyright (c) 2025, Mitchell Jay Richling <http://www.mitchr.me/> All rights reserved.
12!!
13!! Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
14!! conditions are met:
15!!
16!! 1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following
17!! disclaimer.
18!!
19!! 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following
20!! disclaimer in the documentation and/or other materials provided with the distribution.
21!!
22!! 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
23!! derived from this software without specific prior written permission.
24!!
25!! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26!! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27!! DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28!! EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29!! USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30!! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31!! OF THE POSSIBILITY OF SUCH DAMAGE.
32!! @endparblock
33!.!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.H.E.!!
34
35!----------------------------------------------------------------------------------------------------------------------------------
36!> Utilities.
37!!
39 implicit none
40 private
41
43 public :: seq
45
46contains
47
48 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
49 !> @name I/O
50
51 !--------------------------------------------------------------------------------------------------------------------------------
52 !> Output an RK solution matrix.
53 !!
54 !! Examples
55 !! - 1: The default values will produce CSV output @n
56 !! `print_solution(solution)`
57 !! - 2: For columnar output we just need to add a width @n
58 !! `print_solution(solution, fmt_w_o=30)`
59 !!
60 !! @param status Exit status
61 !! | Value | Description
62 !! |-----------|------------
63 !! | -inf-0 | Everything worked
64 !! | 1152 | Could not open file for write
65 !! | 1153 | Could not close file
66 !! | 1154-1183 | Unknown error in this routine
67 !! @param solution Array containing the solution.
68 !! Each COLUMN is a solution:
69 !! - First element is the @f$t@f$ variable
70 !! - `size(y, 1)` elements starting with 2 have @f$\mathbf{y}@f$ values
71 !! - The next `size(y, 1)` elements have @f$\mathbf{y}'@f$ values
72 !! @param filename_o Filename to which we print. Default: NONE
73 !! - If not present, then output will be to output_unit (`STDOUT`).
74 !! @param fmt_d_o Number of digits for floating point numbers. Default: mrkiss_config::fmt_d_ai
75 !! @param fmt_w_o Width of print format for all entities. Default: 0
76 !! - If set to -1, then mrkiss_config::fmt_w_ai will be uesd.
77 !! @param fmt_e_o Number of digits in the exponent of floating point numbers. Default: mrkiss_config::fmt_e_ai
78 !! - Ignored if `fmt_w_o==0`
79 !! @param start_o Starting index to print in \p solution. Default: 1
80 !! @param end_o Ending index to print in \p solution. Default: Number of columns in \p solution.
81 !! @param step_o Print only every \p step_o th value in solution. Default: 1
82 !! @param prt_titles_o Print titles if `.TRUE.` Default: `.not. append_o`
83 !! @param separator_o String to place between fields. Default: ',' if fmt_w_o missing, and ' ' otherwise.
84 !! @param t_min_o Print only solutions with time values `>= t_min_o`
85 !! @param t_max_o Print only solutions with time values `<= t_min_o`
86 !! @param tag_o If non-negative, this integer that will become the first column of the output. Default: -1
87 !! @param append_o Append to file instead of overwriting. Default: `.FALSE.`
88 !!
89 subroutine print_solution(status, solution, filename_o, separator_o, fmt_w_o, fmt_d_o, fmt_e_o, start_o, end_o, step_o, &
90 & prt_titles_o, t_min_o, t_max_o, tag_o, append_o)
91 use, intrinsic :: iso_fortran_env, only: output_unit
93 implicit none
94 ! Arguments
95 integer, intent(out) :: status
96 real(kind=rk), intent(in) :: solution(:,:)
97 character(len=*), intent(in), optional :: filename_o, separator_o
98 integer, intent(in), optional :: fmt_e_o, fmt_d_o, fmt_w_o, start_o, end_o, step_o, tag_o
99 real(kind=rk), intent(in), optional :: t_min_o, t_max_o
100 logical, intent(in), optional :: prt_titles_o, append_o
101 ! Local variables
102 integer :: fmt_e, fmt_d, fmt_w, start_idx, end_idx, step, y_dim, sol_y_idx, tag
103 logical :: prt_titles, append
104 integer :: i, num_int, num_real, out_io_stat, out_io_unit
105 character(len=:), allocatable :: fmt, separator, access_mode, fmt_r_str, fmt_i_str, fmt_a_str
106 character(len=512) :: tmp_str
107 ! Process arguments
108 append = .false.
109 if (present(append_o)) append = append_o
110 prt_titles = .not. append
111 if (present(prt_titles_o)) prt_titles = prt_titles_o
112 tag = -1
113 if (present(tag_o)) tag = tag_o
114 step = 1
115 if (present(step_o)) step = step_o
116 fmt_d = fmt_d_ai
117 if (present(fmt_d_o)) fmt_d = fmt_d_o
118 fmt_e = fmt_e_ai
119 if (present(fmt_e_o)) fmt_e = fmt_e_o
120 fmt_w = 0
121 if (present(fmt_w_o)) fmt_w = fmt_w_o
122 if (fmt_w == -1) fmt_w = fmt_w_ai
123 start_idx = 1
124 if (present(start_o)) start_idx = start_o
125 end_idx = size(solution, 2)
126 if (present(end_o)) end_idx = min(end_o, size(solution, 2))
127 if (present(separator_o)) then
128 separator = separator_o
129 else
130 if (fmt_w == 0) then
131 separator = ","
132 else
133 separator = " "
134 end if
135 end if
136 ! Compute solution meta-data
137 y_dim = (size(solution, 1) - 1) / 2
138 sol_y_idx = 2
139 ! Create format string components
140 if (fmt_w == 0) then
141 ! use f edit descriptor. Ignore fmt_e.
142 write(tmp_str, '("f",i0,".",i0)') fmt_w, fmt_d
143 fmt_r_str = trim(tmp_str)
144 else
145 ! use e edit descriptor
146 write(tmp_str, '("e",i0,".",i0)') fmt_w, fmt_d
147 fmt_r_str = trim(tmp_str)
148 if (fmt_e > 0) then
149 write(tmp_str, '("e",i0)') fmt_e
150 fmt_r_str = fmt_r_str // trim(tmp_str)
151 end if
152 end if
153 write(tmp_str, '("i",i0)') fmt_w
154 fmt_i_str = trim(tmp_str)
155 if (fmt_w == 0) then
156 fmt_a_str = "a"
157 else
158 write(tmp_str, '("a",i0)') fmt_w
159 fmt_a_str = trim(tmp_str)
160 end if
161 ! Figure out how many items per line
162 num_real = size(solution, 1)
163 num_int = 0
164 if (tag<0) then
165 num_int = 1
166 else
167 num_int = 2
168 end if
169 ! Open file
170 if (append) then
171 access_mode = 'APPEND'
172 else
173 access_mode = 'SEQUENTIAL'
174 end if
175 if (present(filename_o)) then
176 open(newunit=out_io_unit, file=filename_o, form='formatted', action='write', access=access_mode, iostat=out_io_stat)
177 if (out_io_stat /= 0) then
178 status = 1152
179 return
180 end if
181 else
182 out_io_unit = output_unit
183 end if
184 ! Print titles
185 if (prt_titles) then
186 fmt = "(" // fmt_a_str // ")"
187 if (tag>=0) then
188 write(out_io_unit, fmt=fmt, advance="no") "tag"
189 write(out_io_unit, fmt='(a)', advance="no") separator
190 end if
191 write(out_io_unit, fmt=fmt, advance="no") "i"
192 write(out_io_unit, fmt='(a)', advance="no") separator
193 write(out_io_unit, fmt=fmt, advance="no") "t"
194 do i=1,y_dim
195 write(out_io_unit, fmt='(a)', advance="no") separator
196 write(tmp_str, fmt='("y",i0)') i
197 write(out_io_unit, fmt=fmt, advance="no") trim(tmp_str)
198 end do
199 do i=1,y_dim
200 write(out_io_unit, fmt='(a)', advance="no") separator
201 write(tmp_str, fmt='("dy",i0)') i
202 write(out_io_unit, fmt=fmt, advance="no") trim(tmp_str)
203 end do
204 write(out_io_unit, fmt='()')
205 end if
206 fmt = '('
207 if (tag>=0) fmt = fmt // fmt_i_str // ',"' // separator // '",'
208 fmt = fmt // fmt_i_str
209 fmt = fmt // repeat(',"' // separator // '",' // fmt_r_str, num_real)
210 fmt = fmt // ')'
211 do i=start_idx,end_idx,step
212 if (present(t_min_o)) then
213 if (solution(1, i) < t_min_o) cycle
214 end if
215 if (present(t_max_o)) then
216 if (solution(1, i) > t_max_o) cycle
217 end if
218 if (tag<0) then
219 write (out_io_unit, fmt=fmt) i, solution(1, i), solution(sol_y_idx:(sol_y_idx+2*y_dim-1), i)
220 else
221 write (out_io_unit, fmt=fmt) tag, i, solution(1, i), solution(sol_y_idx:(sol_y_idx+2*y_dim-1), i)
222 end if
223 end do
224 ! Close file
225 if (present(filename_o)) then
226 close(unit=out_io_unit, status='keep', iostat=out_io_stat)
227 if (out_io_stat /= 0) then
228 status = 1153
229 return
230 end if
231 end if
232 status = 0
233 end subroutine print_solution
234
235 !--------------------------------------------------------------------------------------------------------------------------------
236 !> Output an istat array.
237 !!
238 !! @param status Exit status
239 !! | Value | Description
240 !! |-----------|------------
241 !! | -inf-0 | Everything worked
242 !! | 1365 | Could not open file for write
243 !! | 1366 | Could not close file
244 !! | 1367-1381 | Unknown error in this routine
245 !! @param istats(:) Integer statistics from a solver run. See mrkiss_utils::print_istats() for description of elements.
246 !! @param idxs_to_prt_o(:) Indexes of \p istats to print. Indexes too large are silently ignored.
247 !! @param filename_o Filename to which we print. Default: NONE
248 !! - If not present, then output will be to output_unit (`STDOUT`).
249 !! @param fmt_w_o Width of print format for all entities. Default: mrkiss_config::fmt_w_ai
250 !! @param prt_zeros_o If `.TRUE.`, then print zero values. Default: `.FALSE.`
251 !!
252 subroutine print_istats(status, istats, idxs_to_prt_o, filename_o, fmt_w_o, prt_zeros_o)
253 use, intrinsic :: iso_fortran_env, only: output_unit
255 implicit none
256 ! Arguments
257 integer, intent(out) :: status
258 integer, intent(in) :: istats(istats_size)
259 integer, intent(in), optional :: idxs_to_prt_o(:), fmt_w_o
260 logical, intent(in), optional :: prt_zeros_o
261 character(len=*), intent(in), optional :: filename_o
262 ! Local variables
263 integer, allocatable :: idxs_to_prt(:)
264 integer :: out_io_stat, out_io_unit, i, fmt_w
265 logical :: prt_zeros
266 character(len=32) :: tmp_str1, tmp_str2
267 ! Process arguments
268 prt_zeros = .false.
269 if (present(prt_zeros_o)) prt_zeros = prt_zeros_o
270 idxs_to_prt = [(i, i=1,istats_max_idx)]
271 if (present(idxs_to_prt_o)) idxs_to_prt = idxs_to_prt_o
272 fmt_w = fmt_w_ai
273 if (present(fmt_w_o)) fmt_w = fmt_w_o
274 ! Open file
275 if (present(filename_o)) then
276 open(newunit=out_io_unit, file=filename_o, form='formatted', action='write', iostat=out_io_stat)
277 if (out_io_stat /= 0) then
278 status = 1365
279 return
280 end if
281 else
282 out_io_unit = output_unit
283 end if
284 ! Print
285 write(out_io_unit, fmt='(a25)') "ISTATS Contents"
286 write(tmp_str1, '("(a",i0,",i",i0,")")') istats_str_lng + 7 + 2 + 3 + 2 + 1, fmt_w
287 do i=1,size(idxs_to_prt)
288 if (i <= istats_max_idx) then
289 if ((istats(idxs_to_prt(i)) > 0) .or. prt_zeros) then
290 write(tmp_str2, '(i2.2)') i
291 write (out_io_unit, fmt=tmp_str1) (trim(istats_strs(idxs_to_prt(i))) // ": istats(" // trim(tmp_str2) // "): "), istats(idxs_to_prt(i))
292 end if
293 end if
294 end do
295 ! Close file
296 if (present(filename_o)) then
297 close(unit=out_io_unit, status='keep', iostat=out_io_stat)
298 if (out_io_stat /= 0) then
299 status = 1366
300 return
301 end if
302 end if
303 status = 0
304 end subroutine print_istats
305
306 !--------------------------------------------------------------------------------------------------------------------------------
307 !> Analyze an RK solution matrix.
308 !!
309 !! @param status Exit status
310 !! | Value | Description
311 !! |-----------|------------
312 !! | -inf-0 | Everything worked
313 !! | 1297 | Could not open file for write
314 !! | 1298 | Could not close file
315 !! | 1299-1313 | Unknown error in this routine
316 !! @param solution Array containing the solution.
317 !! Each COLUMN is a solution:
318 !! - First element is the @f$t@f$ variable
319 !! - `size(y, 1)` elements starting with 2 have @f$\mathbf{y}@f$ values
320 !! - The next `size(y, 1)` elements have @f$\mathbf{y}'@f$ values
321 !! @param start_o Starting index to use in \p solution. Default: 1
322 !! @param end_o Ending index to use in \p solution. Default: Number of columns in \p solution.
323 !! @param y_delta_len_idxs_o Components of @f$\mathbf{\Delta{y}}@f$ to use for length computation
324 !! @param filename_o Filename to which we print. Default: NONE
325 !! - If not present, then output will be to output_unit (`STDOUT`).
326 !! @param fmt_d_o Number of digits for floating point numbers. Default: mrkiss_config::fmt_d_ai
327 !! @param fmt_w_o Width of print format for all entities. Default: mrkiss_config::fmt_w_ai
328 !! @param fmt_e_o Number of digits in the exponent of floating point numbers. Default: mrkiss_config::fmt_e_ai
329 !!
330 subroutine analyze_solution(status, solution, filename_o, start_o, end_o, y_delta_len_idxs_o, fmt_w_o, fmt_d_o, fmt_e_o)
331 use, intrinsic :: iso_fortran_env, only: output_unit
332 use :: mrkiss_config, only: rk, fmt_d_ai, fmt_w_ai, fmt_e_ai
333 implicit none
334 ! Arguments
335 integer, intent(out) :: status
336 real(kind=rk), intent(in) :: solution(:,:)
337 character(len=*), intent(in), optional :: filename_o
338 integer, intent(in), optional :: y_delta_len_idxs_o(:), start_o, end_o, fmt_e_o, fmt_d_o, fmt_w_o
339 ! Local variables
340 integer :: y_dim, end_idx, start_idx, fmt_e, fmt_d, fmt_w, i, out_io_stat, out_io_unit
341 real(kind=rk) :: t_max, t_min, t_delta_max, t_delta_min, y_delta_len_max, y_delta_len_min
342 real(kind=rk), allocatable :: dy_max(:), dy_min(:), y_max(:), y_min(:), y_delta_max(:), y_delta_min(:)
343 character(len=32) :: tmp_str
344 character(len=:), allocatable :: fmt, fmt_r_str, fmt_i_str
345 ! Process arguments
346 fmt_d = fmt_d_ai
347 if (present(fmt_d_o)) fmt_d = fmt_d_o
348 fmt_e = fmt_e_ai
349 if (present(fmt_e_o)) fmt_e = fmt_e_o
350 fmt_w = fmt_w_ai
351 if (present(fmt_w_o)) fmt_w = fmt_w_o
352 start_idx = 1
353 if (present(start_o)) start_idx = start_o
354 end_idx = size(solution, 2)
355 if (present(end_o)) end_idx = min(end_o, size(solution, 2))
356 ! Create format string components
357 if (fmt_w == 0) then
358 ! use f edit descriptor. Ignore fmt_e.
359 write(tmp_str, '("f",i0,".",i0)') fmt_w, fmt_d
360 fmt_r_str = trim(tmp_str)
361 else
362 ! use e edit descriptor
363 write(tmp_str, '("e",i0,".",i0)') fmt_w, fmt_d
364 fmt_r_str = trim(tmp_str)
365 if (fmt_e > 0) then
366 write(tmp_str, '("e",i0)') fmt_e
367 fmt_r_str = fmt_r_str // trim(tmp_str)
368 end if
369 end if
370 write(tmp_str, '("i",i0)') fmt_w
371 fmt_i_str = trim(tmp_str)
372 ! Compute
373 y_dim = (size(solution, 1) - 1) / 2
374 if(end_idx - start_idx > 1) then
375 t_delta_max = abs(solution(1, start_idx+1) - solution(1, start_idx))
376 t_delta_min = t_delta_max
377 if (present(y_delta_len_idxs_o)) then
378 y_delta_len_max = norm2(solution(y_delta_len_idxs_o + 2 - 1, start_idx+1) - solution(y_delta_len_idxs_o + 2 - 1, start_idx))
379 else
380 y_delta_len_max = norm2(solution(2:(2+y_dim-1), start_idx+1) - solution(2:(2+y_dim-1), start_idx))
381 end if
382 y_delta_len_min = y_delta_len_max
383 y_delta_max = abs(solution(2:(2+y_dim-1), start_idx+1) - solution(2:(2+y_dim-1), start_idx))
384 y_delta_min = y_delta_max
385 end if
386 y_max = solution(2:(2+y_dim-1), start_idx)
387 y_min = y_max
388 dy_max = solution((2+y_dim):(2+2*y_dim-1), start_idx)
389 dy_min = dy_max
390 t_max = solution(1, start_idx)
391 t_min = t_max
392 do i=(start_idx+1),end_idx
393 y_max = max(y_max, solution(2:(2+y_dim-1), i))
394 y_min = min(y_min, solution(2:(2+y_dim-1), i))
395 dy_max = max(dy_max, solution((2+y_dim):(2+2*y_dim-1), i))
396 dy_min = min(dy_min, solution((2+y_dim):(2+2*y_dim-1), i))
397 t_max = max(t_max, solution(1, i))
398 t_min = min(t_min, solution(1, i))
399 t_delta_max = max(t_delta_max, abs(solution(1, i) - solution(1, i-1)))
400 t_delta_min = min(t_delta_min, abs(solution(1, i) - solution(1, i-1)))
401 if (present(y_delta_len_idxs_o)) then
402 y_delta_len_max = max(y_delta_len_max, norm2(solution(y_delta_len_idxs_o + 2 - 1, i) - solution(y_delta_len_idxs_o + 2 - 1, i-1)))
403 y_delta_len_min = min(y_delta_len_min, norm2(solution(y_delta_len_idxs_o + 2 - 1, i) - solution(y_delta_len_idxs_o + 2 - 1, i-1)))
404 else
405 y_delta_len_max = max(y_delta_len_max, norm2(solution(2:(2+y_dim-1), i) - solution(2:(2+y_dim-1), i-1)))
406 y_delta_len_min = min(y_delta_len_min, norm2(solution(2:(2+y_dim-1), i) - solution(2:(2+y_dim-1), i-1)))
407 end if
408 y_delta_max = max(y_delta_max, abs(solution(2:(2+y_dim-1), i) - solution(2:(2+y_dim-1), i-1)))
409 y_delta_min = min(y_delta_min, abs(solution(2:(2+y_dim-1), i) - solution(2:(2+y_dim-1), i-1)))
410 end do
411 ! Open file
412 if (present(filename_o)) then
413 open(newunit=out_io_unit, file=filename_o, form='formatted', action='write', iostat=out_io_stat)
414 if (out_io_stat /= 0) then
415 status = 1297
416 return
417 end if
418 else
419 out_io_unit = output_unit
420 end if
421 ! Print report
422 write(out_io_unit, fmt='(a25)') "Solution Analysis "
423 fmt = '(a25,' // fmt_i_str // ')'
424 write(out_io_unit, fmt=fmt) " size 1: ", size(solution, 1)
425 write(out_io_unit, fmt=fmt) " size 2: ", size(solution, 2)
426 fmt = '(a25,*(' // fmt_r_str // '))'
427 write(out_io_unit, fmt=fmt) " Max y: ", y_max
428 write(out_io_unit, fmt=fmt) " Min y: ", y_min
429 write(out_io_unit, fmt=fmt) " Max dy: ", dy_max
430 write(out_io_unit, fmt=fmt) " Min dy: ", dy_min
431 write(out_io_unit, fmt=fmt) " Max t: ", t_max
432 write(out_io_unit, fmt=fmt) " Min t: ", t_min
433 write(out_io_unit, fmt=fmt) " Max delta t: ", t_delta_max
434 write(out_io_unit, fmt=fmt) " Min delta t: ", t_delta_min
435 write(out_io_unit, fmt=fmt) " Max delta y: ", y_delta_max
436 write(out_io_unit, fmt=fmt) " Min delta y: ", y_delta_min
437 write(out_io_unit, fmt=fmt) " Max delta y len: ", y_delta_len_max
438 write(out_io_unit, fmt=fmt) " Min delta y len: ", y_delta_len_min
439 ! Close file
440 if (present(filename_o)) then
441 close(unit=out_io_unit, status='keep', iostat=out_io_stat)
442 if (out_io_stat /= 0) then
443 status = 1298
444 return
445 end if
446 end if
447 status = 0
448 end subroutine analyze_solution
449
450 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
451 !> @name Miscellaneous
452
453 !--------------------------------------------------------------------------------------------------------------------------------
454 !> Produce a sequence of values with fixed seporation. Modeled after R's seq() function.
455 !!
456 !! Exactly two of @p from_o, @p to_o, and @p step_o should be provided. The one not provided will be computed from the others.
457 !! If all three are are provided, then they will be checked for consistency. Note that this consistency check is not
458 !! completely reliable due to round-off error. This is why only two should be provided.
459 !!
460 !! @param status Exit status
461 !! | Value | Description
462 !! |-----------|------------
463 !! | -inf-0 | Everything worked
464 !! | 1314 | Inconsistant sequence values: `step_v_o * (size(t)-1) /= to_v - from_v`
465 !! | 1315 | Not enough arguments
466 !! | 1316-1330 | Unknown error in this routine
467 !! @param t Vector to fill with @f$t@f$ values
468 !! @param from_o Starting value for @f$t@f$
469 !! @param to_o Ending value for @f$t@f$
470 !! @param step_o Delta between valeus
471 !! @param max_pts_o Maximum number of points to produce. Default: `size(t, 1)`
472 !! If max_pts_o < size(t, 1), then elements beyond max_pts_o will be untouched.
473 !!
474 subroutine seq(status, t, from_o, to_o, step_o, max_pts_o)
475 use :: mrkiss_config, only: rk, zero_epsilon
476 implicit none
477 ! Arguments
478 integer, intent(out) :: status
479 real(kind=rk), intent(out) :: t(:)
480 real(kind=rk), optional, intent(in) :: from_o, to_o, step_o
481 integer, optional, intent(in) :: max_pts_o
482 ! Variables
483 integer :: n_v, i, max_pts, num_args
484 real(kind=rk) :: from_v, to_v, step_v
485 ! Process arguments
486 max_pts = size(t, 1)
487 if (present(max_pts_o)) max_pts = min(max_pts, max_pts_o)
488 num_args = 0
489 if (present(from_o)) num_args = num_args + 1
490 if (present(to_o)) num_args = num_args + 1
491 if (present(step_o)) num_args = num_args + 1
492 if (num_args < 2) then
493 status = 1315
494 return
495 end if
496 ! Compute paramaters
497 if (.not. (present(from_o))) then
498 status = 0
499 n_v = max_pts - 1
500 to_v = to_o
501 step_v = step_o
502 from_v = -step_v * n_v + to_v
503 elseif (.not. (present(to_o))) then
504 n_v = max_pts - 1
505 step_v = step_o
506 from_v = from_o
507 to_v = step_v * n_v + from_v
508 elseif (.not. (present(step_o))) then
509 status = 0
510 n_v = max_pts - 1
511 to_v = to_o
512 from_v = from_o
513 step_v = -(-to_v + from_v) / n_v
514 else
515 n_v = max_pts - 1
516 to_v = to_o
517 from_v = from_o
518 step_v = step_o
519 if (abs((step_v * n_v) - (to_v - from_v)) > zero_epsilon) then
520 status = 1314
521 else
522 status = 0
523 end if
524 end if
525 ! Compute seqeunce
526 if (status == 0) then
527 do i=0, n_v
528 t(i+1) = from_v+i*step_v
529 end do
530 end if
531 end subroutine seq
532
533 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
534 !> @name Status Codes
535
536 !--------------------------------------------------------------------------------------------------------------------------------
537 !> Return, as a string, the source of a status code.
538 !!
539 !! Status codes are assigned in blocks to subroutines and interfaces. Status codes are frequently "passed up" the call chain.
540 !! i.e. a routine may return a status code that was returned to it by another routine it called. Assigning the codes in blocks
541 !! allows the user to know from which subroutine a status code originated.
542 !!
543 !! Assigned Status Ranges
544 !! | Range | Assignment
545 !! |-----------|-----------
546 !! | 0001-0255 | mrkiss_solvers_wt::deq_iface & mrkiss_solvers_nt::deq_iface
547 !! | 0256-0511 | mrkiss_solvers_wt::stepp_iface & mrkiss_solvers_nt::stepp_iface
548 !! | 0512-0767 | mrkiss_solvers_wt::sdf_iface & mrkiss_solvers_nt::sdf_iface
549 !! | 1232-1247 | mrkiss_solvers_wt::step_all & mrkiss_solvers_nt::step_all
550 !! | 1248-1263 | mrkiss_solvers_wt::step_one & mrkiss_solvers_nt::step_one
551 !! | 1216-1231 | mrkiss_solvers_wt::step_richardson & mrkiss_solvers_nt::step_richardson
552 !! | 1200-1215 | mrkiss_solvers_wt::step_rk4 & mrkiss_solvers_nt::step_rk4
553 !! | 1184-1199 | mrkiss_solvers_wt::step_rkf45 & mrkiss_solvers_nt::step_rkf45
554 !! | 1263-1279 | mrkiss_solvers_wt::step_dp54 & mrkiss_solvers_nt::step_dp54
555 !! | 1120-1151 | mrkiss_solvers_wt::fixed_t_steps & mrkiss_solvers_nt::fixed_t_steps
556 !! | 1024-1055 | mrkiss_solvers_wt::fixed_y_steps & mrkiss_solvers_nt::fixed_y_steps
557 !! | 1280-1296 | mrkiss_solvers_wt::sloppy_fixed_y_steps & mrkiss_solvers_nt::sloppy_fixed_y_steps
558 !! | 1056-1119 | mrkiss_solvers_wt::adaptive_steps & mrkiss_solvers_nt::adaptive_steps
559 !! | 1152-1183 | mrkiss_utils::print_solution
560 !! | 1297-1313 | mrkiss_utils::analyze_solution
561 !! | 1314:1330 | mrkiss_utils::seq
562 !! | 1331:1347 | mrkiss_solvers_wt::interpolate_solution & mrkiss_solvers_nt::interpolate_solution
563 !! | 1348-1364 | mrkiss_solvers_wt::fixed_t_steps_between & mrkiss_solvers_nt::fixed_t_steps_between
564 !! | 1365-1381 | mrkiss_utils::print_istats
565 !! | 1382-1398 | Unallocated
566 !! | 1399-1415 | Unallocated
567 !!
568 !! I use the following bit of Emacs lisp code to generate new blocks:
569 !! @verbatim
570 !! (let ((s "\n"))
571 !! (cl-loop for f from 1348 to 2000 by 17
572 !! do (print f)
573 !! do (setq s (concat s (format "%d-%d\n" f (+ 16 f)))))
574 !! s)
575 !! @endverbatim
576 !!
577 !! @param status This is an `intent(in)` argument!!!!
578 !! @return A string `(len=32)` identifying the origin of the status code.
579 !! - A subroutine or interface name
580 !! - "NO ERROR" for a non-error status of unknown origin
581 !! - "UNKNOWN SOURCE" for an error status of unknown origin
582 !!
583 character(len=32) function status_to_origin(status)
584 implicit none
585 ! Arguments
586 integer, intent(in) :: status
587 ! Process Input
588 if (status <= 0) then
589 status_to_origin = "NO ERROR"
590 elseif ((status >= 1) .and. (status <= 255)) then
591 status_to_origin = "deq_iface_*t"
592 elseif ((status >= 256) .and. (status <= 511)) then
593 status_to_origin = "stepp_iface_*t"
594 elseif ((status >= 512) .and. (status <= 767)) then
595 status_to_origin = "sdf_iface_*t"
596 elseif ((status >= 1232) .and. (status <= 1247)) then
597 status_to_origin = "step_all_*t"
598 elseif ((status >= 1248) .and. (status <= 1263)) then
599 status_to_origin = "step_one_*t"
600 elseif ((status >= 1216) .and. (status <= 1231)) then
601 status_to_origin = "step_richardson_*t"
602 elseif ((status >= 1200) .and. (status <= 1215)) then
603 status_to_origin = "step_rk4_*t"
604 elseif ((status >= 1184) .and. (status <= 1199)) then
605 status_to_origin = "step_rkf45_*t"
606 elseif ((status >= 1263) .and. (status <= 1279)) then
607 status_to_origin = "step_dp54_*t"
608 elseif ((status >= 1120) .and. (status <= 1151)) then
609 status_to_origin = "fixed_t_steps_*t"
610 elseif ((status >= 1024) .and. (status <= 1055)) then
611 status_to_origin = "fixed_y_steps_*t"
612 elseif ((status >= 1280) .and. (status <= 1296)) then
613 status_to_origin = "sloppy_fixed_y_steps_*t"
614 elseif ((status >= 1056) .and. (status <= 1119)) then
615 status_to_origin = "adaptive_steps_*t"
616 elseif ((status >= 1152) .and. (status <= 1183)) then
617 status_to_origin = "print_solution"
618 elseif ((status >= 1297) .and. (status <= 1313)) then
619 status_to_origin = "analyze_solution"
620 elseif ((status >= 1314) .and. (status <= 1330)) then
621 status_to_origin = "seq"
622 elseif ((status >= 1331) .and. (status <= 1347)) then
623 status_to_origin = "interpolate_solution"
624 elseif ((status >= 1348) .and. (status <= 1364)) then
625 status_to_origin = "fixed_t_steps_between_*t"
626 elseif ((status >= 1365) .and. (status <= 1381)) then
627 status_to_origin = "print_istats"
628 else
629 status_to_origin = "UNKNOWN SOURCE"
630 end if
631 end function status_to_origin
632
633 !--------------------------------------------------------------------------------------------------------------------------------
634 !> Return, as a string, the source of a status code.
635 !!
636 !! @param status This is an intent(in) argument!!!!
637 !! @return A string `(len=128)` identifying the message of the status code.
638 !! - Format: "SOURCE: MESSAGE"
639 !! - SOURCE will be the subroutine or interface if known, and "UNKNOWN SOURCE" otherwise
640 !! - MESSAGE will be the error message if known, and "UNKNOWN ERROR" otherwise
641 !! - "NO ERROR" for a non-error status of unknown message
642 !!
643 character(len=128) function status_to_message(status)
644 implicit none
645 ! Arguments
646 integer, intent(in) :: status
647 ! Process Input
648 if (status <= 0) then
649 status_to_message = "NO ERROR"
650 return
651 elseif (status == 1024) then
652 status_to_message = "t_delta_min yielded a longer step than t_delta_max"
653 elseif (status == 1025) then
654 status_to_message = "no_bisect_error_o not present and max_bisect_o violated"
655 elseif (status == 1056) then
656 status_to_message = "no_bisect_error_o not present and max_bisect_o violated"
657 elseif (status == 1152) then
658 status_to_message = "Could not open file for write"
659 elseif (status == 1153) then
660 status_to_message = "Could not close file"
661 elseif (status == 1297) then
662 status_to_message = "Could not open file for write"
663 elseif (status == 1298) then
664 status_to_message = "Could not close file"
665 elseif (status == 1314) then
666 status_to_message = "Inconsistant sequence values: step_v_o * (size(t)-1) /= to_v - from_v"
667 elseif (status == 1315) then
668 status_to_message = "Not enough arguments"
669 elseif (status == 1331) then
670 status_to_message = "new_solution t value out of bounds"
671 elseif (status == 1365) then
672 status_to_message = "Could not open file for write"
673 elseif (status == 1366) then
674 status_to_message = "Could not close file"
675 else
676 status_to_message = "UNKNOWN ERROR"
677 end if
678 status_to_message = trim(status_to_origin(status)) // ": " // status_to_message
679 end function status_to_message
680
681end module mrkiss_utils
Configuration for MRKISS == MR RK KISS == Mitch Richling's Runge-Kutta Keep It Simple Stupid.
real(kind=rk), parameter, public zero_epsilon
Used to test for zero.
integer, parameter, public rk
Real kind used across the library.
integer, parameter, public istats_size
Number of elements in istats(:)
integer, parameter, public fmt_d_ai
Real print format: digits to the right of decimal.
integer, parameter, public istats_str_lng
Length of istats content message.
integer, parameter, public fmt_e_ai
Real print format: digits in exponent.
integer, parameter, public istats_max_idx
Number of used elements in istats(:)
integer, parameter, public fmt_w_ai
Numeric print format: total width.
character(len=istats_str_lng), dimension(istats_size), parameter, public istats_strs
Array of istats descriptions.
Utilities.
character(len=128) function, public status_to_message(status)
Return, as a string, the source of a status code.
subroutine, public print_solution(status, solution, filename_o, separator_o, fmt_w_o, fmt_d_o, fmt_e_o, start_o, end_o, step_o, prt_titles_o, t_min_o, t_max_o, tag_o, append_o)
Output an RK solution matrix.
subroutine, public seq(status, t, from_o, to_o, step_o, max_pts_o)
Produce a sequence of values with fixed seporation.
subroutine, public print_istats(status, istats, idxs_to_prt_o, filename_o, fmt_w_o, prt_zeros_o)
Output an istat array.
subroutine, public analyze_solution(status, solution, filename_o, start_o, end_o, y_delta_len_idxs_o, fmt_w_o, fmt_d_o, fmt_e_o)
Analyze an RK solution matrix.
character(len=32) function, public status_to_origin(status)
Return, as a string, the source of a status code.