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