PB3D  [2.45]
Ideal linear high-n MHD stability in 3-D
messages.f90
Go to the documentation of this file.
1 !------------------------------------------------------------------------------!
3 !------------------------------------------------------------------------------!
4 module messages
5  use str_utilities
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
21  character(len=2) :: lvl_sep = ''
22  character(len=10) :: time_sep = ''
23  real(dp) :: deltat
24  real(dp) :: t1, t2
25  logical :: running
26  logical :: temp_output_active
27  character(len=max_str_ln), allocatable :: temp_output(:)
28 
29 contains
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
55  mem_usage_count = 0
56 #endif
57  end subroutine init_output
58 
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 
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 
112  subroutine init_time()
113  deltat = 0
114  t1 = 0
115  t2 = 0
116  running = .false.
117  end subroutine init_time
118 
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 
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 
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 
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
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 
217  function get_clock() result(time)
218  ! input / output
219  character(len=8) :: 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 
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
238  character(len=*), intent(in) :: routine_name
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 
253  subroutine lvl_ud(inc)
254  ! input / output
255  integer :: inc
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 
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
284  logical, intent(in), optional :: persistent
285  logical, intent(in), optional :: error
286  logical, intent(in), optional :: warning
287  logical, intent(in), optional :: alert
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.
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
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 
474  subroutine print_ar_2(arr)
475  ! input / output
476  real(dp), intent(in) :: arr(:,:)
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 
486  subroutine print_ar_1(arr)
487  ! input / output
488  real(dp), intent(in) :: arr(:)
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 
553  integer function get_mem_usage() result(mem)
555 #if ( lwith_intel && !lwith_gnu)
556  use ifport
557 #endif
558 
559  ! local variables
560  character(len=200):: filename=' '
561  character(len=80) :: line
562  character(len=8) :: pid_char=' '
563  logical :: exists
564  integer :: pid
565  integer :: istat
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
597 end module messages
num_vars::dp
integer, parameter, public dp
double precision
Definition: num_vars.f90:46
messages::print_ar_1
subroutine, public print_ar_1(arr)
Print an array of dimension 1 on the screen.
Definition: messages.f90:487
num_vars
Numerical variables used by most other modules.
Definition: num_vars.f90:4
num_vars::max_str_ln
integer, parameter, public max_str_ln
maximum length of strings
Definition: num_vars.f90:50
messages::passed_time
subroutine, public passed_time()
Display the time that has passed between t1 and t2.
Definition: messages.f90:152
str_utilities::i2str
elemental character(len=max_str_ln) function, public i2str(k)
Convert an integer to string.
Definition: str_utilities.f90:18
messages::print_ar_2
subroutine, public print_ar_2(arr)
Print an array of dimension 2 on the screen.
Definition: messages.f90:475
messages::start_time
subroutine, public start_time()
Start a timer.
Definition: messages.f90:121
messages::get_mem_usage
integer function, public get_mem_usage()
Returns the memory usage in kilobytes.
Definition: messages.f90:554
num_vars::n_procs
integer, public n_procs
nr. of MPI processes
Definition: num_vars.f90:69
messages::stop_time
subroutine, public stop_time()
Stop a timer.
Definition: messages.f90:132
num_vars::debug_version
logical, public debug_version
debug version used
Definition: num_vars.f90:62
messages::lvl
integer, public lvl
determines the indenting. higher lvl = more indenting
Definition: messages.f90:20
str_utilities
Operations on strings.
Definition: str_utilities.f90:4
str_utilities::r2strt
elemental character(len=max_str_ln) function, public r2strt(k)
Convert a real (double) to string.
Definition: str_utilities.f90:54
num_vars::prog_name
character(len=4), public prog_name
name of program, used for info
Definition: num_vars.f90:54
messages::time_sep
character(len=10), public time_sep
defines the length of time part of output
Definition: messages.f90:22
num_vars::print_mem_usage
logical, public print_mem_usage
print memory usage is printed
Definition: num_vars.f90:149
num_vars::mem_usage_i
integer, parameter, public mem_usage_i
file number of memory usage file
Definition: num_vars.f90:187
num_vars::mem_usage_count
integer, public mem_usage_count
counter for memory usage output
Definition: num_vars.f90:58
messages::lvl_sep
character(len=2), public lvl_sep
characters that separate different levels of output
Definition: messages.f90:21
num_vars::no_output
logical, public no_output
no output shown
Definition: num_vars.f90:145
messages::temp_output
character(len=max_str_ln), dimension(:), allocatable, public temp_output
temporary output, before output file is opened
Definition: messages.f90:27
messages::writo
subroutine, public writo(input_str, persistent, error, warning, alert)
Write output to file identified by output_i.
Definition: messages.f90:275
messages
Numerical utilities related to giving output.
Definition: messages.f90:4
num_vars::mem_usage_name
character(len=9), parameter, public mem_usage_name
name of memory usage file
Definition: num_vars.f90:57
messages::get_clock
character(len=8) function get_clock()
Returns the time.
Definition: messages.f90:218
messages::print_goodbye
subroutine, public print_goodbye()
Prints last messag.
Definition: messages.f90:99
num_vars::time_start
integer(kind=8), public time_start
start time of simulation
Definition: num_vars.f90:71
messages::lvl_ud
subroutine, public lvl_ud(inc)
Increases/decreases lvl of output.
Definition: messages.f90:254
num_vars::prog_version
real(dp), parameter, public prog_version
version number
Definition: num_vars.f90:59
messages::print_err_msg
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
messages::init_time
subroutine, public init_time()
Intialize the time passed to 0.
Definition: messages.f90:113
messages::init_output
subroutine, public init_output()
Initialize the variables for the module.
Definition: messages.f90:32
num_vars::rank
integer, public rank
MPI rank.
Definition: num_vars.f90:68
num_vars::output_i
integer, parameter, public output_i
file number of output file
Definition: num_vars.f90:185
num_vars::max_tot_mem
real(dp), public max_tot_mem
maximum total memory for all processes [MB]
Definition: num_vars.f90:74
messages::temp_output_active
logical, public temp_output_active
true if temporary output is to be written in temp_output
Definition: messages.f90:26
num_vars::max_x_mem
real(dp), public max_x_mem
maximum memory for perturbation calculations for all processes [MB]
Definition: num_vars.f90:75
messages::print_hello
subroutine, public print_hello()
Prints first message.
Definition: messages.f90:61