PB3D [2.47]
Ideal linear high-n MHD stability in 3-D
Loading...
Searching...
No Matches
messages.f90
Go to the documentation of this file.
1!------------------------------------------------------------------------------!
2!> Numerical utilities related to giving output.
3!------------------------------------------------------------------------------!
4module messages
6 use num_vars, only: dp, max_str_ln
7 use foul
8
9 implicit none
10 private
15#if ldebug
16 public get_mem_usage
17#endif
18
19 ! global variables
20 integer :: lvl !< determines the indenting. higher \c lvl = more indenting
21 character(len=2) :: lvl_sep = '' !< characters that separate different levels of output
22 character(len=10) :: time_sep = '' !< defines the length of time part of output
23 real(dp) :: deltat !< length of time interval
24 real(dp) :: t1, t2 !< end points of time interval
25 logical :: running !< whether the timer is running
26 logical :: temp_output_active !< true if temporary output is to be written in \c temp_output
27 character(len=max_str_ln), allocatable :: temp_output(:) !< temporary output, before output file is opened
28
29contains
30 !> Initialize the variables for the module.
31 subroutine init_output()
32 use num_vars, only: rank
33#if ldebug
34 use num_vars, only: mem_usage_count
35#endif
36
37 ! output level
38 lvl = 1
39
40 ! time
41 deltat = 0
42 t1 = 0
43 t2 = 0
44 running = .false.
45
46 ! temporary output for master
47 if (rank.eq.0) then
48 temp_output_active = .true.
49 allocate(temp_output(0))
50 else
51 temp_output_active = .false.
52 end if
53
54#if ldebug
56#endif
57 end subroutine init_output
58
59 !> Prints first message.
60 subroutine print_hello()
63
64 ! local variables
65 integer :: istat ! status
66
67 if (rank.eq.0) then
68 call write_formatted(' PB3D Copyright (C) 2019 Toon Weyens',&
69 &'italic')
70 call write_formatted(' This program comes with ABSOLUTELY NO &
71 &WARRANTY.','italic')
72 call write_formatted(' This is free software, and you are welcome &
73 &to redistribute it','italic')
74 call write_formatted(' under certain conditions; See LICENSE for &
75 &details.','italic')
76 call write_formatted('','italic')
77
78 call write_formatted(' Simulation started on '//get_date()//', at '&
79 &//get_clock(),'italic')
80 call write_formatted(' '//prog_name//' version: '//&
81 &trim(r2strt(prog_version)),'italic')
82 if (debug_version) then
83 call write_formatted(' debug version','italic')
84 else
85 call write_formatted(' release version','italic')
86 end if
87 if (n_procs.eq.1) then
88 call write_formatted(' 1 MPI process','italic')
89 else
90 call write_formatted(' '//trim(i2str(n_procs))//&
91 &' MPI processes','italic')
92 end if
93 write(*,*,iostat=istat) ''
94 end if
95 end subroutine print_hello
96
97 !> Prints last messag.
98 subroutine print_goodbye()
99 use num_vars, only: rank
100
101 ! local variables
102 integer :: istat ! status
103
104 if (rank.eq.0) then
105 write(*,*,iostat=istat) ''
106 call write_formatted(' Simulation finished on '//get_date()//&
107 &', at '//get_clock(),'italic')
108 end if
109 end subroutine print_goodbye
110
111 !> Intialize the time passed to 0.
112 subroutine init_time()
113 deltat = 0
114 t1 = 0
115 t2 = 0
116 running = .false.
117 end subroutine init_time
118
119 !> Start a timer.
120 subroutine start_time()
121 if (running) then
122 call writo('Tried to start timer, but was already running',&
123 &warning=.true.)
124 else
125 call cpu_time(t1)
126 running = .true.
127 end if
128 end subroutine start_time
129
130 !> Stop a timer.
131 subroutine stop_time()
132 if (running) then
133 call cpu_time(t2)
134
135 ! increase deltat
136 deltat = deltat+t2-t1
137
138 ! set t1 and t2 back to zero
139 t1 = 0
140 t2 = 0
141 running = .false.
142 else
143 call writo('Tried to stop timer, but was already stopped',&
144 &warning=.true.)
145 end if
146 end subroutine stop_time
147
148 !> Display the time that has passed between \c t1 and \c t2.
149 !!
150 !! Automatically stops time and resets everything to zero.
151 subroutine passed_time()
152 ! local variables
153 character(len=max_str_ln) :: begin_str, end_str
154 integer :: time_in_units(4) ! time in days, hours, minutes, seconds
155 integer :: total_time
156 character(len=6) :: time_units(4) = ['day ','hour ','minute',&
157 &'second']
158 integer :: id ! counter
159
160 ! stop at current time if running
161 if (running) call stop_time
162
163 begin_str = '(this took'
164 if (deltat.lt.1) then
165 end_str = ' less than 1 second)'
166 else
167 ! get days, hours, minutes, seconds
168 total_time = floor(deltat) ! get total time in seconds
169 time_in_units(1) = total_time/(60*60*24) ! get days
170 total_time = mod(total_time,60*60*24) ! subtract days from total time
171 time_in_units(2) = total_time/(60*60) ! get hours
172 total_time = mod(total_time,60*60) ! subtract hours from total time
173 time_in_units(3) = total_time/(60) ! get minutes
174 total_time = mod(total_time,60) ! subtract minutes from total time
175 time_in_units(4) = total_time ! get seconds
176
177 ! set up end_str
178 end_str = ''
179 do id = 1,4
180 if (time_in_units(id).gt.0) then
181 if (trim(end_str).ne.'') end_str = trim(end_str)//','
182 end_str = trim(end_str)//' '//&
183 &trim(i2str(time_in_units(id)))//' '//&
184 &trim(time_units(id))
185 if (time_in_units(id).gt.1) end_str = trim(end_str)//'s'
186 end if
187 end do
188 end_str = trim(end_str)//')'
189 end if
190 call writo(trim(begin_str) // trim(end_str))
191
192 ! restart deltat
193 call init_time
194 end subroutine passed_time
195
196 !> Returns the date.
197 !!
198 !! from <http://infohost.nmt.edu/tcc/help/lang/fortran/date.html>
199 function get_date() result(now)
200#if (lwith_intel && !lwith_gnu)
201 use ifport
202#endif
203 ! input / output
204 character(len=10) :: now !< date
205
206 ! local variables
207 integer :: today(3)
208
209 call idate(today) ! today(1)=day, (2)=month, (3)=year
210
211 write (now,'(i2.2,"/",i2.2,"/",i4.4)') today(2), today(1), today(3)
212 end function get_date
213
214 !> Returns the time.
215 !!
216 !! from <http://infohost.nmt.edu/tcc/help/lang/fortran/date.html>
217 function get_clock() result(time)
218 ! input / output
219 character(len=8) :: time !< time
220
221 ! local variables
222 integer :: now(3)
223
224 call itime(now) ! now(1)=hour, (2)=minute, (3)=second
225
226 write (time,'(i2.2,":",i2.2,":",i2.2)') now
227 end function get_clock
228
229 !> Prints an error message that is either user-provided, or the name of the
230 !! calling routine.
231 !!
232 !! \note This should be used with the macro CHCKERR.
233 subroutine print_err_msg(err_msg,routine_name)
234 use num_vars, only: rank
235
236 ! input / output
237 character(len=*), intent(in) :: err_msg !< error message to be printed
238 character(len=*), intent(in) :: routine_name !< name of the routine
239
240 if (trim(err_msg).eq.'') then
241 lvl = 2
242 call writo('>> calling routine: '//trim(routine_name)//' of rank '&
243 &//trim(i2str(rank)),persistent=.true.)
244 else
245 call writo('ERROR in '//trim(routine_name)//': '//trim(err_msg),&
246 &persistent=.true.,error=.true.)
247 end if
248 end subroutine print_err_msg
249
250 !> Increases/decreases \c lvl of output.
251 !!
252 !! Name stands for level up or down.
253 subroutine lvl_ud(inc)
254 ! input / output
255 integer :: inc !< increment of level
256
257 if (lvl+inc.lt.1) then
258 lvl = 1
259 call writo('cannot go below lowest level',warning=.true.)
260 else
261 lvl = lvl + inc
262 end if
263 end subroutine lvl_ud
264
265 !> Write output to file identified by \c output_i.
266 !!
267 !! This is done using the correct indentation for the level (\c lvl_loc) of
268 !! the output.
269 !!
270 !! By default, only the master outputs, but this can be changed using \c
271 !! persistent.
272 !!
273 !! Optionally, special formatting for error, warning or alert can be chosen.
274 subroutine writo(input_str,persistent,error,warning,alert)
276#if ldebug
277 use mpi
280#endif
281
282 ! input / output
283 character(len=*), intent(in) :: input_str !< the name that is searched for
284 logical, intent(in), optional :: persistent !< output even if not group master
285 logical, intent(in), optional :: error !< error message
286 logical, intent(in), optional :: warning !< warning message
287 logical, intent(in), optional :: alert !< alert message
288
289 ! local variables
290 !character(len=7), parameter :: bright_str = "bright " ! dark terminal theme
291 character(len=0), parameter :: bright_str = "" ! light terminal theme
292 character(len=max_str_ln) :: output_str ! output string
293 character(len=max_str_ln) :: time_str ! time string
294 character(len=max_str_ln) :: header_str ! header string
295 character(len=max_str_ln) :: input_str_loc ! local input string
296 character(len=max_str_ln), allocatable :: temp_output_loc(:) ! local temporary output
297 integer :: id, i_part ! counters
298 integer :: max_len_part, num_parts, st_part, en_part ! variables controlling strings
299 logical :: ignore ! normally, everybody but group master is ignored
300 logical :: error_loc ! local error
301 logical :: warning_loc ! local warning
302 logical :: alert_loc ! local alert
303 integer :: istat ! status
304#if ldebug
305 integer :: mem_usage ! memory usage
306 integer(kind=8) :: clock ! current clock
307#endif
308
309 ! bypass output if no_output
310 if (no_output) return
311
312 ! setup ignore, error, warning and alert
313 ignore = .true. ! ignore by default
314 if (rank.eq.0) ignore = .false. ! master process can output
315 if (present(persistent)) ignore = .not.persistent ! persistent can override this
316 error_loc = .false.
317 if (present(error)) error_loc = error
318 warning_loc = .false.
319 if (present(warning)) warning_loc = warning
320 alert_loc = .false.
321 if (present(alert)) alert_loc = alert
322
323 ! set local input string
324 input_str_loc = input_str
325
326 ! prepend "WARNING: " if warning
327 if (warning_loc) input_str_loc = 'WARNING: '//trim(input_str_loc)
328
329#if ldebug
330 ! memory usage
331 if (print_mem_usage) then
332 ! increment counter
334
335 ! get memory usage
336 mem_usage = get_mem_usage()
337
338 ! append count and memory usage in MegaBytes
339 input_str_loc = trim(input_str_loc)//' - ['//&
340 &trim(i2str(mem_usage_count))//': '//&
341 &trim(i2str(mem_usage))//'kB]'
342
343 ! get clock
344 call system_clock(clock)
345
346 ! write rank, count, time, memory usage to file if not temp_output
347 if (.not.temp_output_active) then
348 open(unit=mem_usage_i,file=prog_name//'_'//&
349 &trim(mem_usage_name)//'.dat',status='old',&
350 &position='append',iostat=istat)
351 if (istat.eq.0) then
352 write(mem_usage_i,"(1X,2I10,I21,I10,2ES23.16)",&
353 &iostat=istat) &
354 &rank, mem_usage_count, clock-time_start, mem_usage, &
355 &max_tot_mem*1000, max_x_mem*1000
356 close(unit=mem_usage_i,iostat=istat)
357 end if
358 end if
359 end if
360#endif
361
362 if (.not.ignore) then ! only group master (= global master if no groups) or persistent
363 ! set local error
364
365 ! Divide the input string length by the max_str_ln and loop over the
366 ! different parts
367 max_len_part = max_str_ln-(lvl-1)*len(lvl_sep) - len(time_sep) ! max length of a part including time part
368 num_parts = (len(trim(input_str_loc))-1)/(max_len_part) + 1 ! how many parts there are
369 do i_part = 1, num_parts
370 ! construct input string for the appropriate level
371 st_part = (i_part-1)*max_len_part+1 ! index of start of this part
372 if (i_part.lt.num_parts) then ! index of end of this part
373 en_part = i_part*max_len_part
374 else ! last part is shorter
375 en_part = len(trim(input_str_loc))
376 end if
377 output_str = input_str_loc(st_part:en_part)
378 call format_str(lvl,output_str)
379 call get_time_str(time_str)
380
381 ! construct header string of equal length as output strength
382 header_str = ''
383 do id = 1, len(trim(output_str)) + len(trim(time_str)) + 1 - &
384 &len(time_sep) ! not including time part, 1 for the space
385 header_str = trim(header_str) // '-'
386 end do
387 header_str = ' '//trim(header_str) ! number of spaces matches time string
388
389 ! write output to file output_i or to temporary output
390 if (temp_output_active) then ! temporary output to internal variable temp_output
391 ! back up previous temporary output in local variable
392 allocate(temp_output_loc(size(temp_output)))
393 temp_output_loc = temp_output
394
395 ! reallocate temp_output
396 deallocate(temp_output)
397 if (lvl.eq.1) then ! first level
398 allocate(temp_output(size(temp_output_loc)+3))
399 temp_output(1:size(temp_output_loc)) = temp_output_loc
400 temp_output(size(temp_output_loc)+1) = trim(header_str) ! first level gets extra lines
401 temp_output(size(temp_output_loc)+2) = trim(time_str)//&
402 &' '//trim(output_str)
403 temp_output(size(temp_output_loc)+3) = trim(header_str)
404 else ! other levels only need one line
405 allocate(temp_output(size(temp_output_loc)+1))
406 temp_output(1:size(temp_output_loc)) = temp_output_loc
407 temp_output(size(temp_output_loc)+1) = trim(time_str)//&
408 &' '//output_str
409 end if
410
411 ! deallocate local variable
412 deallocate(temp_output_loc)
413 else ! normal output to file output_i
414 if (output_i.ne.0) then
415 if (lvl.eq.1) write(output_i,"(1X,A)",iostat=istat) &
416 &trim(header_str) ! first level gets extra lines
417 write(output_i,"(1X,A)",iostat=istat) &
418 &trim(time_str)//' '//trim(output_str)
419 if (lvl.eq.1) write(output_i,"(1X,A)",iostat=istat) &
420 &trim(header_str)
421 end if
422 end if
423
424 ! also write output to screen
425 if (error_loc) then
426 call write_formatted(' '//trim(time_str)//' ',&
427 &'background_red',trim(output_str),'italic underline')
428 else if (warning_loc .or. alert_loc) then
429 call write_formatted(' '//trim(time_str)//' ',&
430 &'background_cyan',trim(output_str),'italic underline')
431 else if (lvl.eq.1) then
432 call write_formatted(' '//trim(header_str),&
433 &bright_str//'green')
434 call write_formatted(' '//trim(time_str)//' ','',&
435 &trim(output_str),bright_str//'green')
436 call write_formatted(' '//trim(header_str),&
437 &bright_str//'green')
438 else if (lvl.eq.2) then
439 call write_formatted(' '//trim(time_str)//' ','',&
440 &trim(output_str),bright_str//'blue')
441 else
442 write(*,"(1X,A)",iostat=istat) &
443 &trim(time_str)//' '//trim(output_str)
444 end if
445 end do
446 end if
447 contains
448 ! formats the string, merging time information with the string.
449 !> \private
450 subroutine format_str(lvl,str)
451 ! input / output
452 integer, intent(in) :: lvl ! lvl of indentation
453 character(len=*), intent(inout) :: str ! string that is to be formatted for the lvl
454
455 ! local variables
456 integer :: id ! counter
457
458 do id = 1,lvl-1 ! start with lvl 1
459 str = lvl_sep // trim(str)
460 end do
461 end subroutine format_str
462
463 ! prints a string with time information
464 !> \private
465 subroutine get_time_str(time_str)
466 ! input / output
467 character(len=*), intent(inout) :: time_str ! string with time information
468
469 time_str = get_clock()//':'
470 end subroutine get_time_str
471 end subroutine writo
472
473 !> Print an array of dimension 2 on the screen.
474 subroutine print_ar_2(arr)
475 ! input / output
476 real(dp), intent(in) :: arr(:,:) !< array to be printed
477
478 integer :: id
479
480 do id=1,size(arr,1)
481 call print_ar_1(arr(id,:))
482 end do
483 end subroutine print_ar_2
484
485 !> Print an array of dimension 1 on the screen.
486 subroutine print_ar_1(arr)
487 ! input / output
488 real(dp), intent(in) :: arr(:) !< array to be printed
489
490 ! local variables
491 integer :: id
492 character(len=2*max_str_ln) :: output_str ! holds string for a range of values
493 character(len=14) :: var_str ! holds string for one value, for last value
494 integer :: vlen ! space for one variable + leading space
495 integer :: n_free ! how much space free
496 logical :: str_full ! whether the output_str is full
497 integer :: istat ! status
498
499 output_str = '|'
500 str_full = .false.
501 id = 1
502 vlen = 10 ! 9 for variable and 1 for space
503
504 do while(.not.str_full .and. id.le.size(arr))
505 var_str = ''
506 n_free = len(output_str) - len(trim(output_str)) ! how many characters free
507
508 if (n_free.ge.2*vlen+6) then ! >2 fit
509 continue
510 else if (2*vlen+2.le.n_free .and. n_free.lt.2*vlen+6 .and. & ! special case: maximum 2 left -> all fit
511 &id+1.ge.size(arr)) then
512 continue
513 else if (vlen+6.le.n_free .and. n_free.lt.2*vlen+6) then ! 1 fit, rest truncated
514 if (id.ge.size(arr)) then ! only 1 left -> all fit
515 continue
516 else ! truncate
517 str_full = .true.
518 end if
519 else if (vlen+2.le.n_free .and. n_free.lt.vlen+6 .and. & ! special case: maximum 1 left -> all fit
520 &id.eq.size(arr)) then
521 continue
522 else ! this should never be reached
523 call writo('not enough room to display variable. Something &
524 &went wrong',warning=.true.)
525 return
526 end if
527
528
529 if (str_full) then ! truncate
530 write (var_str, '(ES9.2)') arr(size(arr)) ! last variable
531 var_str = ' ... ' // trim(var_str)
532 else
533 write (var_str, '(ES9.2)') arr(id) ! current variable
534 var_str = ' ' // trim(var_str)
535 end if
536 output_str = trim(output_str) // trim(var_str)
537
538 id = id+1
539 end do
540 output_str = trim(output_str) // ' |'
541 write(*,'(1X,A)',iostat=istat) output_str
542 end subroutine print_ar_1
543
544#if ldebug
545 !> Returns the memory usage in kilobytes.
546 !!
547 !! Based on
548 !! <http://stackoverflow.com/questions/22028571/track-memory-usage-in-fortran-90>
549 !!
550 !! \note Only works under linux.
551 !!
552 !! \ldebug
553 integer function get_mem_usage() result(mem)
554 use num_vars, only: mem_usage_i
555#if ( lwith_intel && !lwith_gnu)
556 use ifport
557#endif
558
559 ! local variables
560 character(len=200):: filename=' ' !< name of file where memory stored
561 character(len=80) :: line !< line of memory file
562 character(len=8) :: pid_char=' ' !< process ID in string
563 logical :: exists !< whether memory file exists
564 integer :: pid !< process ID
565 integer :: istat !< status
566
567 ! initiazlie mem to negative number
568 mem = -1
569
570 ! get process ID and write it in string
571 pid = getpid()
572 write(pid_char,'(I8)') pid
573
574 ! set up file name
575 filename='/proc/'//trim(adjustl(pid_char))//'/status'
576
577 ! inquire about memory file
578 inquire (file=filename,exist=exists)
579 if (.not.exists) return
580
581 ! open and read memory file
582 open(unit=mem_usage_i-1,file=filename,action='read',iostat=istat)
583 if (istat.ne.0) return
584 do
585 read (mem_usage_i-1,'(A)',iostat=istat) line
586 if (istat.ne.0) return
587 if (line(1:6).eq.'VmRSS:') then
588 read (line(7:),*) mem
589 exit
590 endif
591 end do
592
593 ! close memory file
594 close(mem_usage_i-1)
595 end function get_mem_usage
596#endif
597end module messages
Numerical utilities related to giving output.
Definition messages.f90:4
character(len=max_str_ln), dimension(:), allocatable, public temp_output
temporary output, before output file is opened
Definition messages.f90:27
subroutine, public print_err_msg(err_msg, routine_name)
Prints an error message that is either user-provided, or the name of the calling routine.
Definition messages.f90:234
subroutine, public passed_time()
Display the time that has passed between t1 and t2.
Definition messages.f90:152
integer, public lvl
determines the indenting. higher lvl = more indenting
Definition messages.f90:20
subroutine, public print_ar_1(arr)
Print an array of dimension 1 on the screen.
Definition messages.f90:487
subroutine, public print_goodbye()
Prints last messag.
Definition messages.f90:99
subroutine, public init_output()
Initialize the variables for the module.
Definition messages.f90:32
subroutine, public print_hello()
Prints first message.
Definition messages.f90:61
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition messages.f90:254
subroutine, public print_ar_2(arr)
Print an array of dimension 2 on the screen.
Definition messages.f90:475
integer function, public get_mem_usage()
Returns the memory usage in kilobytes.
Definition messages.f90:554
subroutine, public start_time()
Start a timer.
Definition messages.f90:121
subroutine, public init_time()
Intialize the time passed to 0.
Definition messages.f90:113
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition messages.f90:275
character(len=2), public lvl_sep
characters that separate different levels of output
Definition messages.f90:21
character(len=10), public time_sep
defines the length of time part of output
Definition messages.f90:22
logical, public temp_output_active
true if temporary output is to be written in temp_output
Definition messages.f90:26
subroutine, public stop_time()
Stop a timer.
Definition messages.f90:132
Numerical variables used by most other modules.
Definition num_vars.f90:4
integer, parameter, public dp
double precision
Definition num_vars.f90:46
logical, public print_mem_usage
print memory usage is printed
Definition num_vars.f90:149
logical, public no_output
no output shown
Definition num_vars.f90:145
integer, public n_procs
nr. of MPI processes
Definition num_vars.f90:69
integer, parameter, public max_str_ln
maximum length of strings
Definition num_vars.f90:50
logical, public debug_version
debug version used
Definition num_vars.f90:62
integer, parameter, public mem_usage_i
file number of memory usage file
Definition num_vars.f90:187
integer(kind=8), public time_start
start time of simulation
Definition num_vars.f90:71
character(len=4), public prog_name
name of program, used for info
Definition num_vars.f90:54
real(dp), parameter, public prog_version
version number
Definition num_vars.f90:59
integer, public mem_usage_count
counter for memory usage output
Definition num_vars.f90:58
character(len=9), parameter, public mem_usage_name
name of memory usage file
Definition num_vars.f90:57
integer, public rank
MPI rank.
Definition num_vars.f90:68
real(dp), public max_tot_mem
maximum total memory for all processes [MB]
Definition num_vars.f90:74
integer, parameter, public output_i
file number of output file
Definition num_vars.f90:185
real(dp), public max_x_mem
maximum memory for perturbation calculations for all processes [MB]
Definition num_vars.f90:75
Operations on strings.
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.