FMS  2024.03
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  subroutine mpp_set_root_pe(num)
439  integer, intent(in) :: num
440 
441  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_SET_ROOT_PE: You must first call mpp_init.' )
442  if( .NOT.(any(num.EQ.peset(current_peset_num)%list(:))) ) &
443  call mpp_error( fatal, 'MPP_SET_ROOT_PE: you cannot set a root PE outside the current pelist.' )
444  root_pe = num
445  return
446  end subroutine mpp_set_root_pe
447 
448  !> @brief Declare a pelist.
449  !!
450  !> This call is written specifically to accommodate a MPI restriction
451  !! that requires a parent communicator to create a child communicator, In
452  !! other words: a pelist cannot go off and declare a communicator, but
453  !! every PE in the parent, including those not in pelist(:), must get
454  !! together for the <TT>MPI_COMM_CREATE</TT> call. The parent is
455  !! typically <TT>MPI_COMM_WORLD</TT>, though it could also be a subset
456  !! that includes all PEs in <TT>pelist</TT>.
457  !!
458  !! This call implies synchronization across the PEs in the current
459  !! pelist, of which <TT>pelist</TT> is a subset.
460  subroutine mpp_declare_pelist( pelist, name, commID )
461  integer, intent(in) :: pelist(:) !> pelist you are declaring and storing within FMS
462  character(len=*), intent(in), optional :: name !> unique name for an input pelist
463  integer, intent(out), optional :: commID !> return of current MPI comm group communicator ID
464  integer :: i
465 
466  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_DECLARE_PELIST: You must first call mpp_init.' )
467  i = get_peset(pelist)
468  write( peset(i)%name,'(a,i2.2)' ) 'PElist', i !default name
469  if( PRESENT(name) )peset(i)%name = name
470  if( PRESENT(commid) )commid = peset(i)%id
471 
472  return
473  end subroutine mpp_declare_pelist
474 
475  !#####################################################################
476 
477  !> @brief Set context pelist
478  !!
479  !! This call sets the value of the current pelist, which is the
480  !! context for all subsequent "global" calls where the optional
481  !! <TT>pelist</TT> argument is omitted. All the PEs that are to be in the
482  !! current pelist must call it.
483  !!
484  !! In MPI, this call may hang unless <TT>pelist</TT> has been previous
485  !! declared using @ref mpp_declare_pelist
486  !!
487  !! If the argument <TT>pelist</TT> is absent, the current pelist is
488  !! set to the "world" pelist, of all PEs in the job.
489  subroutine mpp_set_current_pelist( pelist, no_sync )
490  !Once we branch off into a PE subset, we want subsequent "global" calls to
491  !sync only across this subset. This is declared as the current pelist (peset(current_peset_num)%list)
492  !when current_peset all pelist ops with no pelist should apply the current pelist.
493  !also, we set the start PE in this pelist to be the root_pe.
494  !unlike mpp_declare_pelist, this is called by the PEs in the pelist only
495  !so if the PEset has not been previously declared, this will hang in MPI.
496  !if pelist is omitted, we reset pelist to the world pelist.
497  integer, intent(in), optional :: pelist(:)
498  logical, intent(in), optional :: no_sync
499 
500  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_SET_CURRENT_PELIST: You must first call mpp_init.' )
501  if( PRESENT(pelist) )then
502  if( .NOT.any(pe.EQ.pelist) )call mpp_error( fatal, 'MPP_SET_CURRENT_PELIST: pe must be in pelist.' )
503  current_peset_num = get_peset(pelist)
504  else
505  current_peset_num = world_peset_num
506  end if
507  call mpp_set_root_pe( minval(peset(current_peset_num)%list(:)) )
508  if(.not.PRESENT(no_sync))call mpp_sync() !this is called to make sure everyone in the current pelist is here.
509  ! npes = mpp_npes()
510  return
511  end subroutine mpp_set_current_pelist
512 
513  !#####################################################################
514  function mpp_get_current_pelist_name()
515  ! Simply return the current pelist name
516  character(len=len(peset(current_peset_num)%name)) :: mpp_get_current_pelist_name
517 
518  mpp_get_current_pelist_name = peset(current_peset_num)%name
519  end function mpp_get_current_pelist_name
520 
521  !#####################################################################
522  !this is created for use by mpp_define_domains within a pelist
523  !will be published but not publicized
524  subroutine mpp_get_current_pelist( pelist, name, commID )
525  integer, intent(out) :: pelist(:)
526  character(len=*), intent(out), optional :: name
527  integer, intent(out), optional :: commID
528 
529  if( size(pelist(:)).NE.size(peset(current_peset_num)%list(:)) ) &
530  call mpp_error( fatal, 'MPP_GET_CURRENT_PELIST: size(pelist) is wrong.' )
531  pelist(:) = peset(current_peset_num)%list(:)
532  if( PRESENT(name) )name = peset(current_peset_num)%name
533 #ifdef use_libMPI
534  if( PRESENT(commid) )commid = peset(current_peset_num)%id
535 #endif
536 
537  return
538  end subroutine mpp_get_current_pelist
539 
540 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
541  ! !
542  ! PERFORMANCE PROFILING CALLS !
543  ! !
544 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
545 !predefined clock granularities, but you can use any integer!using CLOCK_LOOP and above may distort coarser-grain measurements
546  !> @brief Set the level of granularity of timing measurements.
547  !!
548  !> This routine and three other routines, mpp_clock_id, mpp_clock_begin(id),
549  !! and mpp_clock_end(id) may be used to time parallel code sections, and
550  !! extract parallel statistics. Clocks are identified by names, which
551  !! should be unique in the first 32 characters. The <TT>mpp_clock_id</TT>
552  !! call initializes a clock of a given name and returns an integer
553  !! <TT>id</TT>. This <TT>id</TT> can be used by subsequent
554  !! <TT>mpp_clock_begin</TT> and <TT>mpp_clock_end</TT> calls set around a
555  !! code section to be timed. Example:
556  !! <PRE>
557  !! integer :: id
558  !! id = mpp_clock_id( 'Atmosphere' )
559  !! call mpp_clock_begin(id)
560  !! call atmos_model()
561  !! call mpp_clock_end()
562  !! </PRE>
563  !! Two flags may be used to alter the behaviour of
564  !! <TT>mpp_clock</TT>. If the flag <TT>MPP_CLOCK_SYNC</TT> is turned on
565  !! by <TT>mpp_clock_id</TT>, the clock calls <TT>mpp_sync</TT> across all
566  !! the PEs in the current pelist at the top of the timed code section,
567  !! but allows each PE to complete the code section (and reach
568  !! <TT>mpp_clock_end</TT>) at different times. This allows us to measure
569  !! load imbalance for a given code section. Statistics are written to
570  !! <TT>stdout</TT> by <TT>mpp_exit</TT>.
571  !!
572  !! The flag <TT>MPP_CLOCK_DETAILED</TT> may be turned on by
573  !! <TT>mpp_clock_id</TT> to get detailed communication
574  !! profiles. Communication events of the types <TT>SEND, RECV, BROADCAST,
575  !! REDUCE</TT> and <TT>WAIT</TT> are separately measured for data volume
576  !! and time. Statistics are written to <TT>stdout</TT> by
577  !! <TT>mpp_exit</TT>, and individual PE info is also written to the file
578  !! <TT>mpp_clock.out.####</TT> where <TT>####</TT> is the PE id given by
579  !! <TT>mpp_pe</TT>.
580  !!
581  !! The flags <TT>MPP_CLOCK_SYNC</TT> and <TT>MPP_CLOCK_DETAILED</TT> are
582  !! integer parameters available by use association, and may be summed to
583  !! turn them both on.
584  !!
585  !! While the nesting of clocks is allowed, please note that turning on
586  !! the non-optional flags on inner clocks has certain subtle issues.
587  !! Turning on <TT>MPP_CLOCK_SYNC</TT> on an inner
588  !! clock may distort outer clock measurements of load imbalance. Turning
589  !! on <TT>MPP_CLOCK_DETAILED</TT> will stop detailed measurements on its
590  !! outer clock, since only one detailed clock may be active at one time.
591  !! Also, detailed clocks only time a certain number of events per clock
592  !! (currently 40000) to conserve memory. If this array overflows, a
593  !! warning message is printed, and subsequent events for this clock are
594  !! not timed.
595  !!
596  !! Timings are done using the <TT>f90</TT> standard
597  !! <TT>SYSTEM_CLOCK</TT> intrinsic.
598  !!
599  !! The resolution of SYSTEM_CLOCK is often too coarse for use except
600  !! across large swaths of code. On SGI systems this is transparently
601  !! overloaded with a higher resolution clock made available in a
602  !! non-portable fortran interface made available by
603  !! <TT>nsclock.c</TT>. This approach will eventually be extended to other
604  !! platforms.
605  !!
606  !! New behaviour added at the Havana release allows the user to embed
607  !! profiling calls at varying levels of granularity all over the code,
608  !! and for any particular run, set a threshold of granularity so that
609  !! finer-grained clocks become dormant.
610  !!
611  !! The threshold granularity is held in the private module variable
612  !! <TT>clock_grain</TT>. This value may be modified by the call
613  !! <TT>mpp_clock_set_grain</TT>, and affect clocks initiated by
614  !! subsequent calls to <TT>mpp_clock_id</TT>. The value of
615  !! <TT>clock_grain</TT> is set to an arbitrarily large number initially.
616  !!
617  !! Clocks initialized by <TT>mpp_clock_id</TT> can set a new optional
618  !! argument <TT>grain</TT> setting their granularity level. Clocks check
619  !! this level against the current value of <TT>clock_grain</TT>, and are
620  !! only triggered if they are <I>at or below ("coarser than")</I> the
621  !! threshold. Finer-grained clocks are dormant for that run.
622  !!
623  !!The following grain levels are pre-defined:
624  !!
625  !!<pre>
626  !!
627  !!
628  !! integer, parameter, public :: CLOCK_COMPONENT=1 !component level, e.g model, exchange
629  !! integer, parameter, public :: CLOCK_SUBCOMPONENT=11 !top level within a model component, e.g dynamics, physics
630  !! integer, parameter, public :: CLOCK_MODULE=21 !module level, e.g main subroutine of a physics module
631  !! integer, parameter, public :: CLOCK_ROUTINE=31 !level of individual subroutine or function
632  !! integer, parameter, public :: CLOCK_LOOP=41 !loops or blocks within a routine
633  !! integer, parameter, public :: CLOCK_INFRA=51 !infrastructure level, e.g halo update
634  !!</pre>
635  !!
636  !! Note that subsequent changes to <TT>clock_grain</TT> do not
637  !! change the status of already initiated clocks, and that if the
638  !! optional <TT>grain</TT> argument is absent, the clock is always
639  !! triggered. This guarantees backward compatibility.
640  subroutine mpp_clock_set_grain( grain )
641  integer, intent(in) :: grain
642  !set the granularity of times: only clocks whose grain is lower than
643  !clock_grain are triggered, finer-grained clocks are dormant.
644  !clock_grain is initialized to CLOCK_LOOP, so all clocks above the loop level
645  !are triggered if this is never called.
646  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_SET_GRAIN: You must first call mpp_init.' )
647 
648  clock_grain = grain
649  return
650  end subroutine mpp_clock_set_grain
651 
652  !#####################################################################
653  subroutine clock_init( id, name, flags, grain )
654  integer, intent(in) :: id
655  character(len=*), intent(in) :: name
656  integer, intent(in), optional :: flags, grain
657  integer :: i
658 
659  clocks(id)%name = name
660  clocks(id)%hits = 0
661  clocks(id)%tick = 0
662  clocks(id)%total_ticks = 0
663  clocks(id)%sync_on_begin = .false.
664  clocks(id)%detailed = .false.
665  clocks(id)%peset_num = current_peset_num
666  if( PRESENT(flags) )then
667  if( btest(flags,0) )clocks(id)%sync_on_begin = .true.
668  if( btest(flags,1) )clocks(id)%detailed = .true.
669  end if
670  clocks(id)%grain = 0
671  if( PRESENT(grain) )clocks(id)%grain = grain
672  if( clocks(id)%detailed )then
673  allocate( clocks(id)%events(max_event_types) )
674  clocks(id)%events(event_allreduce)%name = 'ALLREDUCE'
675  clocks(id)%events(event_broadcast)%name = 'BROADCAST'
676  clocks(id)%events(event_recv)%name = 'RECV'
677  clocks(id)%events(event_send)%name = 'SEND'
678  clocks(id)%events(event_wait)%name = 'WAIT'
679  do i=1,max_event_types
680  clocks(id)%events(i)%ticks(:) = 0
681  clocks(id)%events(i)%bytes(:) = 0
682  clocks(id)%events(i)%calls = 0
683  end do
684  clock_summary(id)%name = name
685  clock_summary(id)%event(event_allreduce)%name = 'ALLREDUCE'
686  clock_summary(id)%event(event_broadcast)%name = 'BROADCAST'
687  clock_summary(id)%event(event_recv)%name = 'RECV'
688  clock_summary(id)%event(event_send)%name = 'SEND'
689  clock_summary(id)%event(event_wait)%name = 'WAIT'
690  do i=1,max_event_types
691  clock_summary(id)%event(i)%msg_size_sums(:) = 0.0
692  clock_summary(id)%event(i)%msg_time_sums(:) = 0.0
693  clock_summary(id)%event(i)%total_data = 0.0
694  clock_summary(id)%event(i)%total_time = 0.0
695  clock_summary(id)%event(i)%msg_size_cnts(:) = 0
696  clock_summary(id)%event(i)%total_cnts = 0
697  end do
698  end if
699  return
700  end subroutine clock_init
701 
702  !#####################################################################
703  !> Return an ID for a new or existing clock
704  function mpp_clock_id( name, flags, grain )
705  integer :: mpp_clock_id
706  character(len=*), intent(in) :: name
707  integer, intent(in), optional :: flags, grain
708 
709  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_ID: You must first call mpp_init.')
710 
711  !if grain is present, the clock is only triggered if it
712  !is low ("coarse") enough: compared to clock_grain
713  !finer-grained clocks are dormant.
714  !if grain is absent, clock is triggered.
715  if( PRESENT(grain) )then
716  if( grain.GT.clock_grain )then
717  mpp_clock_id = 0
718  return
719  end if
720  end if
721  mpp_clock_id = 1
722 
723  if( clock_num.EQ.0 )then !first
724  clock_num = mpp_clock_id
725  call clock_init(mpp_clock_id,name,flags)
726  else
727  find_clock: do while( trim(name).NE.trim(clocks(mpp_clock_id)%name) )
729  if( mpp_clock_id.GT.clock_num )then
730  if( mpp_clock_id.GT.max_clocks )then
731  call mpp_error( fatal, 'MPP_CLOCK_ID: too many clock requests, ' // &
732  'check your clock id request or increase MAX_CLOCKS.')
733  else !new clock: initialize
734  clock_num = mpp_clock_id
735  call clock_init(mpp_clock_id,name,flags,grain)
736  exit find_clock
737  end if
738  end if
739  end do find_clock
740  endif
741  return
742  end function mpp_clock_id
743 
744  !#####################################################################
745  subroutine mpp_clock_begin(id)
746  integer, intent(in) :: id
747 
748  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: You must first call mpp_init.' )
749  if( .not. mpp_record_timing_data)return
750  if( id.EQ.0 )return
751  if( id.LT.0 .OR. id.GT.clock_num )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: invalid id.' )
752 
753 !$OMP MASTER
754  if( clocks(id)%peset_num.NE.current_peset_num ) &
755  call mpp_error( fatal, 'MPP_CLOCK_BEGIN: cannot change pelist context of a clock.' )
756  if( clocks(id)%is_on) call mpp_error(fatal, 'MPP_CLOCK_BEGIN: mpp_clock_begin is called again '// &
757  'before calling mpp_clock_end for the clock '//trim(clocks(id)%name) )
758  if( clocks(id)%sync_on_begin .OR. sync_all_clocks )then
759  !do an untimed sync at the beginning of the clock
760  !this puts all PEs in the current pelist on par, so that measurements begin together
761  !ending time will be different, thus measuring load imbalance for this clock.
762  call mpp_sync()
763  end if
764 
765  if (debug) then
766  num_clock_ids = num_clock_ids+1
767  if(num_clock_ids > max_clocks)call mpp_error(fatal,'MPP_CLOCK_BEGIN: max num previous_clock exceeded.' )
768  previous_clock(num_clock_ids) = current_clock
769  current_clock = id
770  endif
771  call system_clock( clocks(id)%tick )
772  clocks(id)%hits = clocks(id)%hits + 1
773  clocks(id)%is_on = .true.
774 !$OMP END MASTER
775  return
776  end subroutine mpp_clock_begin
777 
778  !#####################################################################
779  subroutine mpp_clock_end(id)
780  integer, intent(in) :: id
781  integer(i8_kind) :: delta
782  integer :: errunit
783 
784  if( .NOT.module_is_initialized )call mpp_error( fatal, 'MPP_CLOCK_END: You must first call mpp_init.' )
785  if( .not. mpp_record_timing_data)return
786  if( id.EQ.0 )return
787  if( id.LT.0 .OR. id.GT.clock_num )call mpp_error( fatal, 'MPP_CLOCK_BEGIN: invalid id.' )
788 !$OMP MASTER
789  if( .NOT. clocks(id)%is_on) call mpp_error(fatal, 'MPP_CLOCK_END: mpp_clock_end is called '// &
790  'before calling mpp_clock_begin for the clock '//trim(clocks(id)%name) )
791 
792  call system_clock(end_tick)
793  if( clocks(id)%peset_num.NE.current_peset_num ) &
794  call mpp_error( fatal, 'MPP_CLOCK_END: cannot change pelist context of a clock.' )
795  delta = end_tick - clocks(id)%tick
796  if( delta.LT.0 )then
797  errunit = stderr()
798  write( errunit,* )'pe, id, start_tick, end_tick, delta, max_ticks=', pe, id, clocks(id)%tick, end_tick, &
799  & delta, max_ticks
800  delta = delta + max_ticks + 1
801  call mpp_error( warning, 'MPP_CLOCK_END: Clock rollover, assumed single roll.' )
802  end if
803  clocks(id)%total_ticks = clocks(id)%total_ticks + delta
804  if (debug) then
805  if(num_clock_ids < 1) call mpp_error(note,'MPP_CLOCK_END: min num previous_clock < 1.' )
806  current_clock = previous_clock(num_clock_ids)
807  num_clock_ids = num_clock_ids-1
808  endif
809  clocks(id)%is_on = .false.
810 !$OMP END MASTER
811  return
812  end subroutine mpp_clock_end
813 
814  !#####################################################################
815  subroutine mpp_record_time_start()
816 
817  mpp_record_timing_data = .true.
818 
819  end subroutine mpp_record_time_start
820 
821  !#####################################################################
822  subroutine mpp_record_time_end()
823 
824  mpp_record_timing_data = .false.
825 
826  end subroutine mpp_record_time_end
827 
828 
829  !#####################################################################
830  subroutine increment_current_clock( event_id, bytes )
831  integer, intent(in) :: event_id
832  integer, intent(in), optional :: bytes
833  integer :: n
834  integer(i8_kind) :: delta
835  integer :: errunit
836 
837  if( .not. mpp_record_timing_data )return
838  if( .not.debug .or. (current_clock.EQ.0) )return
839  if( current_clock.LT.0 .OR. current_clock.GT.clock_num )call mpp_error( fatal, &
840  & 'MPP_CLOCK_BEGIN: invalid current_clock.' )
841  if( .NOT.clocks(current_clock)%detailed )return
842  call system_clock(end_tick)
843  n = clocks(current_clock)%events(event_id)%calls + 1
844 
845  if( n.EQ.max_events )call mpp_error( warning, &
846  'MPP_CLOCK: events exceed MAX_EVENTS, ignore detailed profiling data for clock '// &
847  & trim(clocks(current_clock)%name) )
848  if( n.GT.max_events )return
849 
850  clocks(current_clock)%events(event_id)%calls = n
851  delta = end_tick - start_tick
852  if( delta.LT.0 )then
853  errunit = stderr()
854  write( errunit,* )'pe, event_id, start_tick, end_tick, delta, max_ticks=', &
855  pe, event_id, start_tick, end_tick, delta, max_ticks
856  delta = delta + max_ticks + 1
857  call mpp_error( warning, 'MPP_CLOCK_END: Clock rollover, assumed single roll.' )
858  end if
859  clocks(current_clock)%events(event_id)%ticks(n) = delta
860  if( PRESENT(bytes) )clocks(current_clock)%events(event_id)%bytes(n) = bytes
861  return
862  end subroutine increment_current_clock
863 
864  !#####################################################################
865 
866  subroutine dump_clock_summary()
867 
868  real :: total_time,total_time_all,total_data
869  real :: msg_size,eff_bw,s
870  integer :: sd_unit, total_calls
871  integer :: j,k,ct, msg_cnt
872  character(len=2) :: u
873  character(len=FMS_FILE_LEN) :: filename
874  character(len=20),dimension(MAX_BINS),save :: bin
875 
876  data bin( 1) /' 0 - 8 B: '/
877  data bin( 2) /' 8 - 16 B: '/
878  data bin( 3) /' 16 - 32 B: '/
879  data bin( 4) /' 32 - 64 B: '/
880  data bin( 5) /' 64 - 128 B: '/
881  data bin( 6) /'128 - 256 B: '/
882  data bin( 7) /'256 - 512 B: '/
883  data bin( 8) /'512 - 1024 B: '/
884  data bin( 9) /' 1.0 - 2.1 KB: '/
885  data bin(10) /' 2.1 - 4.1 KB: '/
886  data bin(11) /' 4.1 - 8.2 KB: '/
887  data bin(12) /' 8.2 - 16.4 KB: '/
888  data bin(13) /' 16.4 - 32.8 KB: '/
889  data bin(14) /' 32.8 - 65.5 KB: '/
890  data bin(15) /' 65.5 - 131.1 KB: '/
891  data bin(16) /'131.1 - 262.1 KB: '/
892  data bin(17) /'262.1 - 524.3 KB: '/
893  data bin(18) /'524.3 - 1048.6 KB: '/
894  data bin(19) /' 1.0 - 2.1 MB: '/
895  data bin(20) /' >2.1 MB: '/
896 
897  if( .NOT.any(clocks(1:clock_num)%detailed) )return
898  write( filename,'(a,i6.6)' )'mpp_clock.out.', pe
899 
900  open(newunit=sd_unit,file=trim(filename),form='formatted')
901 
902  comm_type: do ct = 1,clock_num
903 
904  if( .NOT.clocks(ct)%detailed )cycle
905  write(sd_unit,*) &
906  clock_summary(ct)%name(1:15),' Communication Data for PE ',pe
907 
908  write(sd_unit,*) ' '
909  write(sd_unit,*) ' '
910 
911  total_time_all = 0.0
912  event_type: do k = 1,max_event_types-1
913 
914  if(clock_summary(ct)%event(k)%total_time == 0.0)cycle
915 
916  total_time = clock_summary(ct)%event(k)%total_time
917  total_time_all = total_time_all + total_time
918  total_data = clock_summary(ct)%event(k)%total_data
919  total_calls = int(clock_summary(ct)%event(k)%total_cnts)
920 
921  write(sd_unit,1000) clock_summary(ct)%event(k)%name(1:9) // ':'
922 
923  write(sd_unit,1001) 'Total Data: ',total_data*1.0e-6, &
924  'MB; Total Time: ', total_time, &
925  'secs; Total Calls: ',total_calls
926 
927  write(sd_unit,*) ' '
928  write(sd_unit,1002) ' Bin Counts Avg Size Eff B/W'
929  write(sd_unit,*) ' '
930 
931  bin_loop: do j=1,max_bins
932 
933  if(clock_summary(ct)%event(k)%msg_size_cnts(j)==0)cycle
934 
935  if(j<=8)then
936  s = 1.0
937  u = ' B'
938  elseif(j<=18)then
939  s = 1.0e-3
940  u = 'KB'
941  else
942  s = 1.0e-6
943  u = 'MB'
944  endif
945 
946  msg_cnt = int(clock_summary(ct)%event(k)%msg_size_cnts(j))
947  msg_size = &
948  s*(clock_summary(ct)%event(k)%msg_size_sums(j)/real(msg_cnt))
949  eff_bw = (1.0e-6)*( clock_summary(ct)%event(k)%msg_size_sums(j) / &
950  clock_summary(ct)%event(k)%msg_time_sums(j) )
951 
952  write(sd_unit,1003) bin(j),msg_cnt,msg_size,u,eff_bw
953 
954  end do bin_loop
955 
956  write(sd_unit,*) ' '
957  write(sd_unit,*) ' '
958  end do event_type
959 
960  ! "Data-less" WAIT
961 
962  if(clock_summary(ct)%event(max_event_types)%total_time>0.0)then
963 
964  total_time = clock_summary(ct)%event(max_event_types)%total_time
965  total_time_all = total_time_all + total_time
966  total_calls = int(clock_summary(ct)%event(max_event_types)%total_cnts)
967 
968  write(sd_unit,1000) clock_summary(ct)%event(max_event_types)%name(1:9) // ':'
969 
970  write(sd_unit,1004) 'Total Calls: ',total_calls,'; Total Time: ', &
971  total_time,'secs'
972 
973  endif
974 
975  write(sd_unit,*) ' '
976  write(sd_unit,1005) 'Total communication time spent for ' // &
977  clock_summary(ct)%name(1:9) // ': ',total_time_all,'secs'
978  write(sd_unit,*) ' '
979  write(sd_unit,*) ' '
980  write(sd_unit,*) ' '
981 
982  end do comm_type
983 
984  close(sd_unit)
985 
986 1000 format(a)
987 1001 format(a,f8.2,a,f8.2,a,i6)
988 1002 format(a)
989 1003 format(a,i6,' ',' ',f9.1,a,' ',f9.2,'MB/sec')
990 1004 format(a,i8,a,f9.2,a)
991 1005 format(a,f9.2,a)
992  return
993  end subroutine dump_clock_summary
994 
995  !#####################################################################
996 
997  integer function get_unit()
998 
999  integer,save :: i
1000  logical :: l_open
1001 
1002  if (pe == root_pe) call mpp_error(warning, &
1003  'get_unit is deprecated and will be removed in a future release, please use the Fortran intrinsic newunit')
1004  do i=10,99
1005  inquire(unit=i,opened=l_open)
1006  if(.not.l_open)exit
1007  end do
1008 
1009  if(i==100)then
1010  call mpp_error(fatal,'Unable to get I/O unit')
1011  else
1012  get_unit = i
1013  endif
1014 
1015  return
1016  end function get_unit
1017 
1018  !#####################################################################
1019 
1020  subroutine sum_clock_data()
1021 
1022  integer :: i,j,k,ct,event_size,event_cnt
1023  real :: msg_time
1024 
1025  clock_type: do ct=1,clock_num
1026  if( .NOT.clocks(ct)%detailed )cycle
1027  event_type: do j=1,max_event_types-1
1028  event_cnt = clocks(ct)%events(j)%calls
1029  event_summary: do i=1,event_cnt
1030 
1031  clock_summary(ct)%event(j)%total_cnts = &
1032  clock_summary(ct)%event(j)%total_cnts + 1
1033 
1034  event_size = int(clocks(ct)%events(j)%bytes(i))
1035 
1036  k = find_bin(event_size)
1037 
1038  clock_summary(ct)%event(j)%msg_size_cnts(k) = &
1039  clock_summary(ct)%event(j)%msg_size_cnts(k) + 1
1040 
1041  clock_summary(ct)%event(j)%msg_size_sums(k) = &
1042  clock_summary(ct)%event(j)%msg_size_sums(k) &
1043  + clocks(ct)%events(j)%bytes(i)
1044 
1045  clock_summary(ct)%event(j)%total_data = &
1046  clock_summary(ct)%event(j)%total_data &
1047  + clocks(ct)%events(j)%bytes(i)
1048 
1049  msg_time = clocks(ct)%events(j)%ticks(i)
1050  msg_time = tick_rate * real( clocks(ct)%events(j)%ticks(i) )
1051 
1052  clock_summary(ct)%event(j)%msg_time_sums(k) = &
1053  clock_summary(ct)%event(j)%msg_time_sums(k) + msg_time
1054 
1055  clock_summary(ct)%event(j)%total_time = &
1056  clock_summary(ct)%event(j)%total_time + msg_time
1057 
1058  end do event_summary
1059  end do event_type
1060 
1061  j = max_event_types ! WAITs
1062  ! "msg_size_cnts" doesn't really mean anything for WAIT
1063  ! but position will be used to store number of counts for now.
1064 
1065  event_cnt = clocks(ct)%events(j)%calls
1066  clock_summary(ct)%event(j)%msg_size_cnts(1) = event_cnt
1067  clock_summary(ct)%event(j)%total_cnts = event_cnt
1068 
1069  msg_time = tick_rate * real( sum( clocks(ct)%events(j)%ticks(1:event_cnt) ) )
1070  clock_summary(ct)%event(j)%msg_time_sums(1) = &
1071  clock_summary(ct)%event(j)%msg_time_sums(1) + msg_time
1072 
1073  clock_summary(ct)%event(j)%total_time = clock_summary(ct)%event(j)%msg_time_sums(1)
1074 
1075  end do clock_type
1076 
1077  return
1078  contains
1079  integer function find_bin(event_size)
1080 
1081  integer,intent(in) :: event_size
1082  integer :: k,msg_size
1083 
1084  msg_size = 8
1085  k = 1
1086  do while(event_size>msg_size .and. k<max_bins)
1087  k = k+1
1088  msg_size = msg_size*2
1089  end do
1090  find_bin = k
1091  return
1092  end function find_bin
1093 
1094  end subroutine sum_clock_data
1095 
1096  !#####################################################################
1097  !> This routine will double the size of peset and copy the original peset data
1098  !! into the expanded one. The maximum allowed to expand is PESET_MAX.
1099  subroutine expand_peset()
1100  integer :: old_peset_max,n
1101  type(communicator), allocatable :: peset_old(:)
1102 
1103  old_peset_max = current_peset_max
1104  if(old_peset_max .GE. peset_max) call mpp_error(fatal, &
1105  "mpp_mod(expand_peset): the number of peset reached PESET_MAX, increase PESET_MAX or contact developer")
1106 
1107  ! copy data to a tempoary data
1108  allocate(peset_old(0:old_peset_max))
1109  do n = 0, old_peset_max
1110  peset_old(n)%count = peset(n)%count
1111  peset_old(n)%id = peset(n)%id
1112  peset_old(n)%group = peset(n)%group
1113  peset_old(n)%name = peset(n)%name
1114  peset_old(n)%start = peset(n)%start
1115  peset_old(n)%log2stride = peset(n)%log2stride
1116 
1117  if( ASSOCIATED(peset(n)%list) ) then
1118  allocate(peset_old(n)%list(size(peset(n)%list(:))) )
1119  peset_old(n)%list(:) = peset(n)%list(:)
1120  deallocate(peset(n)%list)
1121  endif
1122  enddo
1123  deallocate(peset)
1124 
1125  ! create the new peset
1126  current_peset_max = min(peset_max, 2*old_peset_max)
1127  allocate(peset(0:current_peset_max))
1128  peset(:)%count = -1
1129  peset(:)%id = -1
1130  peset(:)%group = -1
1131  peset(:)%start = -1
1132  peset(:)%log2stride = -1
1133  peset(:)%name = " "
1134  do n = 0, old_peset_max
1135  peset(n)%count = peset_old(n)%count
1136  peset(n)%id = peset_old(n)%id
1137  peset(n)%group = peset_old(n)%group
1138  peset(n)%name = peset_old(n)%name
1139  peset(n)%start = peset_old(n)%start
1140  peset(n)%log2stride = peset_old(n)%log2stride
1141 
1142  if( ASSOCIATED(peset_old(n)%list) ) then
1143  allocate(peset(n)%list(size(peset_old(n)%list(:))) )
1144  peset(n)%list(:) = peset_old(n)%list(:)
1145  deallocate(peset_old(n)%list)
1146  endif
1147  enddo
1148  deallocate(peset_old)
1149 
1150  call mpp_error(note, "mpp_mod(expand_peset): size of peset is expanded to ", current_peset_max)
1151 
1152  end subroutine expand_peset
1153  !#####################################################################
1154 
1155  function uppercase (cs)
1156  character(len=*), intent(in) :: cs
1157  character(len=len(cs)),target :: uppercase
1158  integer :: k,tlen
1159  character, pointer :: ca
1160  integer, parameter :: co=iachar('A')-iachar('a') ! case offset
1161  !The transfer function truncates the string with xlf90_r
1162  tlen = len_trim(cs)
1163  if(tlen <= 0) then ! catch IBM compiler bug
1164  uppercase = cs ! simply return input blank string
1165  else
1166  uppercase = cs(1:tlen)
1167  do k=1, tlen
1168  ca => uppercase(k:k)
1169  if(ca >= "a" .and. ca <= "z") ca = achar(ichar(ca)+co)
1170  enddo
1171  endif
1172  end function uppercase
1173 
1174 !#######################################################################
1175 
1176  function lowercase (cs)
1177  character(len=*), intent(in) :: cs
1178  character(len=len(cs)),target :: lowercase
1179  integer, parameter :: co=iachar('a')-iachar('A') ! case offset
1180  integer :: k,tlen
1181  character, pointer :: ca
1182 ! The transfer function truncates the string with xlf90_r
1183  tlen = len_trim(cs)
1184  if(tlen <= 0) then ! catch IBM compiler bug
1185  lowercase = cs ! simply return input blank string
1186  else
1187  lowercase = cs(1:tlen)
1188  do k=1, tlen
1189  ca => lowercase(k:k)
1190  if(ca >= "A" .and. ca <= "Z") ca = achar(ichar(ca)+co)
1191  enddo
1192  endif
1193  end function lowercase
1194 
1195 
1196  !#######################################################################
1197 
1198 !-----------------------------------------------------------------------
1199 !
1200 ! AUTHOR: Rusty Benson (rusty.benson@noaa.gov)
1201 !
1202 !
1203 ! THESE LINES MUST BE PRESENT IN MPP.F90
1204 !
1205 ! ! public variable needed for reading an input nml file from an internal file
1206 ! character(len=:), dimension(:), allocatable, public :: input_nml_file
1207 !
1208 
1209 !-----------------------------------------------------------------------
1210 
1211 !> Reads an existing input nml file into a character array and broadcasts
1212 !! it to the non-root mpi-tasks. This allows the use of reads from an
1213 !! internal file for namelist settings (requires 2003 compliant compiler)
1214 !!
1215 !! read(input_nml_file, nml=<name_nml>, iostat=status)
1216 !!
1217 !!
1218  subroutine read_input_nml(pelist_name_in, alt_input_nml_path)
1219 
1220 ! Include variable "version" to be written to log file.
1221 #include<file_version.h>
1222 
1223  character(len=*), intent(in), optional :: pelist_name_in
1224  character(len=*), intent(in), optional :: alt_input_nml_path
1225 ! private variables
1226  integer :: log_unit
1227  integer :: i
1228  integer, dimension(2) :: lines_and_length
1229  logical :: file_exist
1230  character(len=len(peset(current_peset_num)%name)) :: pelist_name
1231  character(len=FMS_PATH_LEN) :: filename
1232 
1233 ! check the status of input_nml_file
1234  if ( allocated(input_nml_file) ) then
1235  deallocate(input_nml_file)
1236  endif
1237 
1238 ! the following code is necessary for using alternate namelist files (nests, stretched grids, etc)
1239  if (PRESENT(pelist_name_in)) then
1240  ! test to make sure length of pelist_name_in is <= pelist_name
1241  if (len(pelist_name_in) > len(pelist_name)) then
1242  call mpp_error(fatal, &
1243  "mpp_util.inc: read_input_nml optional argument pelist_name_in has size greater than local pelist_name")
1244  else
1245  pelist_name = pelist_name_in
1246  endif
1247  else
1248  pelist_name = mpp_get_current_pelist_name()
1249  endif
1250  filename='input_'//trim(pelist_name)//'.nml'
1251  inquire(file=filename, exist=file_exist)
1252  if (.not. file_exist ) then
1253  if (present(alt_input_nml_path)) then
1254  filename = alt_input_nml_path
1255  else
1256  filename = 'input.nml'
1257  end if
1258  endif
1259  lines_and_length = get_ascii_file_num_lines_and_length(filename)
1260  allocate(character(len=lines_and_length(2))::input_nml_file(lines_and_length(1)))
1261  call read_ascii_file(filename, lines_and_length(2), input_nml_file)
1262 
1263 ! write info logfile
1264  if (pe == root_pe) then
1265  log_unit = stdlog()
1266  write(log_unit,'(a)') '========================================================================'
1267  write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(version)
1268  write(log_unit,'(a)') 'READ_INPUT_NML: '//trim(filename)//' '
1269  do i = 1, lines_and_length(1)
1270  write(log_unit,*) trim(input_nml_file(i))
1271  enddo
1272  end if
1273  end subroutine read_input_nml
1274 
1275 
1276  !#######################################################################
1277  !z1l: This is extracted from read_ascii_file
1278  function get_ascii_file_num_lines(FILENAME, LENGTH, PELIST)
1279  character(len=*), intent(in) :: FILENAME
1280  integer, intent(in) :: LENGTH
1281  integer, intent(in), optional, dimension(:) :: PELIST
1282 
1283  integer :: num_lines, get_ascii_file_num_lines
1284  character(len=LENGTH) :: str_tmp
1285  character(len=5) :: text
1286  integer :: status, f_unit, from_pe
1287  logical :: file_exist
1288 
1289  if( read_ascii_file_on) then
1290  call mpp_error(fatal, &
1291  "mpp_util.inc: get_ascii_file_num_lines is called again before calling read_ascii_file")
1292  endif
1293  read_ascii_file_on = .true.
1294 
1295  from_pe = root_pe
1296  get_ascii_file_num_lines = -1
1297  num_lines = -1
1298  if ( pe == root_pe ) then
1299  inquire(file=filename, exist=file_exist)
1300 
1301  if ( file_exist ) then
1302  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1303 
1304  if ( status .ne. 0 ) then
1305  write (unit=text, fmt='(I5)') status
1306  call mpp_error(fatal, 'get_ascii_file_num_lines: Error opening file:' //trim(filename)// &
1307  '. (IOSTAT = '//trim(text)//')')
1308  else
1309  num_lines = 1
1310  do
1311  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1312  if ( status .lt. 0 ) then
1313  ! deprecate num_lines by 1 and ensure num_lines is at least 1
1314  num_lines = max(num_lines - 1, 1)
1315  exit
1316  endif
1317  if ( status .gt. 0 ) then
1318  write (unit=text, fmt='(I5)') num_lines
1319  call mpp_error(fatal, 'get_ascii_file_num_lines: Error reading line '//trim(text)// &
1320  ' in file '//trim(filename)//'.')
1321  end if
1322  if ( len_trim(str_tmp) == length ) then
1323  write(unit=text, fmt='(I5)') length
1324  call mpp_error(fatal, 'get_ascii_file_num_lines: Length of output string ('//trim(text)//&
1325  & ' is too small. Increase the LENGTH value.')
1326  end if
1327  num_lines = num_lines + 1
1328  end do
1329  close(unit=f_unit)
1330  end if
1331  else
1332  call mpp_error(fatal, 'get_ascii_file_num_lines: File '//trim(filename)//' does not exist.')
1333  end if
1334  end if
1335 
1336  ! Broadcast number of lines
1337  call mpp_broadcast(num_lines, from_pe, pelist=pelist)
1338  get_ascii_file_num_lines = num_lines
1339 
1340  end function get_ascii_file_num_lines
1341 
1342  !#######################################################################
1343  !> @brief Function to determine the maximum line length and number of lines from an ascii file
1344  function get_ascii_file_num_lines_and_length(FILENAME, PELIST)
1345  character(len=*), intent(in) :: filename !< name of the file to be read
1346  integer, intent(in), optional, dimension(:) :: pelist !< optional pelist
1347 
1348  integer, dimension(2) :: get_ascii_file_num_lines_and_length !< number of lines (1) and
1349  !! max line length (2)
1350  integer :: num_lines, max_length
1351  integer, parameter :: length=1024
1352  character(len=LENGTH) :: str_tmp
1353  character(len=5) :: text
1354  integer :: status, f_unit, from_pe
1355  logical :: file_exist
1356 
1357  if( read_ascii_file_on) then
1358  call mpp_error(fatal, &
1359  "mpp_util.inc: get_ascii_file_num_lines is called again before calling read_ascii_file")
1360  endif
1361  read_ascii_file_on = .true.
1362 
1363  from_pe = root_pe
1365  num_lines = -1
1366  max_length = -1
1367  if ( pe == root_pe ) then
1368  inquire(file=filename, exist=file_exist)
1369 
1370  if ( file_exist ) then
1371  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1372 
1373  if ( status .ne. 0 ) then
1374  write (unit=text, fmt='(I5)') status
1375  call mpp_error(fatal, 'get_ascii_file_num_lines: Error opening file:' //trim(filename)// &
1376  '. (IOSTAT = '//trim(text)//')')
1377  else
1378  num_lines = 1
1379  max_length = 1
1380  do
1381  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1382  if ( status .lt. 0 ) then
1383  ! deprecate num_lines by 1 and ensure num_lines is at least 1
1384  num_lines = max(num_lines - 1, 1)
1385  exit
1386  endif
1387  if ( status .gt. 0 ) then
1388  write (unit=text, fmt='(I5)') num_lines
1389  call mpp_error(fatal, 'get_ascii_file_num_lines: Error reading line '//trim(text)// &
1390  ' in file '//trim(filename)//'.')
1391  end if
1392  if ( len_trim(str_tmp) == length) then
1393  write(unit=text, fmt='(I5)') length
1394  call mpp_error(fatal, 'get_ascii_file_num_lines: Length of output string ('//trim(text)//&
1395  & ' is too small. Increase the LENGTH value.')
1396  end if
1397  if (len_trim(str_tmp) > max_length) max_length = len_trim(str_tmp)
1398  num_lines = num_lines + 1
1399  end do
1400  close(unit=f_unit)
1401  end if
1402  else
1403  call mpp_error(fatal, 'get_ascii_file_num_lines: File '//trim(filename)//' does not exist.')
1404  end if
1405  max_length = max_length+1
1406  end if
1407 
1408  ! Broadcast number of lines
1409  call mpp_broadcast(num_lines, from_pe, pelist=pelist)
1410  call mpp_broadcast(max_length, from_pe, pelist=pelist)
1412  get_ascii_file_num_lines_and_length(2) = max_length
1413 
1415 
1416  !-----------------------------------------------------------------------
1417  !
1418  ! AUTHOR: Rusty Benson <rusty.benson@noaa.gov>,
1419  ! Seth Underwood <Seth.Underwood@noaa.gov>
1420  !
1421  !-----------------------------------------------------------------------
1422  ! subroutine READ_ASCII_FILE
1423  !
1424  !
1425  !> Reads any ascii file into a character array and broadcasts
1426  !! it to the non-root mpi-tasks. Based off READ_INPUT_NML.
1427  !!
1428  !! Passed in 'Content' array, must be of the form:
1429  !! character(len=LENGTH), dimension(:), allocatable :: array_name
1430  !!
1431  !! Reads from this array must be done in a do loop over the number of
1432  !! lines, i.e.:
1433  !!
1434  !! do i=1, num_lines
1435  !! read (UNIT=array_name(i), FMT=*) var1, var2, ...
1436  !! end do
1437  subroutine read_ascii_file(FILENAME, LENGTH, Content, PELIST)
1438  character(len=*), intent(in) :: FILENAME
1439  integer, intent(in) :: LENGTH
1440  character(len=*), intent(inout), dimension(:) :: Content
1441  integer, intent(in), optional, dimension(:) :: PELIST
1442 
1443  ! Include variable "version" to be written to log file.
1444 #include<file_version.h>
1445 
1446  character(len=5) :: text
1447  logical :: file_exist
1448  integer :: status, f_unit, log_unit
1449  integer :: from_pe
1450  integer :: pnum_lines, num_lines
1451  character(len=LENGTH) :: str_tmp !< Temporary variable to store line from file
1452 
1453  if( .NOT. read_ascii_file_on) then
1454  call mpp_error(fatal, &
1455  "mpp_util.inc: get_ascii_file_num_lines needs to be called before calling read_ascii_file")
1456  endif
1457  read_ascii_file_on = .false.
1458 
1459  from_pe = root_pe
1460  num_lines = size(content(:))
1461 
1462  if ( pe == root_pe ) then
1463  ! write info logfile
1464  log_unit = stdlog()
1465  write(log_unit,'(a)') '========================================================================'
1466  write(log_unit,'(a)') 'READ_ASCII_FILE: '//trim(version)
1467  write(log_unit,'(a)') 'READ_ASCII_FILE: File: '//trim(filename)
1468 
1469  inquire(file=filename, exist=file_exist)
1470 
1471  if ( file_exist ) then
1472  open(newunit=f_unit, file=filename, action='READ', status='OLD', iostat=status)
1473 
1474  if ( status .ne. 0 ) then
1475  write (unit=text, fmt='(I5)') status
1476  call mpp_error(fatal, 'READ_ASCII_FILE: Error opening file: '// &
1477  & trim(filename)//'. (IOSTAT = '//trim(text)//')')
1478  else
1479 
1480  if ( num_lines .gt. 0 ) then
1481  content(:) = ' '
1482 
1483  rewind(unit=f_unit, iostat=status)
1484  if ( status .ne. 0 ) then
1485  write (unit=text, fmt='(I5)') status
1486  call mpp_error(fatal, 'READ_ASCII_FILE: Unable to re-read file '//trim(filename)//'. (IOSTAT = '&
1487  //trim(text)//'.')
1488  else
1489  ! A second 'sanity' check on the file
1490  pnum_lines = 1
1491 
1492  do
1493  read (unit=f_unit, fmt='(A)', iostat=status) str_tmp
1494 
1495  if ( status .lt. 0 ) then
1496  ! deprecate pnum_lines by 1 and ensure pnum_lines is at least 1
1497  pnum_lines = max(pnum_lines - 1, 1)
1498  exit
1499  endif
1500  if ( status .gt. 0 ) then
1501  write (unit=text, fmt='(I5)') pnum_lines
1502  call mpp_error(fatal, 'READ_ASCII_FILE: Error reading line '// &
1503  & trim(text)//' in file '//trim(filename)//'.')
1504  end if
1505  if(pnum_lines > num_lines) then
1506  call mpp_error(fatal, 'READ_ASCII_FILE: number of lines in file '//trim(filename)// &
1507  ' is greater than size(Content(:)). ')
1508  end if
1509  if ( len_trim(str_tmp) == length ) then
1510  write(unit=text, fmt='(I5)') length
1511  call mpp_error(fatal, 'READ_ASCII_FILE: Length of output string ('//trim(text)// &
1512  & ' is too small. Increase the LENGTH value.')
1513  end if
1514  content(pnum_lines) = str_tmp
1515  pnum_lines = pnum_lines + 1
1516  end do
1517  if(num_lines .NE. pnum_lines) then
1518  call mpp_error(fatal, 'READ_ASCII_FILE: number of lines in file '//trim(filename)// &
1519  ' does not equal to size(Content(:)) ' )
1520  end if
1521  end if
1522  end if
1523  close(unit=f_unit)
1524  end if
1525  else
1526  call mpp_error(fatal, 'READ_ASCII_FILE: File '//trim(filename)//' does not exist.')
1527  end if
1528  end if
1529 
1530  ! Broadcast character array
1531  call mpp_broadcast(content, length, from_pe, pelist=pelist)
1532 
1533  end subroutine read_ascii_file
1534 !> @}
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:1438
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:490
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:1219
subroutine mpp_clock_set_grain(grain)
Set the level of granularity of timing measurements.
Definition: mpp_util.inc:641
subroutine mpp_declare_pelist(pelist, name, commID)
Declare a pelist.
Definition: mpp_util.inc:461
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:1345
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:705
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:1100