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