FMS  2025.02
Flexible Modeling System
mpp_util.inc
1 ! -*-f90-*-
2 
3 
4 !***********************************************************************
5 !* GNU Lesser General Public License
6 !*
7 !* This file is part of the GFDL Flexible Modeling System (FMS).
8 !*
9 !* FMS is free software: you can redistribute it and/or modify it under
10 !* the terms of the GNU Lesser General Public License as published by
11 !* the Free Software Foundation, either version 3 of the License, or (at
12 !* your option) any later version.
13 !*
14 !* FMS is distributed in the hope that it will be useful, but WITHOUT
15 !* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 !* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 !* for more details.
18 !*
19 !* You should have received a copy of the GNU Lesser General Public
20 !* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
21 !***********************************************************************
22 !> @file
23 !> @brief General utility functions for use in @ref mpp_mod
24 
25 !> @addtogroup mpp_mod
26 !> @{
27 
28 #if defined(use_libMPI)
29 #include <mpp_util_mpi.inc>
30 #else
31 #include <mpp_util_nocomm.inc>
32 #endif
33 
34  !> @brief This function returns the current standard fortran unit numbers for input.
35  function stdin()
36  integer :: stdin
37  stdin = in_unit
38  return
39  end function stdin
40 
41  !> @brief This function returns the current standard fortran unit numbers for output.
42  function stdout()
43  integer :: stdout
44  stdout = out_unit
45  if( pe.NE.root_pe )stdout = stdlog()
46  return
47  end function stdout
48 
49  !> @brief This function returns the current standard fortran unit numbers for error messages.
50  function stderr()
51  integer :: stderr
52  stderr = err_unit
53  return
54  end function stderr
55 
56  !> @brief This function returns the current standard fortran unit numbers for log messages.
57  !! Log messages, by convention, are written to the file <TT>logfile.out</TT>.
58  function stdlog()
59  integer :: stdlog
60  logical :: opened
61  character(len=11) :: this_pe
62 !$ logical :: omp_in_parallel
63 !$ integer :: omp_get_num_threads
64 !$ integer :: errunit
65 
66 
67 !NOTES: We can not use mpp_error to handle the error because mpp_error
68 ! will call stdout and stdout will call stdlog for non-root-pe.
69 ! This will be a cicular call.
70 
71 !$ if( omp_in_parallel() .and. (omp_get_num_threads() > 1) ) then
72 !$OMP single
73 !$ errunit = stderr()
74 !$ write( errunit,'(/a/)' ) 'FATAL: STDLOG: is called inside a OMP parallel region'
75 #ifdef use_libMPI
76 !$ call MPI_ABORT( MPI_COMM_WORLD, 1, error )
77 #else
78 !$ call ABORT()
79 #endif
80 !$OMP end single
81 !$ endif
82 
83  if( pe.EQ.root_pe )then
84  write(this_pe,'(a,i6.6,a)') '.',pe,'.out'
85  inquire( file=trim(configfile)//this_pe, opened=opened )
86  if( opened )then
87  FLUSH(log_unit)
88  else
89  open(newunit=log_unit, status='UNKNOWN', file=trim(configfile)//this_pe, position='APPEND', err=10 )
90  end if
91  stdlog = log_unit
92  else
93  inquire(unit=etc_unit, opened=opened )
94  if( opened )then
95  FLUSH(etc_unit)
96  else
97  open(newunit=etc_unit, status='UNKNOWN', file=trim(etcfile), position='APPEND', err=11 )
98  end if
99  stdlog = etc_unit
100  end if
101  return
102 10 call mpp_error( fatal, 'STDLOG: unable to open '//trim(configfile)//this_pe//'.' )
103 11 call mpp_error( fatal, 'STDLOG: unable to open '//trim(etcfile)//'.' )
104  end function stdlog
105 
106  !#####################################################################
107  subroutine mpp_init_logfile()
108  integer :: p
109  logical :: exist
110  character(len=11) :: this_pe
111  if( pe.EQ.root_pe )then
112  do p=0,npes-1
113  write(this_pe,'(a,i6.6,a)') '.',p,'.out'
114  inquire( file=trim(configfile)//this_pe, exist=exist )
115  if(exist)then
116  open(newunit=log_unit, file=trim(configfile)//this_pe, status='REPLACE' )
117  close(log_unit)
118  endif
119  end do
120  end if
121  end subroutine mpp_init_logfile
122 
123  !> Opens the warning log file, called during mpp_init
124  subroutine mpp_init_warninglog()
125  logical :: exist
126  character(len=11) :: this_pe
127  if( pe.EQ.root_pe )then
128  write(this_pe,'(a,i6.6,a)') '.',pe,'.out'
129  inquire( file=trim(warnfile)//this_pe, exist=exist )
130  if(exist)then
131  open(newunit=warn_unit, file=trim(warnfile)//this_pe, status='REPLACE' )
132  else
133  open(newunit=warn_unit, file=trim(warnfile)//this_pe, status='NEW' )
134  endif
135  end if
136  end subroutine mpp_init_warninglog
137 
138  !> @brief This function returns unit number for the warning log
139  !! if on the root pe, otherwise returns the etc_unit value (usually /dev/null)
140  function warnlog()
141  integer :: warnlog
142  if(.not. module_is_initialized) call mpp_error(fatal, "mpp_mod: warnlog cannot be called before mpp_init")
143  if(root_pe .eq. pe) then
144  warnlog = warn_unit
145  else
146  warnlog = etc_unit
147  endif
148  return
149  end function warnlog
150 
151  !#####################################################################
152  subroutine mpp_set_warn_level(flag)
153  integer, intent(in) :: flag
154 
155  if( flag.EQ.warning )then
156  warnings_are_fatal = .false.
157  else if( flag.EQ.fatal )then
158  warnings_are_fatal = .true.
159  else
160  call mpp_error( fatal, 'MPP_SET_WARN_LEVEL: warning flag must be set to WARNING or FATAL.' )
161  end if
162  return
163  end subroutine mpp_set_warn_level
164 
165  !#####################################################################
166  function mpp_error_state()
167  integer :: mpp_error_state
168  mpp_error_state = error_state
169  return
170  end function mpp_error_state
171 
172 !#####################################################################
173 !> @brief overloads to mpp_error_basic, support for error_mesg routine in FMS
174 subroutine mpp_error_mesg( routine, errormsg, errortype )
175  character(len=*), intent(in) :: routine, errormsg
176  integer, intent(in) :: errortype
177 
178  call mpp_error( errortype, trim(routine)//': '//trim(errormsg) )
179  return
180 end subroutine mpp_error_mesg
181 
182 !#####################################################################
183 subroutine mpp_error_noargs()
184  call mpp_error(fatal)
185 end subroutine mpp_error_noargs
186 
187 !#####################################################################
188 subroutine mpp_error_is(errortype, errormsg1, mpp_ival, errormsg2)
189  integer, intent(in) :: errortype
190  INTEGER, intent(in) :: mpp_ival
191  character(len=*), intent(in) :: errormsg1
192  character(len=*), intent(in), optional :: errormsg2
193  call mpp_error( errortype, errormsg1, (/mpp_ival/), errormsg2)
194 end subroutine mpp_error_is
195 !#####################################################################
196 subroutine mpp_error_rs(errortype, errormsg1, mpp_rval, errormsg2)
197  integer, intent(in) :: errortype
198  REAL, intent(in) :: mpp_rval
199  character(len=*), intent(in) :: errormsg1
200  character(len=*), intent(in), optional :: errormsg2
201  call mpp_error( errortype, errormsg1, (/mpp_rval/), errormsg2)
202 end subroutine mpp_error_rs
203 !#####################################################################
204 subroutine mpp_error_ia(errortype, errormsg1, array, errormsg2)
205  integer, intent(in) :: errortype
206  INTEGER, dimension(:), intent(in) :: array
207  character(len=*), intent(in) :: errormsg1
208  character(len=*), intent(in), optional :: errormsg2
209  character(len=512) :: string
210 
211  string = errormsg1//trim(array_to_char(array))
212  if(present(errormsg2)) string = trim(string)//errormsg2
213  call mpp_error_basic( errortype, trim(string))
214 
215 end subroutine mpp_error_ia
216 
217 !#####################################################################
218 subroutine mpp_error_ra(errortype, errormsg1, array, errormsg2)
219  integer, intent(in) :: errortype
220  REAL, dimension(:), intent(in) :: array
221  character(len=*), intent(in) :: errormsg1
222  character(len=*), intent(in), optional :: errormsg2
223  character(len=512) :: string
224 
225  string = errormsg1//trim(array_to_char(array))
226  if(present(errormsg2)) string = trim(string)//errormsg2
227  call mpp_error_basic( errortype, trim(string))
228 
229 end subroutine mpp_error_ra
230 
231 !#####################################################################
232 #define _SUBNAME_ mpp_error_ia_ia
233 #define _ARRAY1TYPE_ integer
234 #define _ARRAY2TYPE_ integer
235 #include <mpp_error_a_a.fh>
236 #undef _SUBNAME_
237 #undef _ARRAY1TYPE_
238 #undef _ARRAY2TYPE_
239 !#####################################################################
240 #define _SUBNAME_ mpp_error_ia_ra
241 #define _ARRAY1TYPE_ integer
242 #define _ARRAY2TYPE_ real
243 #include <mpp_error_a_a.fh>
244 #undef _SUBNAME_
245 #undef _ARRAY1TYPE_
246 #undef _ARRAY2TYPE_
247 !#####################################################################
248 #define _SUBNAME_ mpp_error_ra_ia
249 #define _ARRAY1TYPE_ real
250 #define _ARRAY2TYPE_ integer
251 #include <mpp_error_a_a.fh>
252 #undef _SUBNAME_
253 #undef _ARRAY1TYPE_
254 #undef _ARRAY2TYPE_
255 !#####################################################################
256 #define _SUBNAME_ mpp_error_ra_ra
257 #define _ARRAY1TYPE_ real
258 #define _ARRAY2TYPE_ real
259 #include <mpp_error_a_a.fh>
260 #undef _SUBNAME_
261 #undef _ARRAY1TYPE_
262 #undef _ARRAY2TYPE_
263 !#####################################################################
264 #define _SUBNAME_ mpp_error_ia_is
265 #define _ARRAY1TYPE_ integer
266 #define _ARRAY2TYPE_ integer
267 #include <mpp_error_a_s.fh>
268 #undef _SUBNAME_
269 #undef _ARRAY1TYPE_
270 #undef _ARRAY2TYPE_
271 !#####################################################################
272 #define _SUBNAME_ mpp_error_ia_rs
273 #define _ARRAY1TYPE_ integer
274 #define _ARRAY2TYPE_ real
275 #include <mpp_error_a_s.fh>
276 #undef _SUBNAME_
277 #undef _ARRAY1TYPE_
278 #undef _ARRAY2TYPE_
279 !#####################################################################
280 #define _SUBNAME_ mpp_error_ra_is
281 #define _ARRAY1TYPE_ real
282 #define _ARRAY2TYPE_ integer
283 #include <mpp_error_a_s.fh>
284 #undef _SUBNAME_
285 #undef _ARRAY1TYPE_
286 #undef _ARRAY2TYPE_
287 !#####################################################################
288 #define _SUBNAME_ mpp_error_ra_rs
289 #define _ARRAY1TYPE_ real
290 #define _ARRAY2TYPE_ real
291 #include <mpp_error_a_s.fh>
292 #undef _SUBNAME_
293 #undef _ARRAY1TYPE_
294 #undef _ARRAY2TYPE_
295 !#####################################################################
296 #define _SUBNAME_ mpp_error_is_ia
297 #define _ARRAY1TYPE_ integer
298 #define _ARRAY2TYPE_ integer
299 #include <mpp_error_s_a.fh>
300 #undef _SUBNAME_
301 #undef _ARRAY1TYPE_
302 #undef _ARRAY2TYPE_
303 !#####################################################################
304 #define _SUBNAME_ mpp_error_is_ra
305 #define _ARRAY1TYPE_ integer
306 #define _ARRAY2TYPE_ real
307 #include <mpp_error_s_a.fh>
308 #undef _SUBNAME_
309 #undef _ARRAY1TYPE_
310 #undef _ARRAY2TYPE_
311 !#####################################################################
312 #define _SUBNAME_ mpp_error_rs_ia
313 #define _ARRAY1TYPE_ real
314 #define _ARRAY2TYPE_ integer
315 #include <mpp_error_s_a.fh>
316 #undef _SUBNAME_
317 #undef _ARRAY1TYPE_
318 #undef _ARRAY2TYPE_
319 !#####################################################################
320 #define _SUBNAME_ mpp_error_rs_ra
321 #define _ARRAY1TYPE_ real
322 #define _ARRAY2TYPE_ real
323 #include <mpp_error_s_a.fh>
324 #undef _SUBNAME_
325 #undef _ARRAY1TYPE_
326 #undef _ARRAY2TYPE_
327 !#####################################################################
328 #define _SUBNAME_ mpp_error_is_is
329 #define _ARRAY1TYPE_ integer
330 #define _ARRAY2TYPE_ integer
331 #include <mpp_error_s_s.fh>
332 #undef _SUBNAME_
333 #undef _ARRAY1TYPE_
334 #undef _ARRAY2TYPE_
335 !#####################################################################
336 #define _SUBNAME_ mpp_error_is_rs
337 #define _ARRAY1TYPE_ integer
338 #define _ARRAY2TYPE_ real
339 #include <mpp_error_s_s.fh>
340 #undef _SUBNAME_
341 #undef _ARRAY1TYPE_
342 #undef _ARRAY2TYPE_
343 !#####################################################################
344 #define _SUBNAME_ mpp_error_rs_is
345 #define _ARRAY1TYPE_ real
346 #define _ARRAY2TYPE_ integer
347 #include <mpp_error_s_s.fh>
348 #undef _SUBNAME_
349 #undef _ARRAY1TYPE_
350 #undef _ARRAY2TYPE_
351 !#####################################################################
352 #define _SUBNAME_ mpp_error_rs_rs
353 #define _ARRAY1TYPE_ real
354 #define _ARRAY2TYPE_ real
355 #include <mpp_error_s_s.fh>
356 #undef _SUBNAME_
357 #undef _ARRAY1TYPE_
358 #undef _ARRAY2TYPE_
359 !#####################################################################
360 function iarray_to_char(iarray) result(string)
361 integer, intent(in) :: iarray(:)
362 character(len=256) :: string
363 character(len=32) :: chtmp
364 integer :: i, len_tmp, len_string
365 
366  string = ''
367  do i=1,size(iarray)
368  write(chtmp,'(i16)') iarray(i)
369  chtmp = adjustl(chtmp)
370  len_tmp = len_trim(chtmp)
371  len_string = len_trim(string)
372  string(len_string+1:len_string+len_tmp) = trim(chtmp)
373  string(len_string+len_tmp+1:len_string+len_tmp+1) = ','
374  enddo
375  len_string = len_trim(string)
376  string(len_string:len_string) = ' ' ! remove trailing comma
377 
378 end function iarray_to_char
379 !#####################################################################
380 function rarray_to_char(rarray) result(string)
381 real, intent(in) :: rarray(:)
382 character(len=256) :: string
383 character(len=32) :: chtmp
384 integer :: i, len_tmp, len_string
385 
386  string = ''
387  do i=1,size(rarray)
388  write(chtmp,'(G16.9)') rarray(i)
389  chtmp = adjustl(chtmp)
390  len_tmp = len_trim(chtmp)
391  len_string = len_trim(string)
392  string(len_string+1:len_string+len_tmp) = trim(chtmp)
393  string(len_string+len_tmp+1:len_string+len_tmp+1) = ','
394  enddo
395  len_string = len_trim(string)
396  string(len_string:len_string) = ' ' ! remove trailing comma
397 
398 end function rarray_to_char
399 
400  !> @brief Returns processor ID.
401  !!
402  !> This returns the unique ID associated with a PE. This number runs
403  !! between 0 and <TT>npes-1</TT>, where <TT>npes</TT> is the total
404  !! processor count, returned by <TT>mpp_npes</TT>. For a uniprocessor
405  !! application this will always return 0.
406  function mpp_pe()
407  integer :: mpp_pe
408 
409  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_PE: You must first call mpp_init.' )
410  mpp_pe = pe
411  return
412  end function mpp_pe
413 
414  !#####################################################################
415 
416  !> @brief Returns processor count for current pelist
417  !!
418  !> This returns the number of PEs in the current pelist. For a uniprocessor application,
419  !! it will always return 1.
420  function mpp_npes()
421  integer :: mpp_npes
422 
423  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_NPES: You must first call mpp_init.' )
424  mpp_npes = size(peset(current_peset_num)%list(:))
425  return
426  end function mpp_npes
427 
428  !#####################################################################
429  function mpp_root_pe()
430  integer :: mpp_root_pe
431 
432  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_ROOT_PE: You must first call mpp_init.' )
433  mpp_root_pe = root_pe
434  return
435  end function mpp_root_pe
436 
437  !#####################################################################
438  function mpp_commid()
439  integer :: mpp_commid
440 
441  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_COMMID: You must first call mpp_init.' )
442  mpp_commid = peset(current_peset_num)%id
443  return
444  end function mpp_commid
445 
446  !#####################################################################
447  subroutine mpp_set_root_pe(num)
448  integer, intent(in) :: num
449 
450  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_SET_ROOT_PE: You must first call mpp_init.' )
451  if( .NOT.(any(num.EQ.peset(current_peset_num)%list(:))) ) &
452  call mpp_error( fatal, 'MPP_SET_ROOT_PE: you cannot set a root PE outside the current pelist.' )
453  root_pe = num
454  return
455  end subroutine mpp_set_root_pe
456 
457  !> @brief Declare a pelist.
458  !!
459  !> This call is written specifically to accommodate a MPI restriction
460  !! that requires a parent communicator to create a child communicator, In
461  !! other words: a pelist cannot go off and declare a communicator, but
462  !! every PE in the parent, including those not in pelist(:), must get
463  !! together for the <TT>MPI_COMM_CREATE</TT> call. The parent is
464  !! typically <TT>MPI_COMM_WORLD</TT>, though it could also be a subset
465  !! that includes all PEs in <TT>pelist</TT>.
466  !!
467  !! This call implies synchronization across the PEs in the current
468  !! pelist, of which <TT>pelist</TT> is a subset.
469  subroutine mpp_declare_pelist( pelist, name, commID )
470  integer, intent(in) :: pelist(:) !> pelist you are declaring and storing within FMS
471  character(len=*), intent(in), optional :: name !> unique name for an input pelist
472  integer, intent(out), optional :: commID !> return of current MPI comm group communicator ID
473  integer :: i
474 
475  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_DECLARE_PELIST: You must first call mpp_init.' )
476  i = get_peset(pelist)
477  write( peset(i)%name,'(a,i2.2)' ) 'PElist', i !default name
478  if( PRESENT(name) )peset(i)%name = name
479  if( PRESENT(commid) )commid = peset(i)%id
480 
481  return
482  end subroutine mpp_declare_pelist
483 
484  !#####################################################################
485 
486  !> @brief Set context pelist
487  !!
488  !! This call sets the value of the current pelist, which is the
489  !! context for all subsequent "global" calls where the optional
490  !! <TT>pelist</TT> argument is omitted. All the PEs that are to be in the
491  !! current pelist must call it.
492  !!
493  !! In MPI, this call may hang unless <TT>pelist</TT> has been previous
494  !! declared using @ref mpp_declare_pelist
495  !!
496  !! If the argument <TT>pelist</TT> is absent, the current pelist is
497  !! set to the "world" pelist, of all PEs in the job.
498  subroutine mpp_set_current_pelist( pelist, no_sync )
499  !Once we branch off into a PE subset, we want subsequent "global" calls to
500  !sync only across this subset. This is declared as the current pelist (peset(current_peset_num)%list)
501  !when current_peset all pelist ops with no pelist should apply the current pelist.
502  !also, we set the start PE in this pelist to be the root_pe.
503  !unlike mpp_declare_pelist, this is called by the PEs in the pelist only
504  !so if the PEset has not been previously declared, this will hang in MPI.
505  !if pelist is omitted, we reset pelist to the world pelist.
506  integer, intent(in), optional :: pelist(:)
507  logical, intent(in), optional :: no_sync
508 
509  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_SET_CURRENT_PELIST: You must first call mpp_init.' )
510  if( PRESENT(pelist) )then
511  if( .NOT.any(pe.EQ.pelist) )call mpp_error( fatal, 'MPP_SET_CURRENT_PELIST: pe must be in pelist.' )
512  current_peset_num = get_peset(pelist)
513  else
514  current_peset_num = world_peset_num
515  end if
516  call mpp_set_root_pe( minval(peset(current_peset_num)%list(:)) )
517  if(.not.PRESENT(no_sync))call mpp_sync() !this is called to make sure everyone in the current pelist is here.
518  ! npes = mpp_npes()
519  return
520  end subroutine mpp_set_current_pelist
521 
522  !#####################################################################
523  function mpp_get_current_pelist_name()
524  ! Simply return the current pelist name
525  character(len=len(peset(current_peset_num)%name)) :: mpp_get_current_pelist_name
526 
527  mpp_get_current_pelist_name = peset(current_peset_num)%name
528  end function mpp_get_current_pelist_name
529 
530  !#####################################################################
531  !this is created for use by mpp_define_domains within a pelist
532  !will be published but not publicized
533  subroutine mpp_get_current_pelist( pelist, name, commID )
534  integer, intent(out) :: pelist(:)
535  character(len=*), intent(out), optional :: name
536  integer, intent(out), optional :: commID
537 
538  if( size(pelist(:)).NE.size(peset(current_peset_num)%list(:)) ) &
539  call mpp_error( fatal, 'MPP_GET_CURRENT_PELIST: size(pelist) is wrong.' )
540  pelist(:) = peset(current_peset_num)%list(:)
541  if( PRESENT(name) )name = peset(current_peset_num)%name
542 #ifdef use_libMPI
543  if( PRESENT(commid) )commid = peset(current_peset_num)%id
544 #endif
545 
546  return
547  end subroutine mpp_get_current_pelist
548 
549 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
550  ! !
551  ! PERFORMANCE PROFILING CALLS !
552  ! !
553 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
554 !predefined clock granularities, but you can use any integer!using CLOCK_LOOP and above may distort coarser-grain measurements
555  !> @brief Set the level of granularity of timing measurements.
556  !!
557  !> This routine and three other routines, mpp_clock_id, mpp_clock_begin(id),
558  !! and mpp_clock_end(id) may be used to time parallel code sections, and
559  !! extract parallel statistics. Clocks are identified by names, which
560  !! should be unique in the first 32 characters. The <TT>mpp_clock_id</TT>
561  !! call initializes a clock of a given name and returns an integer
562  !! <TT>id</TT>. This <TT>id</TT> can be used by subsequent
563  !! <TT>mpp_clock_begin</TT> and <TT>mpp_clock_end</TT> calls set around a
564  !! code section to be timed. Example:
565  !! <PRE>
566  !! integer :: id
567  !! id = mpp_clock_id( 'Atmosphere' )
568  !! call mpp_clock_begin(id)
569  !! call atmos_model()
570  !! call mpp_clock_end()
571  !! </PRE>
572  !! Two flags may be used to alter the behaviour of
573  !! <TT>mpp_clock</TT>. If the flag <TT>MPP_CLOCK_SYNC</TT> is turned on
574  !! by <TT>mpp_clock_id</TT>, the clock calls <TT>mpp_sync</TT> across all
575  !! the PEs in the current pelist at the top of the timed code section,
576  !! but allows each PE to complete the code section (and reach
577  !! <TT>mpp_clock_end</TT>) at different times. This allows us to measure
578  !! load imbalance for a given code section. Statistics are written to
579  !! <TT>stdout</TT> by <TT>mpp_exit</TT>.
580  !!
581  !! The flag <TT>MPP_CLOCK_DETAILED</TT> may be turned on by
582  !! <TT>mpp_clock_id</TT> to get detailed communication
583  !! profiles. Communication events of the types <TT>SEND, RECV, BROADCAST,
584  !! REDUCE</TT> and <TT>WAIT</TT> are separately measured for data volume
585  !! and time. Statistics are written to <TT>stdout</TT> by
586  !! <TT>mpp_exit</TT>, and individual PE info is also written to the file
587  !! <TT>mpp_clock.out.####</TT> where <TT>####</TT> is the PE id given by
588  !! <TT>mpp_pe</TT>.
589  !!
590  !! The flags <TT>MPP_CLOCK_SYNC</TT> and <TT>MPP_CLOCK_DETAILED</TT> are
591  !! integer parameters available by use association, and may be summed to
592  !! turn them both on.
593  !!
594  !! While the nesting of clocks is allowed, please note that turning on
595  !! the non-optional flags on inner clocks has certain subtle issues.
596  !! Turning on <TT>MPP_CLOCK_SYNC</TT> on an inner
597  !! clock may distort outer clock measurements of load imbalance. Turning
598  !! on <TT>MPP_CLOCK_DETAILED</TT> will stop detailed measurements on its
599  !! outer clock, since only one detailed clock may be active at one time.
600  !! Also, detailed clocks only time a certain number of events per clock
601  !! (currently 40000) to conserve memory. If this array overflows, a
602  !! warning message is printed, and subsequent events for this clock are
603  !! not timed.
604  !!
605  !! Timings are done using the <TT>f90</TT> standard
606  !! <TT>SYSTEM_CLOCK</TT> intrinsic.
607  !!
608  !! The resolution of SYSTEM_CLOCK is often too coarse for use except
609  !! across large swaths of code. On SGI systems this is transparently
610  !! overloaded with a higher resolution clock made available in a
611  !! non-portable fortran interface made available by
612  !! <TT>nsclock.c</TT>. This approach will eventually be extended to other
613  !! platforms.
614  !!
615  !! New behaviour added at the Havana release allows the user to embed
616  !! profiling calls at varying levels of granularity all over the code,
617  !! and for any particular run, set a threshold of granularity so that
618  !! finer-grained clocks become dormant.
619  !!
620  !! The threshold granularity is held in the private module variable
621  !! <TT>clock_grain</TT>. This value may be modified by the call
622  !! <TT>mpp_clock_set_grain</TT>, and affect clocks initiated by
623  !! subsequent calls to <TT>mpp_clock_id</TT>. The value of
624  !! <TT>clock_grain</TT> is set to an arbitrarily large number initially.
625  !!
626  !! Clocks initialized by <TT>mpp_clock_id</TT> can set a new optional
627  !! argument <TT>grain</TT> setting their granularity level. Clocks check
628  !! this level against the current value of <TT>clock_grain</TT>, and are
629  !! only triggered if they are <I>at or below ("coarser than")</I> the
630  !! threshold. Finer-grained clocks are dormant for that run.
631  !!
632  !!The following grain levels are pre-defined:
633  !!
634  !!<pre>
635  !!
636  !!
637  !! integer, parameter, public :: CLOCK_COMPONENT=1 !component level, e.g model, exchange
638  !! integer, parameter, public :: CLOCK_SUBCOMPONENT=11 !top level within a model component, e.g dynamics, physics
639  !! integer, parameter, public :: CLOCK_MODULE=21 !module level, e.g main subroutine of a physics module
640  !! integer, parameter, public :: CLOCK_ROUTINE=31 !level of individual subroutine or function
641  !! integer, parameter, public :: CLOCK_LOOP=41 !loops or blocks within a routine
642  !! integer, parameter, public :: CLOCK_INFRA=51 !infrastructure level, e.g halo update
643  !!</pre>
644  !!
645  !! Note that subsequent changes to <TT>clock_grain</TT> do not
646  !! change the status of already initiated clocks, and that if the
647  !! optional <TT>grain</TT> argument is absent, the clock is always
648  !! triggered. This guarantees backward compatibility.
649  subroutine mpp_clock_set_grain( grain )
650  integer, intent(in) :: grain
651  !set the granularity of times: only clocks whose grain is lower than
652  !clock_grain are triggered, finer-grained clocks are dormant.
653  !clock_grain is initialized to CLOCK_LOOP, so all clocks above the loop level
654  !are triggered if this is never called.
655  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_SET_GRAIN: You must first call mpp_init.' )
656 
657  clock_grain = grain
658  return
659  end subroutine mpp_clock_set_grain
660 
661  !#####################################################################
662  subroutine clock_init( id, name, flags, grain )
663  integer, intent(in) :: id
664  character(len=*), intent(in) :: name
665  integer, intent(in), optional :: flags, grain
666  integer :: i
667 
668  clocks(id)%name = name
669  clocks(id)%hits = 0
670  clocks(id)%tick = 0
671  clocks(id)%total_ticks = 0
672  clocks(id)%sync_on_begin = .false.
673  clocks(id)%detailed = .false.
674  clocks(id)%peset_num = current_peset_num
675  if( PRESENT(flags) )then
676  if( btest(flags,0) )clocks(id)%sync_on_begin = .true.
677  if( btest(flags,1) )clocks(id)%detailed = .true.
678  end if
679  clocks(id)%grain = 0
680  if( PRESENT(grain) )clocks(id)%grain = grain
681  if( clocks(id)%detailed )then
682  allocate( clocks(id)%events(max_event_types) )
683  clocks(id)%events(event_allreduce)%name = 'ALLREDUCE'
684  clocks(id)%events(event_broadcast)%name = 'BROADCAST'
685  clocks(id)%events(event_recv)%name = 'RECV'
686  clocks(id)%events(event_send)%name = 'SEND'
687  clocks(id)%events(event_wait)%name = 'WAIT'
688  do i=1,max_event_types
689  clocks(id)%events(i)%ticks(:) = 0
690  clocks(id)%events(i)%bytes(:) = 0
691  clocks(id)%events(i)%calls = 0
692  end do
693  clock_summary(id)%name = name
694  clock_summary(id)%event(event_allreduce)%name = 'ALLREDUCE'
695  clock_summary(id)%event(event_broadcast)%name = 'BROADCAST'
696  clock_summary(id)%event(event_recv)%name = 'RECV'
697  clock_summary(id)%event(event_send)%name = 'SEND'
698  clock_summary(id)%event(event_wait)%name = 'WAIT'
699  do i=1,max_event_types
700  clock_summary(id)%event(i)%msg_size_sums(:) = 0.0
701  clock_summary(id)%event(i)%msg_time_sums(:) = 0.0
702  clock_summary(id)%event(i)%total_data = 0.0
703  clock_summary(id)%event(i)%total_time = 0.0
704  clock_summary(id)%event(i)%msg_size_cnts(:) = 0
705  clock_summary(id)%event(i)%total_cnts = 0
706  end do
707  end if
708  return
709  end subroutine clock_init
710 
711  !#####################################################################
712  !> Return an ID for a new or existing clock
713  function mpp_clock_id( name, flags, grain )
714  integer :: mpp_clock_id
715  character(len=*), intent(in) :: name
716  integer, intent(in), optional :: flags, grain
717 
718  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_ID: You must first call mpp_init.')
719 
720  !if grain is present, the clock is only triggered if it
721  !is low ("coarse") enough: compared to clock_grain
722  !finer-grained clocks are dormant.
723  !if grain is absent, clock is triggered.
724  if( PRESENT(grain) )then
725  if( grain.GT.clock_grain )then
726  mpp_clock_id = 0
727  return
728  end if
729  end if
730  mpp_clock_id = 1
731 
732  if( clock_num.EQ.0 )then !first
733  clock_num = mpp_clock_id
734  call clock_init(mpp_clock_id,name,flags)
735  else
736  find_clock: do while( trim(name).NE.trim(clocks(mpp_clock_id)%name) )
738  if( mpp_clock_id.GT.clock_num )then
739  if( mpp_clock_id.GT.max_clocks )then
740  call mpp_error( fatal, 'MPP_CLOCK_ID: too many clock requests, ' // &
741  'check your clock id request or increase MAX_CLOCKS.')
742  else !new clock: initialize
743  clock_num = mpp_clock_id
744  call clock_init(mpp_clock_id,name,flags,grain)
745  exit find_clock
746  end if
747  end if
748  end do find_clock
749  endif
750  return
751  end function mpp_clock_id
752 
753  !#####################################################################
754  subroutine mpp_clock_begin(id)
755  integer, intent(in) :: id
756 
757  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: You must first call mpp_init.' )
758  if( .not. mpp_record_timing_data)return
759  if( id.EQ.0 )return
760  if( id.LT.0 .OR. id.GT.clock_num )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: invalid id.' )
761 
762 !$OMP MASTER
763  if( clocks(id)%peset_num.NE.current_peset_num ) &
764  call mpp_error( fatal, 'MPP_CLOCK_BEGIN: cannot change pelist context of a clock.' )
765  if( clocks(id)%is_on) call mpp_error(fatal, 'MPP_CLOCK_BEGIN: mpp_clock_begin is called again '// &
766  'before calling mpp_clock_end for the clock '//trim(clocks(id)%name) )
767  if( clocks(id)%sync_on_begin .OR. sync_all_clocks )then
768  !do an untimed sync at the beginning of the clock
769  !this puts all PEs in the current pelist on par, so that measurements begin together
770  !ending time will be different, thus measuring load imbalance for this clock.
771  call mpp_sync()
772  end if
773 
774  if (debug) then
775  num_clock_ids = num_clock_ids+1
776  if(num_clock_ids > max_clocks)call mpp_error(fatal,'MPP_CLOCK_BEGIN: max num previous_clock exceeded.' )
777  previous_clock(num_clock_ids) = current_clock
778  current_clock = id
779  endif
780  call system_clock( clocks(id)%tick )
781  clocks(id)%hits = clocks(id)%hits + 1
782  clocks(id)%is_on = .true.
783 !$OMP END MASTER
784  return
785  end subroutine mpp_clock_begin
786 
787  !#####################################################################
788  subroutine mpp_clock_end(id)
789  integer, intent(in) :: id
790  integer(i8_kind) :: delta
791  integer :: errunit
792 
793  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_END: You must first call mpp_init.' )
794  if( .not. mpp_record_timing_data)return
795  if( id.EQ.0 )return
796  if( id.LT.0 .OR. id.GT.clock_num )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: invalid id.' )
797 !$OMP MASTER
798  if( .NOT. clocks(id)%is_on) call mpp_error(fatal, 'MPP_CLOCK_END: mpp_clock_end is called '// &
799  'before calling mpp_clock_begin for the clock '//trim(clocks(id)%name) )
800 
801  call system_clock(end_tick)
802  if( clocks(id)%peset_num.NE.current_peset_num ) &
803  call mpp_error( fatal, 'MPP_CLOCK_END: cannot change pelist context of a clock.' )
804  delta = end_tick - clocks(id)%tick
805  if( delta.LT.0 )then
806  errunit = stderr()
807  write( errunit,* )'pe, id, start_tick, end_tick, delta, max_ticks=', pe, id, clocks(id)%tick, end_tick, &
808  & delta, max_ticks
809  delta = delta + max_ticks + 1
810  call mpp_error( warning, 'MPP_CLOCK_END: Clock rollover, assumed single roll.' )
811  end if
812  clocks(id)%total_ticks = clocks(id)%total_ticks + delta
813  if (debug) then
814  if(num_clock_ids < 1) call mpp_error(note,'MPP_CLOCK_END: min num previous_clock < 1.' )
815  current_clock = previous_clock(num_clock_ids)
816  num_clock_ids = num_clock_ids-1
817  endif
818  clocks(id)%is_on = .false.
819 !$OMP END MASTER
820  return
821  end subroutine mpp_clock_end
822 
823  !#####################################################################
824  subroutine mpp_record_time_start()
825 
826  mpp_record_timing_data = .true.
827 
828  end subroutine mpp_record_time_start
829 
830  !#####################################################################
831  subroutine mpp_record_time_end()
832 
833  mpp_record_timing_data = .false.
834 
835  end subroutine mpp_record_time_end
836 
837 
838  !#####################################################################
839  subroutine increment_current_clock( event_id, bytes )
840  integer, intent(in) :: event_id
841  integer, intent(in), optional :: bytes
842  integer :: n
843  integer(i8_kind) :: delta
844  integer :: errunit
845 
846  if( .not. mpp_record_timing_data )return
847  if( .not.debug .or. (current_clock.EQ.0) )return
848  if( current_clock.LT.0 .OR. current_clock.GT.clock_num )call mpp_error( fatal, &
849  & 'MPP_CLOCK_BEGIN: invalid current_clock.' )
850  if( .NOT.clocks(current_clock)%detailed )return
851  call system_clock(end_tick)
852  n = clocks(current_clock)%events(event_id)%calls + 1
853 
854  if( n.EQ.max_events )call mpp_error( warning, &
855  'MPP_CLOCK: events exceed MAX_EVENTS, ignore detailed profiling data for clock '// &
856  & trim(clocks(current_clock)%name) )
857  if( n.GT.max_events )return
858 
859  clocks(current_clock)%events(event_id)%calls = n
860  delta = end_tick - start_tick
861  if( delta.LT.0 )then
862  errunit = stderr()
863  write( errunit,* )'pe, event_id, start_tick, end_tick, delta, max_ticks=', &
864  pe, event_id, start_tick, end_tick, delta, max_ticks
865  delta = delta + max_ticks + 1
866  call mpp_error( warning, 'MPP_CLOCK_END: Clock rollover, assumed single roll.' )
867  end if
868  clocks(current_clock)%events(event_id)%ticks(n) = delta
869  if( PRESENT(bytes) )clocks(current_clock)%events(event_id)%bytes(n) = bytes
870  return
871  end subroutine increment_current_clock
872 
873  !#####################################################################
874 
875  subroutine dump_clock_summary()
876 
877  real :: total_time,total_time_all,total_data
878  real :: msg_size,eff_bw,s
879  integer :: sd_unit, total_calls
880  integer :: j,k,ct, msg_cnt
881  character(len=2) :: u
882  character(len=FMS_FILE_LEN) :: filename
883  character(len=20),dimension(MAX_BINS),save :: bin
884 
885  data bin( 1) /' 0 - 8 B: '/
886  data bin( 2) /' 8 - 16 B: '/
887  data bin( 3) /' 16 - 32 B: '/
888  data bin( 4) /' 32 - 64 B: '/
889  data bin( 5) /' 64 - 128 B: '/
890  data bin( 6) /'128 - 256 B: '/
891  data bin( 7) /'256 - 512 B: '/
892  data bin( 8) /'512 - 1024 B: '/
893  data bin( 9) /' 1.0 - 2.1 KB: '/
894  data bin(10) /' 2.1 - 4.1 KB: '/
895  data bin(11) /' 4.1 - 8.2 KB: '/
896  data bin(12) /' 8.2 - 16.4 KB: '/
897  data bin(13) /' 16.4 - 32.8 KB: '/
898  data bin(14) /' 32.8 - 65.5 KB: '/
899  data bin(15) /' 65.5 - 131.1 KB: '/
900  data bin(16) /'131.1 - 262.1 KB: '/
901  data bin(17) /'262.1 - 524.3 KB: '/
902  data bin(18) /'524.3 - 1048.6 KB: '/
903  data bin(19) /' 1.0 - 2.1 MB: '/
904  data bin(20) /' >2.1 MB: '/
905 
906  if( .NOT.any(clocks(1:clock_num)%detailed) )return
907  write( filename,'(a,i6.6)' )'mpp_clock.out.', pe
908 
909  open(newunit=sd_unit,file=trim(filename),form='formatted')
910 
911  comm_type: do ct = 1,clock_num
912 
913  if( .NOT.clocks(ct)%detailed )cycle
914  write(sd_unit,*) &
915  clock_summary(ct)%name(1:15),' Communication Data for PE ',pe
916 
917  write(sd_unit,*) ' '
918  write(sd_unit,*) ' '
919 
920  total_time_all = 0.0
921  event_type: do k = 1,max_event_types-1
922 
923  if(clock_summary(ct)%event(k)%total_time == 0.0)cycle
924 
925  total_time = clock_summary(ct)%event(k)%total_time
926  total_time_all = total_time_all + total_time
927  total_data = clock_summary(ct)%event(k)%total_data
928  total_calls = int(clock_summary(ct)%event(k)%total_cnts)
929 
930  write(sd_unit,1000) clock_summary(ct)%event(k)%name(1:9) // ':'
931 
932  write(sd_unit,1001) 'Total Data: ',total_data*1.0e-6, &
933  'MB; Total Time: ', total_time, &
934  'secs; Total Calls: ',total_calls
935 
936  write(sd_unit,*) ' '
937  write(sd_unit,1002) ' Bin Counts Avg Size Eff B/W'
938  write(sd_unit,*) ' '
939 
940  bin_loop: do j=1,max_bins
941 
942  if(clock_summary(ct)%event(k)%msg_size_cnts(j)==0)cycle
943 
944  if(j<=8)then
945  s = 1.0
946  u = ' B'
947  elseif(j<=18)then
948  s = 1.0e-3
949  u = 'KB'
950  else
951  s = 1.0e-6
952  u = 'MB'
953  endif
954 
955  msg_cnt = int(clock_summary(ct)%event(k)%msg_size_cnts(j))
956  msg_size = &
957  s*(clock_summary(ct)%event(k)%msg_size_sums(j)/real(msg_cnt))
958  eff_bw = (1.0e-6)*( clock_summary(ct)%event(k)%msg_size_sums(j) / &
959  clock_summary(ct)%event(k)%msg_time_sums(j) )
960 
961  write(sd_unit,1003) bin(j),msg_cnt,msg_size,u,eff_bw
962 
963  end do bin_loop
964 
965  write(sd_unit,*) ' '
966  write(sd_unit,*) ' '
967  end do event_type
968 
969  ! "Data-less" WAIT
970 
971  if(clock_summary(ct)%event(max_event_types)%total_time>0.0)then
972 
973  total_time = clock_summary(ct)%event(max_event_types)%total_time
974  total_time_all = total_time_all + total_time
975  total_calls = int(clock_summary(ct)%event(max_event_types)%total_cnts)
976 
977  write(sd_unit,1000) clock_summary(ct)%event(max_event_types)%name(1:9) // ':'
978 
979  write(sd_unit,1004) 'Total Calls: ',total_calls,'; Total Time: ', &
980  total_time,'secs'
981 
982  endif
983 
984  write(sd_unit,*) ' '
985  write(sd_unit,1005) 'Total communication time spent for ' // &
986  clock_summary(ct)%name(1:9) // ': ',total_time_all,'secs'
987  write(sd_unit,*) ' '
988  write(sd_unit,*) ' '
989  write(sd_unit,*) ' '
990 
991  end do comm_type
992 
993  close(sd_unit)
994 
995 1000 format(a)
996 1001 format(a,f8.2,a,f8.2,a,i6)
997 1002 format(a)
998 1003 format(a,i6,' ',' ',f9.1,a,' ',f9.2,'MB/sec')
999 1004 format(a,i8,a,f9.2,a)
1000 1005 format(a,f9.2,a)
1001  return
1002  end subroutine dump_clock_summary
1003 
1004  !#####################################################################
1005 
1006  integer function get_unit()
1007 
1008  integer,save :: i
1009  logical :: l_open
1010 
1011  if (pe == root_pe) call mpp_error(warning, &
1012  'get_unit is deprecated and will be removed in a future release, please use the Fortran intrinsic newunit')
1013  do i=10,99
1014  inquire(unit=i,opened=l_open)
1015  if(.not.l_open)exit
1016  end do
1017 
1018  if(i==100)then
1019  call mpp_error(fatal,'Unable to get I/O unit')
1020  else
1021  get_unit = i
1022  endif
1023 
1024  return
1025  end function get_unit
1026 
1027  !#####################################################################
1028 
1029  subroutine sum_clock_data()
1030 
1031  integer :: i,j,k,ct,event_size,event_cnt
1032  real :: msg_time
1033 
1034  clock_type: do ct=1,clock_num
1035  if( .NOT.clocks(ct)%detailed )cycle
1036  event_type: do j=1,max_event_types-1
1037  event_cnt = clocks(ct)%events(j)%calls
1038  event_summary: do i=1,event_cnt
1039 
1040  clock_summary(ct)%event(j)%total_cnts = &
1041  clock_summary(ct)%event(j)%total_cnts + 1
1042 
1043  event_size = int(clocks(ct)%events(j)%bytes(i))
1044 
1045  k = find_bin(event_size)
1046 
1047  clock_summary(ct)%event(j)%msg_size_cnts(k) = &
1048  clock_summary(ct)%event(j)%msg_size_cnts(k) + 1
1049 
1050  clock_summary(ct)%event(j)%msg_size_sums(k) = &
1051  clock_summary(ct)%event(j)%msg_size_sums(k) &
1052  + clocks(ct)%events(j)%bytes(i)
1053 
1054  clock_summary(ct)%event(j)%total_data = &
1055  clock_summary(ct)%event(j)%total_data &
1056  + clocks(ct)%events(j)%bytes(i)
1057 
1058  msg_time = clocks(ct)%events(j)%ticks(i)
1059  msg_time = tick_rate * real( clocks(ct)%events(j)%ticks(i) )
1060 
1061  clock_summary(ct)%event(j)%msg_time_sums(k) = &
1062  clock_summary(ct)%event(j)%msg_time_sums(k) + msg_time
1063 
1064  clock_summary(ct)%event(j)%total_time = &
1065  clock_summary(ct)%event(j)%total_time + msg_time
1066 
1067  end do event_summary
1068  end do event_type
1069 
1070  j = max_event_types ! WAITs
1071  ! "msg_size_cnts" doesn't really mean anything for WAIT
1072  ! but position will be used to store number of counts for now.
1073 
1074  event_cnt = clocks(ct)%events(j)%calls
1075  clock_summary(ct)%event(j)%msg_size_cnts(1) = event_cnt
1076  clock_summary(ct)%event(j)%total_cnts = event_cnt
1077 
1078  msg_time = tick_rate * real( sum( clocks(ct)%events(j)%ticks(1:event_cnt) ) )
1079  clock_summary(ct)%event(j)%msg_time_sums(1) = &
1080  clock_summary(ct)%event(j)%msg_time_sums(1) + msg_time
1081 
1082  clock_summary(ct)%event(j)%total_time = clock_summary(ct)%event(j)%msg_time_sums(1)
1083 
1084  end do clock_type
1085 
1086  return
1087  contains
1088  integer function find_bin(event_size)
1089 
1090  integer,intent(in) :: event_size
1091  integer :: k,msg_size
1092 
1093  msg_size = 8
1094  k = 1
1095  do while(event_size>msg_size .and. k<max_bins)
1096  k = k+1
1097  msg_size = msg_size*2
1098  end do
1099  find_bin = k
1100  return
1101  end function find_bin
1102 
1103  end subroutine sum_clock_data
1104 
1105  !#####################################################################
1106  !> This routine will double the size of peset and copy the original peset data
1107  !! into the expanded one. The maximum allowed to expand is PESET_MAX.
1108  subroutine expand_peset()
1109  integer :: old_peset_max,n
1110  type(communicator), allocatable :: peset_old(:)
1111 
1112  old_peset_max = current_peset_max
1113  if(old_peset_max .GE. peset_max) call mpp_error(fatal, &
1114  "mpp_mod(expand_peset): the number of peset reached PESET_MAX, increase PESET_MAX or contact developer")
1115 
1116  ! copy data to a tempoary data
1117  allocate(peset_old(0:old_peset_max))
1118  do n = 0, old_peset_max
1119  peset_old(n)%count = peset(n)%count
1120  peset_old(n)%id = peset(n)%id
1121  peset_old(n)%group = peset(n)%group
1122  peset_old(n)%name = peset(n)%name
1123  peset_old(n)%start = peset(n)%start
1124  peset_old(n)%log2stride = peset(n)%log2stride
1125 
1126  if( ASSOCIATED(peset(n)%list) ) then
1127  allocate(peset_old(n)%list(size(peset(n)%list(:))) )
1128  peset_old(n)%list(:) = peset(n)%list(:)
1129  deallocate(peset(n)%list)
1130  endif
1131  enddo
1132  deallocate(peset)
1133 
1134  ! create the new peset
1135  current_peset_max = min(peset_max, 2*old_peset_max)
1136  allocate(peset(0:current_peset_max))
1137  peset(:)%count = -1
1138  peset(:)%id = -1
1139  peset(:)%group = -1
1140  peset(:)%start = -1
1141  peset(:)%log2stride = -1
1142  peset(:)%name = " "
1143  do n = 0, old_peset_max
1144  peset(n)%count = peset_old(n)%count
1145  peset(n)%id = peset_old(n)%id
1146  peset(n)%group = peset_old(n)%group
1147  peset(n)%name = peset_old(n)%name
1148  peset(n)%start = peset_old(n)%start
1149  peset(n)%log2stride = peset_old(n)%log2stride
1150 
1151  if( ASSOCIATED(peset_old(n)%list) ) then
1152  allocate(peset(n)%list(size(peset_old(n)%list(:))) )
1153  peset(n)%list(:) = peset_old(n)%list(:)
1154  deallocate(peset_old(n)%list)
1155  endif
1156  enddo
1157  deallocate(peset_old)
1158 
1159  call mpp_error(note, "mpp_mod(expand_peset): size of peset is expanded to ", current_peset_max)
1160 
1161  end subroutine expand_peset
1162  !#####################################################################
1163 
1164  function uppercase (cs)
1165  character(len=*), intent(in) :: cs
1166  character(len=len(cs)),target :: uppercase
1167  integer :: k,tlen
1168  character, pointer :: ca
1169  integer, parameter :: co=iachar('A')-iachar('a') ! case offset
1170  !The transfer function truncates the string with xlf90_r
1171  tlen = len_trim(cs)
1172  if(tlen <= 0) then ! catch IBM compiler bug
1173  uppercase = cs ! simply return input blank string
1174  else
1175  uppercase = cs(1:tlen)
1176  do k=1, tlen
1177  ca => uppercase(k:k)
1178  if(ca >= "a" .and. ca <= "z") ca = achar(ichar(ca)+co)
1179  enddo
1180  endif
1181  end function uppercase
1182 
1183 !#######################################################################
1184 
1185  function lowercase (cs)
1186  character(len=*), intent(in) :: cs
1187  character(len=len(cs)),target :: lowercase
1188  integer, parameter :: co=iachar('a')-iachar('A') ! case offset
1189  integer :: k,tlen
1190  character, pointer :: ca
1191 ! The transfer function truncates the string with xlf90_r
1192  tlen = len_trim(cs)
1193  if(tlen <= 0) then ! catch IBM compiler bug
1194  lowercase = cs ! simply return input blank string
1195  else
1196  lowercase = cs(1:tlen)
1197  do k=1, tlen
1198  ca => lowercase(k:k)
1199  if(ca >= "A" .and. ca <= "Z") ca = achar(ichar(ca)+co)
1200  enddo
1201  endif
1202  end function lowercase
1203 
1204 
1205  !#######################################################################
1206 
1207 !-----------------------------------------------------------------------
1208 !
1209 ! AUTHOR: Rusty Benson (rusty.benson@noaa.gov)
1210 !
1211 !
1212 ! THESE LINES MUST BE PRESENT IN MPP.F90
1213 !
1214 ! ! public variable needed for reading an input nml file from an internal file
1215 ! character(len=:), dimension(:), allocatable, public :: input_nml_file
1216 !
1217 
1218 !-----------------------------------------------------------------------
1219 
1220 !> Reads an existing input nml file into a character array and broadcasts
1221 !! it to the non-root mpi-tasks. This allows the use of reads from an
1222 !! internal file for namelist settings (requires 2003 compliant compiler)
1223 !!
1224 !! read(input_nml_file, nml=<name_nml>, iostat=status)
1225 !!
1226 !!
1227  subroutine read_input_nml(pelist_name_in, alt_input_nml_path)
1228 
1229 ! Include variable "version" to be written to log file.
1230 #include<file_version.h>
1231 
1232  character(len=*), intent(in), optional :: pelist_name_in
1233  character(len=*), intent(in), optional :: alt_input_nml_path
1234 ! private variables
1235  integer :: log_unit
1236  integer :: i
1237  integer, dimension(2) :: lines_and_length
1238  logical :: file_exist
1239  character(len=len(peset(current_peset_num)%name)) :: pelist_name
1240  character(len=FMS_PATH_LEN) :: filename
1241 
1242 ! check the status of input_nml_file
1243  if ( allocated(input_nml_file) ) then
1244  deallocate(input_nml_file)
1245  endif
1246 
1247 ! the following code is necessary for using alternate namelist files (nests, stretched grids, etc)
1248  if (PRESENT(pelist_name_in)) then
1249  ! test to make sure length of pelist_name_in is <= pelist_name
1250  if (len(pelist_name_in) > len(pelist_name)) then
1251  call mpp_error(fatal, &
1252  "mpp_util.inc: read_input_nml optional argument pelist_name_in has size greater than local pelist_name")
1253  else
1254  pelist_name = pelist_name_in
1255  endif
1256  else
1257  pelist_name = mpp_get_current_pelist_name()
1258  endif
1259  filename='input_'//trim(pelist_name)//'.nml'
1260  inquire(file=filename, exist=file_exist)
1261  if (.not. file_exist ) then
1262  if (present(alt_input_nml_path)) then
1263  filename = alt_input_nml_path
1264  else
1265  filename = 'input.nml'
1266  end if
1267  endif
1268  lines_and_length = get_ascii_file_num_lines_and_length(filename)
1269  allocate(character(len=lines_and_length(2))::input_nml_file(lines_and_length(1)))
1270  call read_ascii_file(filename, lines_and_length(2), input_nml_file)
1271 
1272 ! write info logfile
1273  if (pe == root_pe) then
1274  log_unit = stdlog()
1275  write(log_unit,'(a)') '========================================================================'
1276  write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(version)
1277  write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(filename)//' '
1278  do i = 1, lines_and_length(1)
1279  write(log_unit,*) trim(input_nml_file(i))
1280  enddo
1281  end if
1282  end subroutine read_input_nml
1283 
1284 
1285  !#######################################################################
1286  !z1l: This is extracted from read_ascii_file
1287  function get_ascii_file_num_lines(FILENAME, LENGTH, PELIST)
1288  character(len=*), intent(in) :: FILENAME
1289  integer, intent(in) :: LENGTH
1290  integer, intent(in), optional, dimension(:) :: PELIST
1291 
1292  integer :: num_lines, get_ascii_file_num_lines
1293  character(len=LENGTH) :: str_tmp
1294  character(len=5) :: text
1295  integer :: status, f_unit, from_pe
1296  logical :: file_exist
1297 
1298  if( read_ascii_file_on) then
1299  call mpp_error(fatal, &
1300  "mpp_util.inc: get_ascii_file_num_lines is called again before calling read_ascii_file")
1301  endif
1302  read_ascii_file_on = .true.
1303 
1304  from_pe = root_pe
1305  get_ascii_file_num_lines = -1
1306  num_lines = -1
1307  if ( pe == root_pe ) then
1308  inquire(file=filename, exist=file_exist)
1309 
1310  if ( file_exist ) then
1311  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1312 
1313  if ( status .ne. 0 ) then
1314  write (unit=text, fmt='(I5)') status
1315  call mpp_error(fatal, 'get_ascii_file_num_lines: Error opening file:' //trim(filename)// &
1316  '. (IOSTAT = '//trim(text)//')')
1317  else
1318  num_lines = 1
1319  do
1320  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1321  if ( status .lt. 0 ) then
1322  ! deprecate num_lines by 1 and ensure num_lines is at least 1
1323  num_lines = max(num_lines - 1, 1)
1324  exit
1325  endif
1326  if ( status .gt. 0 ) then
1327  write (unit=text, fmt='(I5)') num_lines
1328  call mpp_error(fatal, 'get_ascii_file_num_lines: Error reading line '//trim(text)// &
1329  ' in file '//trim(filename)//'.')
1330  end if
1331  if ( len_trim(str_tmp) == length ) then
1332  write(unit=text, fmt='(I5)') length
1333  call mpp_error(fatal, 'get_ascii_file_num_lines: Length of output string ('//trim(text)//&
1334  & ' is too small. Increase the LENGTH value.')
1335  end if
1336  num_lines = num_lines + 1
1337  end do
1338  close(unit=f_unit)
1339  end if
1340  else
1341  call mpp_error(fatal, 'get_ascii_file_num_lines: File '//trim(filename)//' does not exist.')
1342  end if
1343  end if
1344 
1345  ! Broadcast number of lines
1346  call mpp_broadcast(num_lines, from_pe, pelist=pelist)
1347  get_ascii_file_num_lines = num_lines
1348 
1349  end function get_ascii_file_num_lines
1350 
1351  !#######################################################################
1352  !> @brief Function to determine the maximum line length and number of lines from an ascii file
1353  function get_ascii_file_num_lines_and_length(FILENAME, PELIST)
1354  character(len=*), intent(in) :: filename !< name of the file to be read
1355  integer, intent(in), optional, dimension(:) :: pelist !< optional pelist
1356 
1357  integer, dimension(2) :: get_ascii_file_num_lines_and_length !< number of lines (1) and
1358  !! max line length (2)
1359  integer :: num_lines, max_length
1360  integer, parameter :: length=1024
1361  character(len=LENGTH) :: str_tmp
1362  character(len=5) :: text
1363  integer :: status, f_unit, from_pe
1364  logical :: file_exist
1365 
1366  if( read_ascii_file_on) then
1367  call mpp_error(fatal, &
1368  "mpp_util.inc: get_ascii_file_num_lines is called again before calling read_ascii_file")
1369  endif
1370  read_ascii_file_on = .true.
1371 
1372  from_pe = root_pe
1374  num_lines = -1
1375  max_length = -1
1376  if ( pe == root_pe ) then
1377  inquire(file=filename, exist=file_exist)
1378 
1379  if ( file_exist ) then
1380  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1381 
1382  if ( status .ne. 0 ) then
1383  write (unit=text, fmt='(I5)') status
1384  call mpp_error(fatal, 'get_ascii_file_num_lines: Error opening file:' //trim(filename)// &
1385  '. (IOSTAT = '//trim(text)//')')
1386  else
1387  num_lines = 1
1388  max_length = 1
1389  do
1390  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1391  if ( status .lt. 0 ) then
1392  ! deprecate num_lines by 1 and ensure num_lines is at least 1
1393  num_lines = max(num_lines - 1, 1)
1394  exit
1395  endif
1396  if ( status .gt. 0 ) then
1397  write (unit=text, fmt='(I5)') num_lines
1398  call mpp_error(fatal, 'get_ascii_file_num_lines: Error reading line '//trim(text)// &
1399  ' in file '//trim(filename)//'.')
1400  end if
1401  if ( len_trim(str_tmp) == length) then
1402  write(unit=text, fmt='(I5)') length
1403  call mpp_error(fatal, 'get_ascii_file_num_lines: Length of output string ('//trim(text)//&
1404  & ' is too small. Increase the LENGTH value.')
1405  end if
1406  if (len_trim(str_tmp) > max_length) max_length = len_trim(str_tmp)
1407  num_lines = num_lines + 1
1408  end do
1409  close(unit=f_unit)
1410  end if
1411  else
1412  call mpp_error(fatal, 'get_ascii_file_num_lines: File '//trim(filename)//' does not exist.')
1413  end if
1414  max_length = max_length+1
1415  end if
1416 
1417  ! Broadcast number of lines
1418  call mpp_broadcast(num_lines, from_pe, pelist=pelist)
1419  call mpp_broadcast(max_length, from_pe, pelist=pelist)
1421  get_ascii_file_num_lines_and_length(2) = max_length
1422 
1424 
1425  !-----------------------------------------------------------------------
1426  !
1427  ! AUTHOR: Rusty Benson <rusty.benson@noaa.gov>,
1428  ! Seth Underwood <Seth.Underwood@noaa.gov>
1429  !
1430  !-----------------------------------------------------------------------
1431  ! subroutine READ_ASCII_FILE
1432  !
1433  !
1434  !> Reads any ascii file into a character array and broadcasts
1435  !! it to the non-root mpi-tasks. Based off READ_INPUT_NML.
1436  !!
1437  !! Passed in 'Content' array, must be of the form:
1438  !! character(len=LENGTH), dimension(:), allocatable :: array_name
1439  !!
1440  !! Reads from this array must be done in a do loop over the number of
1441  !! lines, i.e.:
1442  !!
1443  !! do i=1, num_lines
1444  !! read (UNIT=array_name(i), FMT=*) var1, var2, ...
1445  !! end do
1446  subroutine read_ascii_file(FILENAME, LENGTH, Content, PELIST)
1447  character(len=*), intent(in) :: FILENAME
1448  integer, intent(in) :: LENGTH
1449  character(len=*), intent(inout), dimension(:) :: Content
1450  integer, intent(in), optional, dimension(:) :: PELIST
1451 
1452  ! Include variable "version" to be written to log file.
1453 #include<file_version.h>
1454 
1455  character(len=5) :: text
1456  logical :: file_exist
1457  integer :: status, f_unit, log_unit
1458  integer :: from_pe
1459  integer :: pnum_lines, num_lines
1460  character(len=LENGTH) :: str_tmp !< Temporary variable to store line from file
1461 
1462  if( .NOT. read_ascii_file_on) then
1463  call mpp_error(fatal, &
1464  "mpp_util.inc: get_ascii_file_num_lines needs to be called before calling read_ascii_file")
1465  endif
1466  read_ascii_file_on = .false.
1467 
1468  from_pe = root_pe
1469  num_lines = size(content(:))
1470 
1471  if ( pe == root_pe ) then
1472  ! write info logfile
1473  log_unit = stdlog()
1474  write(log_unit,'(a)') '========================================================================'
1475  write(log_unit,'(a)') 'READ_ASCII_FILE: '//trim(version)
1476  write(log_unit,'(a)') 'READ_ASCII_FILE: File: '//trim(filename)
1477 
1478  inquire(file=filename, exist=file_exist)
1479 
1480  if ( file_exist ) then
1481  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1482 
1483  if ( status .ne. 0 ) then
1484  write (unit=text, fmt='(I5)') status
1485  call mpp_error(fatal, 'READ_ASCII_FILE: Error opening file: '// &
1486  & trim(filename)//'. (IOSTAT = '//trim(text)//')')
1487  else
1488 
1489  if ( num_lines .gt. 0 ) then
1490  content(:) = ' '
1491 
1492  rewind(unit=f_unit, iostat=status)
1493  if ( status .ne. 0 ) then
1494  write (unit=text, fmt='(I5)') status
1495  call mpp_error(fatal, 'READ_ASCII_FILE: Unable to re-read file '//trim(filename)//'. (IOSTAT = '&
1496  //trim(text)//'.')
1497  else
1498  ! A second 'sanity' check on the file
1499  pnum_lines = 1
1500 
1501  do
1502  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1503 
1504  if ( status .lt. 0 ) then
1505  ! deprecate pnum_lines by 1 and ensure pnum_lines is at least 1
1506  pnum_lines = max(pnum_lines - 1, 1)
1507  exit
1508  endif
1509  if ( status .gt. 0 ) then
1510  write (unit=text, fmt='(I5)') pnum_lines
1511  call mpp_error(fatal, 'READ_ASCII_FILE: Error reading line '// &
1512  & trim(text)//' in file '//trim(filename)//'.')
1513  end if
1514  if(pnum_lines > num_lines) then
1515  call mpp_error(fatal, 'READ_ASCII_FILE: number of lines in file '//trim(filename)// &
1516  ' is greater than size(Content(:)). ')
1517  end if
1518  if ( len_trim(str_tmp) == length ) then
1519  write(unit=text, fmt='(I5)') length
1520  call mpp_error(fatal, 'READ_ASCII_FILE: Length of output string ('//trim(text)// &
1521  & ' is too small. Increase the LENGTH value.')
1522  end if
1523  content(pnum_lines) = str_tmp
1524  pnum_lines = pnum_lines + 1
1525  end do
1526  if(num_lines .NE. pnum_lines) then
1527  call mpp_error(fatal, 'READ_ASCII_FILE: number of lines in file '//trim(filename)// &
1528  ' does not equal to size(Content(:)) ' )
1529  end if
1530  end if
1531  end if
1532  close(unit=f_unit)
1533  end if
1534  else
1535  call mpp_error(fatal, 'READ_ASCII_FILE: File '//trim(filename)//' does not exist.')
1536  end if
1537  end if
1538 
1539  ! Broadcast character array
1540  call mpp_broadcast(content, length, from_pe, pelist=pelist)
1541 
1542  end subroutine read_ascii_file
1543 !> @}
subroutine mpp_error_basic(errortype, errormsg)
A very basic error handler uses ABORT and FLUSH calls, may need to use cpp to rename.
integer function stdout()
This function returns the current standard fortran unit numbers for output.
Definition: mpp_util.inc:43
subroutine read_ascii_file(FILENAME, LENGTH, Content, PELIST)
Reads any ascii file into a character array and broadcasts it to the non-root mpi-tasks....
Definition: mpp_util.inc:1447
subroutine mpp_init_warninglog()
Opens the warning log file, called during mpp_init.
Definition: mpp_util.inc:125
subroutine mpp_error_mesg(routine, errormsg, errortype)
overloads to mpp_error_basic, support for error_mesg routine in FMS
Definition: mpp_util.inc:175
subroutine mpp_set_current_pelist(pelist, no_sync)
Set context pelist.
Definition: mpp_util.inc:499
integer function stderr()
This function returns the current standard fortran unit numbers for error messages.
Definition: mpp_util.inc:51
subroutine read_input_nml(pelist_name_in, alt_input_nml_path)
Reads an existing input nml file into a character array and broadcasts it to the non-root mpi-tasks....
Definition: mpp_util.inc:1228
subroutine mpp_clock_set_grain(grain)
Set the level of granularity of timing measurements.
Definition: mpp_util.inc:650
subroutine mpp_declare_pelist(pelist, name, commID)
Declare a pelist.
Definition: mpp_util.inc:470
integer function stdlog()
This function returns the current standard fortran unit numbers for log messages. Log messages,...
Definition: mpp_util.inc:59
integer function mpp_npes()
Returns processor count for current pelist.
Definition: mpp_util.inc:421
integer function, dimension(2) get_ascii_file_num_lines_and_length(FILENAME, PELIST)
Function to determine the maximum line length and number of lines from an ascii file.
Definition: mpp_util.inc:1354
integer function mpp_pe()
Returns processor ID.
Definition: mpp_util.inc:407
subroutine mpp_sync(pelist, do_self)
Synchronize PEs in list.
integer function mpp_clock_id(name, flags, grain)
Return an ID for a new or existing clock.
Definition: mpp_util.inc:714
integer function warnlog()
This function returns unit number for the warning log if on the root pe, otherwise returns the etc_un...
Definition: mpp_util.inc:141
integer function stdin()
This function returns the current standard fortran unit numbers for input.
Definition: mpp_util.inc:36
subroutine expand_peset()
This routine will double the size of peset and copy the original peset data into the expanded one....
Definition: mpp_util.inc:1109