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