Oasis3-MCT
mod_oasis_reprosum.F90
Go to the documentation of this file.
1 
2 !> OASIS reproducible sum method from P. Worley
3 
5 
6 !-----------------------------------------------------------------------
7 !
8 ! Purpose:
9 !> Compute reproducible global sums of a set of arrays across an MPI
10 !> subcommunicator
11 !
12 ! Methods:
13 !> Compute using either or both a scalable, reproducible algorithm and a
14 !> scalable, nonreproducible algorithm:
15 !> * Reproducible (scalable):
16 !> Convert to fixed point (integer vector representation) to enable
17 !> reproducibility when using MPI_Allreduce
18 !> * Alternative usually reproducible (scalable):
19 !> Use parallel double-double algorithm due to Helen He and
20 !> Chris Ding, based on David Bailey's/Don Knuth's DDPDD algorithm
21 !> * Nonreproducible (scalable):
22 !> Floating point and MPI_Allreduce based.
23 !> If computing both reproducible and nonreproducible sums, compare
24 !> these and report relative difference (if absolute difference
25 !> less than sum) or absolute difference back to calling routine.
26 !
27 ! Author: P. Worley (based on suggestions from J. White for fixed
28 ! point algorithm and on He/Ding paper for ddpdd
29 ! algorithm)
30 !
31 ! Modified by T.Craig for Oasis, May 2017 based on the public version in
32 ! CESM1.2.
33 !
34 !-----------------------------------------------------------------------
35 
36 !-----------------------------------------------------------------------
37 !- use statements ------------------------------------------------------
38 !-----------------------------------------------------------------------
39 #if ( defined noI8 )
40  ! Workaround for when shr_kind_i8 is not supported.
41  use mod_oasis_kinds, only: r8 => ip_r8_p, i8 => ip_i4_p
42 #else
43  use mod_oasis_kinds, only: r8 => ip_r8_p, i8 => ip_i8_p
44 #endif
45  use mod_oasis_data, only: nulprt
46  use mod_oasis_sys, only: oasis_abort, estr
49 
50 !-----------------------------------------------------------------------
51 !- module boilerplate --------------------------------------------------
52 !-----------------------------------------------------------------------
53  implicit none
54  private
55 
56 !-----------------------------------------------------------------------
57 !- include statements --------------------------------------------------
58 !-----------------------------------------------------------------------
59 #include <mpif.h>
60 
61 !-----------------------------------------------------------------------
62 ! Public interfaces ----------------------------------------------------
63 !-----------------------------------------------------------------------
64  public :: &
65  oasis_reprosum_setopts, &! set runtime options
66  oasis_reprosum_calc, &! calculate distributed sum
67  oasis_reprosum_tolexceeded ! utility function to check relative
68  ! differences against the tolerance
69 
70 !-----------------------------------------------------------------------
71 ! Public data ----------------------------------------------------------
72 !-----------------------------------------------------------------------
73  logical, public :: oasis_reprosum_recompute = .false.
74 
75  real(r8), public :: oasis_reprosum_reldiffmax = -1.0_r8
76 
77 !-----------------------------------------------------------------------
78 ! Private interfaces ---------------------------------------------------
79 !-----------------------------------------------------------------------
80  private :: &
81  ddpdd, &! double-double sum routine
82  split_indices ! split indices among OMP threads
83 
84 !-----------------------------------------------------------------------
85 ! Private data ---------------------------------------------------------
86 !-----------------------------------------------------------------------
87 
88  logical :: repro_sum_use_ddpdd = .false.
89  logical :: detailed_timing = .false.
90 
91  CONTAINS
92 
93 !========================================================================
94 !-----------------------------------------------------------------------
95 ! Purpose:
96 !> Set runtime options
97 ! Author: P. Worley
98 !-----------------------------------------------------------------------
99 
100  subroutine oasis_reprosum_setopts(repro_sum_use_ddpdd_in, &
101  repro_sum_rel_diff_max_in, &
102  repro_sum_recompute_in, &
103  repro_sum_master, &
104  repro_sum_logunit )
106 !------------------------------Arguments--------------------------------
107  logical, intent(in), optional :: repro_sum_use_ddpdd_in
108  !< Use DDPDD algorithm instead of fixed precision algorithm
109  real(r8), intent(in), optional :: repro_sum_rel_diff_max_in
110  !< maximum permissible difference between reproducible and
111  !< nonreproducible sums
112  logical, intent(in), optional :: repro_sum_recompute_in
113  !< recompute using different algorithm when difference between
114  !< reproducible and nonreproducible sums is too great
115  logical, intent(in), optional :: repro_sum_master
116  !< flag indicating whether this process should output
117  !< log messages
118  integer, intent(in), optional :: repro_sum_logunit
119  !< unit number for log messages
120 !---------------------------Local Workspace-----------------------------
121  integer llogunit ! unit number for log messages
122  logical master ! local master?
123  logical,save :: firstcall = .true. ! first call
124  character(len=*),parameter :: subname = '(oasis_reprosum_setopts)'
125 !-----------------------------------------------------------------------
126 
127  if ( present(repro_sum_master) ) then
128  master = repro_sum_master
129  else
130  master = .false.
131  endif
132 
133  if ( present(repro_sum_logunit) ) then
134  llogunit = repro_sum_logunit
135  else
136  llogunit = nulprt
137  endif
138 
139  if (.not. firstcall) then
140  write(llogunit,*) subname,estr,' can only be called once'
141  call oasis_abort(file=__file__,line=__line__)
142  endif
143  firstcall = .false.
144 
145  if ( present(repro_sum_use_ddpdd_in) ) then
146  repro_sum_use_ddpdd = repro_sum_use_ddpdd_in
147  endif
148  if ( present(repro_sum_rel_diff_max_in) ) then
149  oasis_reprosum_reldiffmax = repro_sum_rel_diff_max_in
150  endif
151  if ( present(repro_sum_recompute_in) ) then
152  oasis_reprosum_recompute = repro_sum_recompute_in
153  endif
154  if (master) then
155  if ( repro_sum_use_ddpdd ) then
156  write(llogunit,*) subname, &
157  'Using double-double-based (scalable) usually reproducible ', &
158  'distributed sum algorithm'
159  else
160  write(llogunit,*) subname, &
161  'Using fixed-point-based (scalable) reproducible ', &
162  'distributed sum algorithm'
163  endif
164 
165  if (oasis_reprosum_reldiffmax >= 0._r8) then
166  write(llogunit,*) subname, &
167  ' with a maximum relative error tolerance of ', &
169  if (oasis_reprosum_recompute) then
170  write(llogunit,*) subname, &
171  'If tolerance exceeded, sum is recomputed using ', &
172  'a serial algorithm.'
173  else
174  write(llogunit,*) subname, &
175  'If tolerance exceeded, fixed-precision is sum used ', &
176  'but a warning is output.'
177  endif
178  else
179  write(llogunit,*) subname, &
180  'and not comparing with floating point algorithms.'
181  endif
182 
183  endif
184  end subroutine oasis_reprosum_setopts
185 
186 !========================================================================
187 
188 !
189 ! Purpose:
190 !> Compute the global sum of each field in "arr" using the indicated
191 !> communicator with a reproducible yet scalable implementation based
192 !> on a fixed point algorithm. An alternative is to use an "almost
193 !> always reproducible" floating point algorithm.
194 !
195 ! The accuracy of the fixed point algorithm is controlled by the
196 ! number of "levels" of integer expansion. The algorithm will calculate
197 ! the number of levels that is required for the sum to be essentially
198 ! exact. The optional parameter arr_max_levels can be used to override
199 ! the calculated value. The optional parameter arr_max_levels_out can be
200 ! used to return the values used.
201 !
202 ! The algorithm also requires an upper bound on
203 ! the maximum summand (in absolute value) for each field, and will
204 ! calculate this internally. However, if the optional parameters
205 ! arr_max_levels and arr_gbl_max are both set, then the algorithm will
206 ! use the values in arr_gbl_max for the upper bounds instead. If these
207 ! are not upper bounds, or if the upper bounds are not tight enough
208 ! to achieve the requisite accuracy, and if the optional parameter
209 ! repro_sum_validate is NOT set to .false., the algorithm will repeat the
210 ! computation with appropriate upper bounds. If only arr_gbl_max is present,
211 ! then the maxima are computed internally (and the specified values are
212 ! ignored). The optional parameter arr_gbl_max_out can be
213 ! used to return the values used.
214 !
215 ! Finally, the algorithm requires an upper bound on the number of
216 ! local summands across all processes. This will be calculated internally,
217 ! using an MPI collective, but the value in the optional argument
218 ! gbl_max_nsummands will be used instead if (1) it is present, (2)
219 ! it is > 0, and (3) the maximum value and required number of levels
220 ! are also specified. (If the maximum value is calculated, the same
221 ! MPI collective is used to determine the maximum number of local
222 ! summands.) The accuracy of the user-specified value is not checked.
223 ! However, if set to < 1, the value will instead be calculated. If the
224 ! optional parameter gbl_max_nsummands_out is present, then the value
225 ! used (gbl_max_nsummands if >= 1; calculated otherwise) will be
226 ! returned.
227 !
228 ! If requested (by setting oasis_reprosum_reldiffmax >= 0.0 and passing in
229 ! the optional rel_diff parameter), results are compared with a
230 ! nonreproducible floating point algorithm.
231 !
232 ! Note that the cost of the algorithm is not strongly correlated with
233 ! the number of levels, which primarily shows up as a (modest) increase
234 ! in cost of the MPI_Allreduce as a function of vector length. Rather the
235 ! cost is more a function of (a) the number of integers required to
236 ! represent an individual summand and (b) the number of MPI_Allreduce
237 ! calls. The number of integers required to represent an individual
238 ! summand is 1 or 2 when using 8-byte integers for 8-byte real summands
239 ! when the number of local summands is not too large. As the number of
240 ! local summands increases, the number of integers required increases.
241 ! The number of MPI_Allreduce calls is either 2 (specifying nothing) or
242 ! 1 (specifying gbl_max_nsummands, arr_max_levels, and arr_gbl_max
243 ! correctly). When specifying arr_max_levels and arr_gbl_max
244 ! incorrectly, 3 or 4 MPI_Allreduce calls will be required.
245 !
246 ! The alternative algorithm is a minor modification of a parallel
247 ! implementation of David Bailey's routine DDPDD by Helen He
248 ! and Chris Ding. Bailey uses the Knuth trick to implement quadruple
249 ! precision summation of double precision values with 10 double
250 ! precision operations. The advantage of this algorithm is that
251 ! it requires a single MPI_Allreduce and is less expensive per summand
252 ! than is the fixed precision algorithm. The disadvantage is that it
253 ! is not guaranteed to be reproducible (though it is reproducible
254 ! much more often than is the standard algorithm). This alternative
255 ! is used when the optional parameter ddpdd_sum is set to .true. It is
256 ! also used if the fixed precision algorithm radix assumption does not
257 ! hold.
258 
259 !----------------------------------------------------------------------
260  subroutine oasis_reprosum_calc (arr, arr_gsum, nsummands, dsummands, &
261  nflds, ddpdd_sum, &
262  arr_gbl_max, arr_gbl_max_out, &
263  arr_max_levels, arr_max_levels_out, &
264  gbl_max_nsummands, gbl_max_nsummands_out,&
265  gbl_count, repro_sum_validate, &
266  repro_sum_stats, rel_diff, commid )
267 !----------------------------------------------------------------------
268 
269 ! Arguments
270 
271  integer, intent(in) :: nsummands !< number of local summands
272  integer, intent(in) :: dsummands !< declared first dimension
273  integer, intent(in) :: nflds !< number of fields
274  real(r8), intent(in) :: arr(dsummands,nflds)
275  !< input array
276 
277  real(r8), intent(out):: arr_gsum(nflds)
278  !< global means
279 
280  logical, intent(in), optional :: ddpdd_sum
281  !< use ddpdd algorithm instead
282  !< of fixed precision algorithm
283 
284  real(r8), intent(in), optional :: arr_gbl_max(nflds)
285  !< upper bound on max(abs(arr))
286 
287  real(r8), intent(out), optional :: arr_gbl_max_out(nflds)
288  !< calculated upper bound on
289  !< max(abs(arr))
290 
291  integer, intent(in), optional :: arr_max_levels(nflds)
292  !< maximum number of levels of
293  !< integer expansion to use
294 
295  integer, intent(out), optional :: arr_max_levels_out(nflds)
296  !< output of number of levels of
297  !< integer expansion to used
298 
299  integer, intent(in), optional :: gbl_max_nsummands
300  !< maximum of nsummand over all
301  !< processes
302 
303  integer, intent(out), optional :: gbl_max_nsummands_out
304  !< calculated maximum nsummands
305  !< over all processes
306 
307  integer, intent(in), optional :: gbl_count
308  !< was total number of summands;
309  !< now is ignored; use
310  !< gbl_max_nsummands instead
311 
312  logical, intent(in), optional :: repro_sum_validate
313  !< flag enabling/disabling testing that gmax and max_levels are
314  !< accurate/sufficient. Default is enabled.
315 
316  integer, intent(inout), optional :: repro_sum_stats(5)
317  !< increment running totals for
318  !< (1) one-reduction repro_sum
319  !< (2) two-reduction repro_sum
320  !< (3) both types in one call
321  !< (4) nonrepro_sum
322  !< (5) global max nsummands reduction
323 
324  real(r8), intent(out), optional :: rel_diff(2,nflds)
325  !< relative and absolute
326  !< differences between fixed
327  !< and floating point sums
328 
329  integer, intent(in), optional :: commid
330  !< MPI communicator
331 
332 ! Local workspace
333 
334  logical :: use_ddpdd_sum ! flag indicating whether to
335  ! use oasis_reprosum_ddpdd or not
336  logical :: recompute ! flag indicating need to
337  ! determine gmax/gmin before
338  ! computing sum
339  logical :: validate ! flag indicating need to
340  ! verify gmax and max_levels
341  ! are accurate/sufficient
342  integer :: omp_nthreads ! number of OpenMP threads
343  integer :: mpi_comm ! MPI subcommunicator
344  integer :: tasks ! number of MPI processes
345  integer :: ierr ! MPI error return
346  integer :: ifld, isum, ithread ! loop variables
347  integer :: max_nsummands ! max nsummands over all processes
348  ! or threads (used in both ways)
349 
350  integer, allocatable :: isum_beg(:), isum_end(:)
351  ! range of summand indices for each
352  ! OpenMP thread
353  integer, allocatable :: arr_tlmin_exp(:,:)
354  ! per thread local exponent minima
355  integer, allocatable :: arr_tlmax_exp(:,:)
356  ! per thread local exponent maxima
357  integer :: arr_exp, arr_exp_tlmin, arr_exp_tlmax
358  ! summand exponent and working min/max
359  integer :: arr_lmin_exp(nflds) ! local exponent minima
360  integer :: arr_lmax_exp(nflds) ! local exponent maxima
361  integer :: arr_lextremes(0:nflds,2)! local exponent extrema
362  integer :: arr_gextremes(0:nflds,2)! global exponent extrema
363 
364  integer :: arr_gmax_exp(nflds) ! global exponents maxima
365  integer :: arr_gmin_exp(nflds) ! global exponents minima
366  integer :: arr_max_shift ! maximum safe exponent for
367  ! value < 1 (so that sum does
368  ! not overflow)
369  integer :: max_levels(nflds) ! maximum number of levels of
370  ! integer expansion to use
371  integer :: max_level ! maximum value in max_levels
372  integer :: gbl_max_red ! global max local sum reduction? (0/1)
373  integer :: repro_sum_fast ! 1 reduction repro_sum? (0/1)
374  integer :: repro_sum_slow ! 2 reduction repro_sum? (0/1)
375  integer :: repro_sum_both ! both fast and slow? (0/1)
376  integer :: nonrepro_sum ! nonrepro_sum? (0/1)
377 
378  real(r8) :: xmax_nsummands ! dble of max_nsummands
379  real(r8) :: arr_lsum(nflds) ! local sums
380  real(r8) :: arr_gsum_fast(nflds) ! global sum calculated using
381  ! fast, nonreproducible,
382  ! floating point alg.
383  real(r8) :: abs_diff ! absolute difference between
384  ! fixed and floating point
385  ! sums
386 #ifdef _OPENMP
387  integer omp_get_max_threads
388  external omp_get_max_threads
389 #endif
390  character(len=*),parameter :: subname = '(oasis_reprosum_calc)'
391 
392 !-----------------------------------------------------------------------
393 
394 ! check whether should use oasis_reprosum_ddpdd algorithm
395  use_ddpdd_sum = repro_sum_use_ddpdd
396  if ( present(ddpdd_sum) ) then
397  use_ddpdd_sum = ddpdd_sum
398  endif
399 
400 ! check whether intrinsic-based algorithm will work on this system
401 ! (requires floating point and integer bases to be the same)
402 ! If not, always use ddpdd.
403  use_ddpdd_sum = use_ddpdd_sum .or. (radix(0._r8) /= radix(0_i8))
404 
405 ! initialize local statistics variables
406  gbl_max_red = 0
407  repro_sum_fast = 0
408  repro_sum_slow = 0
409  repro_sum_both = 0
410  nonrepro_sum = 0
411 
412 ! set MPI communicator
413  if ( present(commid) ) then
414  mpi_comm = commid
415  else
416  mpi_comm = mpi_comm_world
417  endif
418 
419  if (detailed_timing) then
420  call oasis_timer_start('oasis_reprosum_prebarrier')
421  call oasis_mpi_barrier(mpi_comm,subname)
422  call oasis_timer_stop ('oasis_reprosum_prebarrier')
423  endif
424 
425  if ( use_ddpdd_sum ) then
426 
427  if (detailed_timing) call oasis_timer_start('oasis_reprosum_ddpdd')
428 
429  call oasis_reprosum_ddpdd(arr, arr_gsum, nsummands, dsummands, &
430  nflds, mpi_comm)
431  repro_sum_fast = 1
432 
433  if (detailed_timing) call oasis_timer_stop('oasis_reprosum_ddpdd')
434 
435  else
436 
437  if (detailed_timing) call oasis_timer_start('oasis_reprosum_int')
438 
439 ! get number of MPI tasks
440  call mpi_comm_size(mpi_comm, tasks, ierr)
441 
442 ! get number of OpenMP threads
443 #ifdef _OPENMP
444  omp_nthreads = omp_get_max_threads()
445 #else
446  omp_nthreads = 1
447 #endif
448 
449 ! see if have sufficient information to not require max/min allreduce
450  recompute = .true.
451  validate = .false.
452  if ( present(arr_gbl_max) .and. present(arr_max_levels) ) then
453  recompute = .false.
454 
455 ! setting lower bound on max_level*nflds to be 64 to improve OpenMP
456 ! performance for loopb in oasis_reprosum_int
457  max_level = (64/nflds) + 1
458  do ifld=1,nflds
459  if ((arr_gbl_max(ifld) .ge. 0.0_r8) .and. &
460  (arr_max_levels(ifld) > 0)) then
461 
462  arr_gmax_exp(ifld) = exponent(arr_gbl_max(ifld))
463  if (max_level < arr_max_levels(ifld)) &
464  max_level = arr_max_levels(ifld)
465 
466  else
467  recompute = .true.
468  endif
469  enddo
470 
471  if (.not. recompute) then
472 
473 ! determine maximum number of summands in local phases of the
474 ! algorithm
475  if (detailed_timing) call oasis_timer_start("repro_sum_allr_max")
476  if ( present(gbl_max_nsummands) ) then
477  if (gbl_max_nsummands < 1) then
478  call mpi_allreduce (nsummands, max_nsummands, 1, &
479  mpi_integer, mpi_max, mpi_comm, ierr)
480  gbl_max_red = 1
481  else
482  max_nsummands = gbl_max_nsummands
483  endif
484  else
485  call mpi_allreduce (nsummands, max_nsummands, 1, &
486  mpi_integer, mpi_max, mpi_comm, ierr)
487  gbl_max_red = 1
488  endif
489  if (detailed_timing) call oasis_timer_stop("repro_sum_allr_max")
490 
491 ! determine maximum shift. Shift needs to be small enough that summation
492 ! does not exceed maximum number of digits in i8.
493 
494 ! if requested, return max_nsummands before it is redefined
495  if ( present( gbl_max_nsummands_out) ) then
496  gbl_max_nsummands_out = max_nsummands
497  endif
498 
499 ! summing within each thread first
500  max_nsummands = (max_nsummands/omp_nthreads) + 1
501 ! then over threads and tasks
502  max_nsummands = max(max_nsummands, tasks*omp_nthreads)
503 
504  xmax_nsummands = dble(max_nsummands)
505  arr_max_shift = digits(0_i8) - (exponent(xmax_nsummands) + 1)
506  if (arr_max_shift < 2) then
507  write(nulprt,*) subname,estr,' number of summands too large for fixed precision algorithm'
508  call oasis_abort(file=__file__,line=__line__)
509  endif
510 
511 ! calculate sum
512  if (present(repro_sum_validate)) then
513  validate = repro_sum_validate
514  else
515  validate = .true.
516  endif
517  call oasis_reprosum_int(arr, arr_gsum, nsummands, dsummands, &
518  nflds, arr_max_shift, arr_gmax_exp, &
519  arr_max_levels, max_level, validate, &
520  recompute, omp_nthreads, mpi_comm)
521 
522 ! record statistics, etc.
523  repro_sum_fast = 1
524  if (recompute) then
525  repro_sum_both = 1
526  else
527 ! if requested, return specified levels and upper bounds on maxima
528  if ( present(arr_max_levels_out) ) then
529  do ifld=1,nflds
530  arr_max_levels_out(ifld) = arr_max_levels(ifld)
531  enddo
532  endif
533  if ( present(arr_gbl_max_out) ) then
534  do ifld=1,nflds
535  arr_gbl_max_out(ifld) = arr_gbl_max(ifld)
536  enddo
537  endif
538  endif
539  endif
540  endif
541 
542 ! do not have sufficient information; calculate global max/min and
543 ! use to compute required number of levels
544  if (recompute) then
545 
546 ! record statistic
547  repro_sum_slow = 1
548 
549 ! determine maximum and minimum (non-zero) summand values and
550 ! maximum number of local summands
551 
552 ! allocate thread-specific work space
553  allocate(arr_tlmax_exp(nflds,omp_nthreads))
554  allocate(arr_tlmin_exp(nflds,omp_nthreads))
555  allocate(isum_beg(omp_nthreads))
556  allocate(isum_end(omp_nthreads))
557 
558 ! split summand index range over OpenMP threads
559  call split_indices(nsummands, omp_nthreads, isum_beg, isum_end)
560 
561 !$omp parallel do &
562 !$omp default(shared) &
563 !$omp private(ithread, ifld, isum, arr_exp, arr_exp_tlmin, arr_exp_tlmax)
564  do ithread=1,omp_nthreads
565  if (detailed_timing) call oasis_timer_start('repro_sum_loopa')
566  do ifld=1,nflds
567  arr_exp_tlmin = maxexponent(1._r8)
568  arr_exp_tlmax = minexponent(1._r8)
569  do isum=isum_beg(ithread),isum_end(ithread)
570  if (arr(isum,ifld) .ne. 0.0_r8) then
571  arr_exp = exponent(arr(isum,ifld))
572  arr_exp_tlmin = min(arr_exp,arr_exp_tlmin)
573  arr_exp_tlmax = max(arr_exp,arr_exp_tlmax)
574  endif
575  end do
576  arr_tlmin_exp(ifld,ithread) = arr_exp_tlmin
577  arr_tlmax_exp(ifld,ithread) = arr_exp_tlmax
578  end do
579  if (detailed_timing) call oasis_timer_stop('repro_sum_loopa')
580  end do
581 
582  do ifld=1,nflds
583  arr_lmax_exp(ifld) = maxval(arr_tlmax_exp(ifld,:))
584  arr_lmin_exp(ifld) = minval(arr_tlmin_exp(ifld,:))
585  end do
586  deallocate(arr_tlmin_exp,arr_tlmax_exp,isum_beg,isum_end)
587 
588  arr_lextremes(0,:) = -nsummands
589  arr_lextremes(1:nflds,1) = -arr_lmax_exp(:)
590  arr_lextremes(1:nflds,2) = arr_lmin_exp(:)
591  if (detailed_timing) call oasis_timer_start("repro_sum_allr_minmax")
592  call mpi_allreduce (arr_lextremes, arr_gextremes, 2*(nflds+1), &
593  mpi_integer, mpi_min, mpi_comm, ierr)
594  if (detailed_timing) call oasis_timer_stop("repro_sum_allr_minmax")
595  max_nsummands = -arr_gextremes(0,1)
596  arr_gmax_exp(:) = -arr_gextremes(1:nflds,1)
597  arr_gmin_exp(:) = arr_gextremes(1:nflds,2)
598 
599 ! if a field is identically zero, arr_gmin_exp still equals MAXEXPONENT
600 ! and arr_gmax_exp still equals MINEXPONENT. In this case, set
601 ! arr_gmin_exp = arr_gmax_exp = MINEXPONENT
602  do ifld=1,nflds
603  arr_gmin_exp(ifld) = min(arr_gmax_exp(ifld),arr_gmin_exp(ifld))
604  enddo
605 
606 ! if requested, return upper bounds on observed maxima
607  if ( present(arr_gbl_max_out) ) then
608  do ifld=1,nflds
609  arr_gbl_max_out(ifld) = scale(1.0_r8,arr_gmax_exp(ifld))
610  enddo
611  endif
612 
613 ! if requested, return max_nsummands before it is redefined
614  if ( present( gbl_max_nsummands_out) ) then
615  gbl_max_nsummands_out = max_nsummands
616  endif
617 
618 ! determine maximum shift (same as in previous branch, but with calculated
619 ! max_nsummands). Shift needs to be small enough that summation does not
620 ! exceed maximum number of digits in i8.
621 
622 ! summing within each thread first
623  max_nsummands = (max_nsummands/omp_nthreads) + 1
624 ! then over threads and tasks
625  max_nsummands = max(max_nsummands, tasks*omp_nthreads)
626 
627  xmax_nsummands = dble(max_nsummands)
628  arr_max_shift = digits(0_i8) - (exponent(xmax_nsummands) + 1)
629  if (arr_max_shift < 2) then
630  write(nulprt,*) subname,estr,' number of summands too large for fixed precision algorithm'
631  call oasis_abort(file=__file__,line=__line__)
632  endif
633 
634 ! determine maximum number of levels required for each field
635 ! ((digits(0_i8) + (arr_gmax_exp(ifld)-arr_gmin_exp(ifld))) / arr_max_shift)
636 ! + 1 because first truncation probably does not involve a maximal shift
637 ! + 1 to guarantee that the integer division rounds up (not down)
638 ! (setting lower bound on max_level*nflds to be 64 to improve OpenMP
639 ! performance for loopb in oasis_reprosum_int)
640  max_level = (64/nflds) + 1
641  do ifld=1,nflds
642  max_levels(ifld) = 2 + &
643  ((digits(0_i8) + (arr_gmax_exp(ifld)-arr_gmin_exp(ifld))) &
644  / arr_max_shift)
645  if ( present(arr_max_levels) .and. (.not. validate) ) then
646 ! if validate true, then computation with arr_max_levels failed
647 ! previously
648  if ( arr_max_levels(ifld) > 0 ) then
649  max_levels(ifld) = &
650  min(arr_max_levels(ifld),max_levels(ifld))
651  endif
652  endif
653  if (max_level < max_levels(ifld)) &
654  max_level = max_levels(ifld)
655  enddo
656 
657 ! if requested, return calculated levels
658  if ( present(arr_max_levels_out) ) then
659  do ifld=1,nflds
660  arr_max_levels_out(ifld) = max_levels(ifld)
661  enddo
662  endif
663 
664 ! calculate sum
665  validate = .false.
666  call oasis_reprosum_int(arr, arr_gsum, nsummands, dsummands, nflds, &
667  arr_max_shift, arr_gmax_exp, max_levels, &
668  max_level, validate, recompute, &
669  omp_nthreads, mpi_comm)
670 
671  endif
672 
673  if (detailed_timing) call oasis_timer_stop('oasis_reprosum_int')
674 
675  endif
676 
677 ! compare fixed and floating point results
678  if ( present(rel_diff) ) then
679  if (oasis_reprosum_reldiffmax >= 0.0_r8) then
680 
681  if (detailed_timing) then
682  call oasis_timer_start('oasis_nonreprosum_prebarrier')
683  call oasis_mpi_barrier(mpi_comm,subname)
684  call oasis_timer_stop ('oasis_nonreprosum_prebarrier')
685  endif
686 
687  if (detailed_timing) call oasis_timer_start('nonrepro_sum')
688 ! record statistic
689  nonrepro_sum = 1
690 ! compute nonreproducible sum
691  arr_lsum(:) = 0._r8
692 !$omp parallel do &
693 !$omp default(shared) &
694 !$omp private(ifld, isum)
695  do ifld=1,nflds
696  do isum=1,nsummands
697  arr_lsum(ifld) = arr(isum,ifld) + arr_lsum(ifld)
698  end do
699  end do
700 
701  call mpi_allreduce (arr_lsum, arr_gsum_fast, nflds, &
702  mpi_real8, mpi_sum, mpi_comm, ierr)
703 
704  if (detailed_timing) call oasis_timer_stop('nonrepro_sum')
705 
706 ! determine differences
707 !$omp parallel do &
708 !$omp default(shared) &
709 !$omp private(ifld, abs_diff)
710  do ifld=1,nflds
711  abs_diff = abs(arr_gsum_fast(ifld)-arr_gsum(ifld))
712  if (abs(arr_gsum(ifld)) > abs_diff) then
713  rel_diff(1,ifld) = abs_diff/abs(arr_gsum(ifld))
714  else
715  rel_diff(1,ifld) = abs_diff
716  endif
717  rel_diff(2,ifld) = abs_diff
718  enddo
719  else
720  rel_diff(:,:) = 0.0_r8
721  endif
722  endif
723 
724 ! return statistics
725  if ( present(repro_sum_stats) ) then
726  repro_sum_stats(1) = repro_sum_stats(1) + repro_sum_fast
727  repro_sum_stats(2) = repro_sum_stats(2) + repro_sum_slow
728  repro_sum_stats(3) = repro_sum_stats(3) + repro_sum_both
729  repro_sum_stats(4) = repro_sum_stats(4) + nonrepro_sum
730  repro_sum_stats(5) = repro_sum_stats(5) + gbl_max_red
731  endif
732 
733 
734  end subroutine oasis_reprosum_calc
735 
736 !========================================================================
737 !
738 ! Purpose:
739 !> Compute the global sum of each field in "arr" using the indicated
740 !> communicator with a reproducible yet scalable implementation based
741 !> on a fixed point algorithm. The accuracy of the fixed point algorithm
742 !> is controlled by the number of "levels" of integer expansion, the
743 !> maximum value of which is specified by max_level.
744 !
745 !----------------------------------------------------------------------
746 
747  subroutine oasis_reprosum_int (arr, arr_gsum, nsummands, dsummands, nflds, &
748  arr_max_shift, arr_gmax_exp, max_levels, &
749  max_level, validate, recompute, &
750  omp_nthreads, mpi_comm )
752 !----------------------------------------------------------------------
753 
754 ! Arguments
755 
756  integer, intent(in) :: nsummands !< number of local summands
757  integer, intent(in) :: dsummands !< declared first dimension
758  integer, intent(in) :: nflds !< number of fields
759  integer, intent(in) :: arr_max_shift !< maximum safe exponent for
760  !< value < 1 (so that sum
761  !< does not overflow)
762  integer, intent(in) :: arr_gmax_exp(nflds)
763  !< exponents of global maxima
764  integer, intent(in) :: max_levels(nflds)
765  !< maximum number of levels
766  !< of integer expansion
767  integer, intent(in) :: max_level !< maximum value in
768  !< max_levels
769  integer, intent(in) :: omp_nthreads !< number of OpenMP threads
770  integer, intent(in) :: mpi_comm !< MPI subcommunicator
771 
772  real(r8), intent(in) :: arr(dsummands,nflds)
773  !< input array
774 
775  logical, intent(in):: validate
776  !< flag indicating that accuracy of solution generated from
777  !< arr_gmax_exp and max_levels should be tested
778 
779  logical, intent(out):: recompute
780  !< flag indicating that either the upper bounds are inaccurate,
781  !< or max_levels and arr_gmax_exp do not generate accurate
782  !< enough sums
783 
784  real(r8), intent(out):: arr_gsum(nflds) !< global means
785 
786 ! Local workspace
787 
788  integer, parameter :: max_jlevel = &
789  1 + (digits(0_i8)/digits(0.0_r8))
790 
791  integer(i8) :: i8_arr_tlsum_level(0:max_level,nflds,omp_nthreads)
792  ! integer vector representing local
793  ! sum (per thread, per field)
794  integer(i8) :: i8_arr_lsum_level((max_level+3)*nflds)
795  ! integer vector representing local
796  ! sum
797  integer(i8) :: i8_arr_level ! integer part of summand for current
798  ! expansion level
799  integer(i8) :: i8_arr_gsum_level((max_level+3)*nflds)
800  ! integer vector representing global
801  ! sum
802  integer(i8) :: IX_8 ! integer representation of current
803  ! jlevels of X_8 ('part' of
804  ! i8_arr_gsum_level)
805  integer(i8) :: i8_sign ! sign global sum
806  integer(i8) :: i8_radix ! radix for i8 variables
807 
808  integer :: max_error(nflds,omp_nthreads)
809  ! accurate upper bound on data?
810  integer :: not_exact(nflds,omp_nthreads)
811  ! max_levels sufficient to
812  ! capture all digits?
813  integer :: isum_beg(omp_nthreads), isum_end(omp_nthreads)
814  ! range of summand indices for each
815  ! OpenMP thread
816  integer :: ifld, isum, ithread
817  ! loop variables
818  integer :: arr_exp ! exponent of summand
819  integer :: arr_shift ! exponent used to generate integer
820  ! for current expansion level
821  integer :: ilevel ! current integer expansion level
822  integer :: offset(nflds) ! beginning location in
823  ! i8_arr_{g,l}sum_level for integer
824  ! expansion of current ifld
825  integer :: voffset ! modification to offset used to
826  ! include validation metrics
827  integer :: ioffset ! offset(ifld)
828  integer :: jlevel ! number of floating point 'pieces'
829  ! extracted from a given i8 integer
830  integer :: ierr ! MPI error return
831  integer :: LX(max_jlevel) ! exponent of X_8 (see below)
832  integer :: veclth ! total length of i8_arr_lsum_level
833  integer :: sum_digits ! lower bound on number of significant
834  ! in integer expansion of sum
835  integer :: curr_exp ! exponent of partial sum during
836  ! reconstruction from integer vector
837  integer :: corr_exp ! exponent of current summand in
838  ! reconstruction from integer vector
839 
840  real(r8) :: arr_frac ! fraction of summand
841  real(r8) :: arr_remainder ! part of summand remaining after
842  ! current level of integer expansion
843  real(r8) :: X_8(max_jlevel) ! r8 vector representation of current
844  ! i8_arr_gsum_level
845  real(r8) :: RX_8 ! r8 representation of difference
846  ! between current i8_arr_gsum_level
847  ! and current jlevels of X_8
848  ! (== IX_8). Also used in final
849  ! scaling step
850 
851  logical :: first ! flag used to indicate that just
852  ! beginning reconstruction of sum
853  ! from integer vector
854 
855  character(len=*),parameter :: subname = '(oasis_reprosum_int)'
856 
857 !-----------------------------------------------------------------------
858 ! Save radix of i8 variables in an i8 variable
859  i8_radix = radix(ix_8)
860 
861 ! If validating upper bounds, reserve space for validation metrics
862 ! In both cases, reserve an extra level for overflow from the top level
863  if (validate) then
864  voffset = 3
865  else
866  voffset = 1
867  endif
868 
869 ! compute offsets for each field
870  offset(1) = voffset
871  do ifld=2,nflds
872  offset(ifld) = offset(ifld-1) &
873  + (max_levels(ifld-1) + voffset)
874  enddo
875  veclth = offset(nflds) + max_levels(nflds)
876 
877 ! split summand index range over OpenMP threads
878  call split_indices(nsummands, omp_nthreads, isum_beg, isum_end)
879 
880 ! convert local summands to vector of integers and sum
881 ! (Using scale instead of set_exponent because arr_remainder may not be
882 ! "normal" after level 1 calculation)
883  i8_arr_lsum_level(:) = 0_i8
884 
885 !$omp parallel do &
886 !$omp default(shared) &
887 !$omp private(ithread, ifld, ioffset, isum, arr_frac, arr_exp, &
888 !$omp arr_shift, ilevel, i8_arr_level, arr_remainder, RX_8, IX_8)
889  do ithread=1,omp_nthreads
890  if (detailed_timing) call oasis_timer_start('repro_sum_loopb')
891  do ifld=1,nflds
892  ioffset = offset(ifld)
893 
894  max_error(ifld,ithread) = 0
895  not_exact(ifld,ithread) = 0
896 
897  i8_arr_tlsum_level(:,ifld,ithread) = 0_i8
898  do isum=isum_beg(ithread),isum_end(ithread)
899  arr_remainder = 0.0_r8
900 
901  if (arr(isum,ifld) .ne. 0.0_r8) then
902  arr_exp = exponent(arr(isum,ifld))
903  arr_frac = fraction(arr(isum,ifld))
904 
905 ! test that global maximum upper bound is an upper bound
906  if (arr_exp > arr_gmax_exp(ifld)) then
907  max_error(ifld,ithread) = 1
908  exit
909  endif
910 
911 ! calculate first shift
912  arr_shift = arr_max_shift - (arr_gmax_exp(ifld)-arr_exp)
913 
914 ! determine first (probably) nonzero level (assuming initial fraction is
915 ! 'normal' - algorithm still works if this is not true)
916 ! NOTE: this is critical; scale will set to zero if min exponent is too small.
917  if (arr_shift < 1) then
918  ilevel = (1 + (arr_gmax_exp(ifld)-arr_exp))/arr_max_shift
919  arr_shift = ilevel*arr_max_shift - (arr_gmax_exp(ifld)-arr_exp)
920 
921  do while (arr_shift < 1)
922  arr_shift = arr_shift + arr_max_shift
923  ilevel = ilevel + 1
924  enddo
925  else
926  ilevel = 1
927  endif
928 
929  if (ilevel .le. max_levels(ifld)) then
930 ! apply first shift/truncate, add it to the relevant running
931 ! sum, and calculate the remainder.
932  arr_remainder = scale(arr_frac,arr_shift)
933  i8_arr_level = int(arr_remainder,i8)
934  i8_arr_tlsum_level(ilevel,ifld,ithread) = &
935  i8_arr_tlsum_level(ilevel,ifld,ithread) + i8_arr_level
936  arr_remainder = arr_remainder - i8_arr_level
937 
938 ! while the remainder is non-zero, continue to shift, truncate,
939 ! sum, and calculate new remainder
940  do while ((arr_remainder .ne. 0.0_r8) &
941  .and. (ilevel < max_levels(ifld)))
942  ilevel = ilevel + 1
943  arr_remainder = scale(arr_remainder,arr_max_shift)
944  i8_arr_level = int(arr_remainder,i8)
945  i8_arr_tlsum_level(ilevel,ifld,ithread) = &
946  i8_arr_tlsum_level(ilevel,ifld,ithread) + i8_arr_level
947  arr_remainder = arr_remainder - i8_arr_level
948  enddo
949 
950  endif
951  endif
952 
953  if (arr_remainder .ne. 0.0_r8) then
954  not_exact(ifld,ithread) = 1
955  endif
956 
957  enddo
958 
959 ! postprocess integer vector to eliminate potential for overlap in the following
960 ! sums over threads and processes: if value larger than or equal to
961 ! (radix(IX_8)**arr_max_shift), add this 'overlap' to next larger integer in
962 ! vector, resulting in nonoverlapping ranges for each component. Note that
963 ! "ilevel-1==0" corresponds to an extra level used to guarantee that the sums
964 ! over threads and processes do not overflow for ilevel==1.
965  do ilevel=max_levels(ifld),1,-1
966  rx_8 = i8_arr_tlsum_level(ilevel,ifld,ithread)
967  ix_8 = int(scale(rx_8,-arr_max_shift),i8)
968  if (ix_8 .ne. 0_i8) then
969  i8_arr_tlsum_level(ilevel-1,ifld,ithread) = &
970  i8_arr_tlsum_level(ilevel-1,ifld,ithread) + ix_8
971  ix_8 = ix_8*(i8_radix**arr_max_shift)
972  i8_arr_tlsum_level(ilevel,ifld,ithread) = &
973  i8_arr_tlsum_level(ilevel,ifld,ithread) - ix_8
974  endif
975  enddo
976  enddo
977  if (detailed_timing) call oasis_timer_stop('repro_sum_loopb')
978  enddo
979 
980 ! sum contributions from different threads
981  do ifld=1,nflds
982  ioffset = offset(ifld)
983  do ithread = 1,omp_nthreads
984  do ilevel = 0,max_levels(ifld)
985  i8_arr_lsum_level(ioffset+ilevel) = &
986  i8_arr_lsum_level(ioffset+ilevel) &
987  + i8_arr_tlsum_level(ilevel,ifld,ithread)
988  enddo
989  enddo
990  enddo
991 
992 ! record if upper bound was inaccurate or if level expansion stopped
993 ! before full accuracy was achieved
994  if (validate) then
995  do ifld=1,nflds
996  ioffset = offset(ifld)
997  i8_arr_lsum_level(ioffset-voffset+1) = maxval(max_error(ifld,:))
998  i8_arr_lsum_level(ioffset-voffset+2) = maxval(not_exact(ifld,:))
999  enddo
1000  endif
1001 
1002 ! sum integer vector element-wise
1003 #if ( defined noI8 )
1004  ! Workaround for when i8 is not supported.
1005  if (detailed_timing) call oasis_timer_start("repro_sum_allr_i4")
1006  call mpi_allreduce (i8_arr_lsum_level, i8_arr_gsum_level, &
1007  veclth, mpi_integer, mpi_sum, mpi_comm, ierr)
1008  if (detailed_timing) call oasis_timer_stop("repro_sum_allr_i4")
1009 #else
1010  if (detailed_timing) call oasis_timer_start("repro_sum_allr_i8")
1011  call mpi_allreduce (i8_arr_lsum_level, i8_arr_gsum_level, &
1012  veclth, mpi_integer8, mpi_sum, mpi_comm, ierr)
1013  if (detailed_timing) call oasis_timer_stop("repro_sum_allr_i8")
1014 #endif
1015 
1016 ! Construct global sum from integer vector representation:
1017 ! 1) arr_max_shift is the shift applied to fraction(arr_gmax) .
1018 ! When shifting back, need to "add back in" true arr_gmax exponent. This was
1019 ! removed implicitly by working only with the fraction .
1020 ! 2) want to add levels into sum in reverse order (smallest to largest). However,
1021 ! even this can generate floating point rounding errors if signs of integers
1022 ! alternate. To avoid this, do some arithmetic with integer vectors so that all
1023 ! components have the same sign. This should keep relative difference between
1024 ! using different integer sizes (e.g. i8 and i4) to machine epsilon
1025 ! 3) assignment to X_8 will usually lose accuracy since maximum integer
1026 ! size is greater than the max number of 'digits' in r8 value (if xmax_nsummands
1027 ! correction not very large). Calculate remainder and add in first (since
1028 ! smaller). One correction is sufficient for r8 (53 digits) and i8 (63 digits).
1029 ! For r4 (24 digits) may need to correct twice. Code is written in a general
1030 ! fashion, to work no matter how many corrections are necessary (assuming
1031 ! max_jlevel parameter calculation is correct).
1032 
1033  recompute = .false.
1034  do ifld=1,nflds
1035  arr_gsum(ifld) = 0.0_r8
1036  ioffset = offset(ifld)
1037 
1038 ! if validate is .true., test whether the summand upper bound
1039 ! was exceeded on any of the processes
1040  if (validate) then
1041  if (i8_arr_gsum_level(ioffset-voffset+1) .ne. 0_i8) then
1042  recompute = .true.
1043  endif
1044  endif
1045 
1046  if (.not. recompute) then
1047 
1048 ! preprocess integer vector:
1049 ! a) if value larger than or equal to (radix(IX_8)**arr_max_shift), add this 'overlap'
1050 ! to next larger integer in vector, resulting in nonoverlapping ranges for each
1051 ! component. Note that have "ilevel-1=0" level here as described above.
1052  do ilevel=max_levels(ifld),1,-1
1053  rx_8 = i8_arr_gsum_level(ioffset+ilevel)
1054  ix_8 = int(scale(rx_8,-arr_max_shift),i8)
1055  if (ix_8 .ne. 0_i8) then
1056  i8_arr_gsum_level(ioffset+ilevel-1) = i8_arr_gsum_level(ioffset+ilevel-1) &
1057  + ix_8
1058  ix_8 = ix_8*(i8_radix**arr_max_shift)
1059  i8_arr_gsum_level(ioffset+ilevel) = i8_arr_gsum_level(ioffset+ilevel) &
1060  - ix_8
1061  endif
1062  enddo
1063 ! b) subtract +/- 1 from larger and add +/- 1 to smaller when necessary
1064 ! so that all vector components have the same sign (eliminating loss
1065 ! of accuracy arising from difference of large values when
1066 ! reconstructing r8 sum from integer vector)
1067  ilevel = 0
1068  do while ((i8_arr_gsum_level(ioffset+ilevel) .eq. 0_i8) &
1069  .and. (ilevel < max_levels(ifld)))
1070  ilevel = ilevel + 1
1071  enddo
1072 !
1073  if (ilevel < max_levels(ifld)) then
1074  if (i8_arr_gsum_level(ioffset+ilevel) > 0_i8) then
1075  i8_sign = 1_i8
1076  else
1077  i8_sign = -1_i8
1078  endif
1079  do jlevel=ilevel,max_levels(ifld)-1
1080  if (sign(1_i8,i8_arr_gsum_level(ioffset+jlevel)) &
1081  .ne. sign(1_i8,i8_arr_gsum_level(ioffset+jlevel+1))) then
1082  i8_arr_gsum_level(ioffset+jlevel) = i8_arr_gsum_level(ioffset+jlevel) &
1083  - i8_sign
1084  i8_arr_gsum_level(ioffset+jlevel+1) = i8_arr_gsum_level(ioffset+jlevel+1) &
1085  + i8_sign*(i8_radix**arr_max_shift)
1086  endif
1087  enddo
1088  endif
1089 
1090 ! start with maximum shift, and work up to larger values
1091  arr_shift = arr_gmax_exp(ifld) &
1092  - max_levels(ifld)*arr_max_shift
1093  curr_exp = 0
1094  first = .true.
1095  do ilevel=max_levels(ifld),0,-1
1096 
1097  if (i8_arr_gsum_level(ioffset+ilevel) .ne. 0_i8) then
1098  jlevel = 1
1099 
1100 ! r8 representation of higher order bits in integer
1101  x_8(jlevel) = i8_arr_gsum_level(ioffset+ilevel)
1102  lx(jlevel) = exponent(x_8(jlevel))
1103 
1104 ! calculate remainder
1105  ix_8 = int(x_8(jlevel),i8)
1106  rx_8 = (i8_arr_gsum_level(ioffset+ilevel) - ix_8)
1107 
1108 ! repeat using remainder
1109  do while (rx_8 .ne. 0.0_r8)
1110  jlevel = jlevel + 1
1111  x_8(jlevel) = rx_8
1112  lx(jlevel) = exponent(rx_8)
1113  ix_8 = ix_8 + int(rx_8,i8)
1114  rx_8 = (i8_arr_gsum_level(ioffset+ilevel) - ix_8)
1115  enddo
1116 
1117 ! add in contributions, smaller to larger, rescaling for each
1118 ! addition to guarantee that exponent of working summand is always
1119 ! larger than minexponent
1120  do while (jlevel > 0)
1121  if (first) then
1122  curr_exp = lx(jlevel) + arr_shift
1123  arr_gsum(ifld) = fraction(x_8(jlevel))
1124  first = .false.
1125  else
1126  corr_exp = curr_exp - (lx(jlevel) + arr_shift)
1127  arr_gsum(ifld) = fraction(x_8(jlevel)) &
1128  + scale(arr_gsum(ifld),corr_exp)
1129  curr_exp = lx(jlevel) + arr_shift
1130  endif
1131  jlevel = jlevel - 1
1132  enddo
1133 
1134  endif
1135 
1136  arr_shift = arr_shift + arr_max_shift
1137  enddo
1138 
1139 ! apply final exponent correction, scaling first if exponent is too small
1140 ! to apply directly
1141  corr_exp = curr_exp + exponent(arr_gsum(ifld))
1142  if (corr_exp .ge. minexponent(1._r8)) then
1143  arr_gsum(ifld) = set_exponent(arr_gsum(ifld),corr_exp)
1144  else
1145  rx_8 = set_exponent(arr_gsum(ifld), &
1146  corr_exp-minexponent(1._r8))
1147  arr_gsum(ifld) = scale(rx_8,minexponent(1._r8))
1148  endif
1149 
1150 ! if validate is .true. and some precision lost, test whether 'too much'
1151 ! was lost, due to too loose an upper bound, too stringent a limit on number
1152 ! of levels of expansion, cancellation, .... Calculated by comparing lower
1153 ! bound on number of sigificant digits with number of digits in 1.0_r8 .
1154  if (validate) then
1155  if (i8_arr_gsum_level(ioffset-voffset+2) .ne. 0_i8) then
1156 
1157 ! find first nonzero level and use exponent for this level, then assume all
1158 ! subsequent levels contribute arr_max_shift digits.
1159  sum_digits = 0
1160  do ilevel=0,max_levels(ifld)
1161  if (sum_digits .eq. 0) then
1162  if (i8_arr_gsum_level(ioffset+ilevel) .ne. 0_i8) then
1163  x_8(1) = i8_arr_gsum_level(ioffset+ilevel)
1164  lx(1) = exponent(x_8(1))
1165  sum_digits = lx(1)
1166  endif
1167  else
1168  sum_digits = sum_digits + arr_max_shift
1169  endif
1170  enddo
1171 
1172  if (sum_digits < digits(1.0_r8)) then
1173  recompute = .true.
1174  endif
1175  endif
1176  endif
1177 
1178  endif
1179 
1180  enddo
1181 
1182 
1183  end subroutine oasis_reprosum_int
1184 
1185 !========================================================================
1186 !
1187 ! Purpose:
1188 !> Test whether distributed sum exceeds tolerance and print out a
1189 !> warning message.
1190 !
1191 !----------------------------------------------------------------------
1192 
1193  logical function oasis_reprosum_tolexceeded (name, nflds, master, &
1194  logunit, rel_diff )
1195 !----------------------------------------------------------------------
1196 
1197 ! Arguments
1198 
1199  character(len=*), intent(in) :: name !< distributed sum identifier
1200  integer, intent(in) :: nflds !< number of fields
1201  logical, intent(in) :: master !< process that will write
1202  !< warning messages?
1203  integer, optional, intent(in) :: logunit!< unit warning messages
1204  !< written to
1205  real(r8), intent(in) :: rel_diff(2,nflds)
1206  !< relative and absolute
1207  !< differences between fixed
1208  !< and floating point sums
1209 
1210 ! Local workspace
1211 
1212  integer :: llogunit ! local log unit
1213  integer :: ifld ! field index
1214  integer :: exceeds_limit ! number of fields whose
1215  ! sum exceeds tolerance
1216  real(r8) :: max_rel_diff ! maximum relative difference
1217  integer :: max_rel_diff_idx ! field index for max. rel. diff.
1218  real(r8) :: max_abs_diff ! maximum absolute difference
1219  integer :: max_abs_diff_idx ! field index for max. abs. diff.
1220  character(len=*),parameter :: subname = '(oasis_reprosum_tolExceeded)'
1221 
1222 !-----------------------------------------------------------------------
1223 
1224  oasis_reprosum_tolexceeded = .false.
1225  if (oasis_reprosum_reldiffmax < 0.0_r8) return
1226 
1227  if ( present(logunit) ) then
1228  llogunit = logunit
1229  else
1230  llogunit = nulprt
1231  endif
1232 
1233  ! check that "fast" reproducible sum is accurate enough.
1234  exceeds_limit = 0
1235  max_rel_diff = 0.0_r8
1236  max_abs_diff = 0.0_r8
1237  do ifld=1,nflds
1238  if (rel_diff(1,ifld) > oasis_reprosum_reldiffmax) then
1239  exceeds_limit = exceeds_limit + 1
1240  if (rel_diff(1,ifld) > max_rel_diff) then
1241  max_rel_diff = rel_diff(1,ifld)
1242  max_rel_diff_idx = ifld
1243  endif
1244  if (rel_diff(2,ifld) > max_abs_diff) then
1245  max_abs_diff = rel_diff(2,ifld)
1246  max_abs_diff_idx = ifld
1247  endif
1248  endif
1249  enddo
1250 
1251  if (exceeds_limit > 0) then
1252  if (master) then
1253  write(llogunit,*) subname,trim(name), &
1254  ': difference in fixed and floating point sums ', &
1255  ' exceeds tolerance in ', exceeds_limit, &
1256  ' fields.'
1257  write(llogunit,*) subname,' Maximum relative diff: (rel)', &
1258  rel_diff(1,max_rel_diff_idx), ' (abs) ', &
1259  rel_diff(2,max_rel_diff_idx)
1260  write(llogunit,*) subname,' Maximum absolute diff: (rel)', &
1261  rel_diff(1,max_abs_diff_idx), ' (abs) ', &
1262  rel_diff(2,max_abs_diff_idx)
1263  endif
1265  endif
1266 
1267 
1268  end function oasis_reprosum_tolexceeded
1269 
1270 !========================================================================
1271 !
1272 ! Purpose:
1273 !> Compute the global sum of each field in "arr" using the indicated
1274 !> communicator with a reproducible yet scalable implementation based
1275 !> on He and Ding's implementation of the double-double algorithm.
1276 !
1277 !----------------------------------------------------------------------
1278 
1279  subroutine oasis_reprosum_ddpdd (arr, arr_gsum, nsummands, dsummands, &
1280  nflds, mpi_comm )
1281 !----------------------------------------------------------------------
1282 
1283 ! Arguments
1284 
1285  integer, intent(in) :: nsummands !< number of local summands
1286  integer, intent(in) :: dsummands !< declared first dimension
1287  integer, intent(in) :: nflds !< number of fields
1288  real(r8), intent(in) :: arr(dsummands,nflds)
1289  !< input array
1290  integer, intent(in) :: mpi_comm !< MPI subcommunicator
1291 
1292  real(r8), intent(out):: arr_gsum(nflds)
1293  !< global sums
1294 
1295 
1296 ! Local workspace
1297 
1298  integer :: old_cw ! for x86 processors, save
1299  ! current arithmetic mode
1300  integer :: ifld, isum ! loop variables
1301  integer :: ierr ! MPI error return
1302 
1303  real(r8) :: e, t1, t2 ! temporaries
1304  complex(r8) :: arr_lsum_dd(nflds) ! local sums (in double-double
1305  ! format)
1306  complex(r8) :: arr_gsum_dd(nflds) ! global sums (in double-double
1307  ! format)
1308 
1309  integer, save :: mpi_sumdd
1310  logical, save :: first_time = .true.
1311  character(len=*),parameter :: subname = '(oasis_reprosum_ddpdd)'
1312 
1313 !-----------------------------------------------------------------------
1314 
1315  call shr_reprosumx86_fix_start (old_cw)
1316 
1317  if (first_time) then
1318  call mpi_op_create(ddpdd, .true., mpi_sumdd, ierr)
1319  first_time = .false.
1320  endif
1321 
1322  do ifld=1,nflds
1323  arr_lsum_dd(ifld) = (0.0_r8,0.0_r8)
1324 
1325  do isum=1,nsummands
1326 
1327  ! Compute arr(isum,ifld) + arr_lsum_dd(ifld) using Knuth''s
1328  ! trick.
1329  t1 = arr(isum,ifld) + real(arr_lsum_dd(ifld))
1330  e = t1 - arr(isum,ifld)
1331  t2 = ((real(arr_lsum_dd(ifld)) - e) &
1332  + (arr(isum,ifld) - (t1 - e))) &
1333  + aimag(arr_lsum_dd(ifld))
1334 
1335  ! The result is t1 + t2, after normalization.
1336  arr_lsum_dd(ifld) = cmplx( t1 + t2, t2 - ((t1 + t2) - t1), r8 )
1337  enddo
1338 
1339  enddo
1340 
1341  call mpi_allreduce (arr_lsum_dd, arr_gsum_dd, nflds, &
1342  mpi_complex16, mpi_sumdd, mpi_comm, ierr)
1343  do ifld=1,nflds
1344  arr_gsum(ifld) = real(arr_gsum_dd(ifld))
1345  enddo
1346 
1347  call shr_reprosumx86_fix_end (old_cw)
1348 
1349  end subroutine oasis_reprosum_ddpdd
1350 
1351 !-----------------------------------------------------------------------
1352 
1353  subroutine ddpdd (dda, ddb, len, itype)
1354 !----------------------------------------------------------------------
1355 !
1356 ! Purpose:
1357 ! Modification of original codes written by David H. Bailey
1358 ! This subroutine computes ddb(i) = dda(i)+ddb(i)
1359 !
1360 !----------------------------------------------------------------------
1361 
1362 ! Arguments
1363 
1364  integer, intent(in) :: len ! array length
1365  complex(r8), intent(in) :: dda(len) ! input
1366  complex(r8), intent(inout) :: ddb(len) ! result
1367  integer, intent(in) :: itype ! unused
1368 
1369 ! Local workspace
1370 
1371  real(r8) e, t1, t2
1372  integer i
1373  character(len=*),parameter :: subname = '(oasis_reprosum_mod:DDPDD)'
1374 
1375 !-----------------------------------------------------------------------
1376 
1377  do i = 1, len
1378 ! Compute dda + ddb using Knuth's trick.
1379  t1 = real(dda(i)) + real(ddb(i))
1380  e = t1 - real(dda(i))
1381  t2 = ((real(ddb(i)) - e) + (real(dda(i)) - (t1 - e))) &
1382  + aimag(dda(i)) + aimag(ddb(i))
1383 
1384 ! The result is t1 + t2, after normalization.
1385  ddb(i) = cmplx( t1 + t2, t2 - ((t1 + t2) - t1), r8 )
1386  enddo
1387 
1388 
1389  end subroutine ddpdd
1390 
1391 !-----------------------------------------------------------------------
1392 
1393  subroutine split_indices(total,num_pieces,ibeg,iend)
1394 !----------------------------------------------------------------------
1395 !
1396 ! Purpose:
1397 ! Split range into 'num_pieces'
1398 !
1399 !----------------------------------------------------------------------
1400 
1401 ! Arguments
1402 
1403  integer, intent(in) :: total
1404  integer, intent(in) :: num_pieces
1405  integer, intent(out) :: ibeg(num_pieces), iend(num_pieces)
1406 
1407 ! Local workspace
1408 
1409  integer :: itmp1, itmp2, ioffset, i
1410  character(len=*),parameter :: subname = '(oasis_reprosum_mod:split_indices)'
1411 
1412 !-----------------------------------------------------------------------
1413 
1414  itmp1 = total/num_pieces
1415  itmp2 = mod(total,num_pieces)
1416  ioffset = 0
1417  do i=1,itmp2
1418  ibeg(i) = ioffset + 1
1419  iend(i) = ioffset + (itmp1+1)
1420  ioffset = iend(i)
1421  enddo
1422  do i=itmp2+1,num_pieces
1423  ibeg(i) = ioffset + 1
1424  if (ibeg(i) > total) then
1425  iend(i) = ibeg(i) - 1
1426  else
1427  iend(i) = ioffset + itmp1
1428  ioffset = iend(i)
1429  endif
1430  enddo
1431 
1432  end subroutine split_indices
1433 
1434 !========================================================================
1435 
1436 end module mod_oasis_reprosum
System type methods.
subroutine oasis_reprosum_int(arr, arr_gsum, nsummands, dsummands, nflds, arr_max_shift, arr_gmax_exp, max_levels, max_level, validate, recompute, omp_nthreads, mpi_comm)
Compute the global sum of each field in "arr" using the indicated communicator with a reproducible ye...
Provides a common location for several OASIS variables.
subroutine, public oasis_reprosum_calc(arr, arr_gsum, nsummands, dsummands, nflds, ddpdd_sum, arr_gbl_max, arr_gbl_max_out, arr_max_levels, arr_max_levels_out, gbl_max_nsummands, gbl_max_nsummands_out, gbl_count, repro_sum_validate, repro_sum_stats, rel_diff, commid)
Compute the global sum of each field in "arr" using the indicated communicator with a reproducible ye...
integer(kind=ip_intwp_p) nulprt
subroutine, public oasis_reprosum_setopts(repro_sum_use_ddpdd_in, repro_sum_rel_diff_max_in, repro_sum_recompute_in, repro_sum_master, repro_sum_logunit)
Set runtime options.
subroutine, public oasis_abort(id_compid, cd_routine, cd_message, file, line, rcode)
OASIS abort method, publically available to users.
integer, parameter ip_i4_p
integer, parameter ip_r8_p
subroutine, private ddpdd(dda, ddb, len, itype)
Defines kinds for OASIS.
real(r8), public oasis_reprosum_reldiffmax
Provides a generic and simpler interface into MPI calls for OASIS.
logical function, public oasis_reprosum_tolexceeded(name, nflds, master, logunit, rel_diff)
Test whether distributed sum exceeds tolerance and print out a warning message.
subroutine, public oasis_timer_start(timer_label, barrier)
Start a timer.
subroutine, public oasis_timer_stop(timer_label)
Stop a timer.
character(len= *), parameter, public estr
subroutine, public oasis_mpi_barrier(comm, string)
Call MPI_BARRIER for a particular communicator.
subroutine, private split_indices(total, num_pieces, ibeg, iend)
subroutine oasis_reprosum_ddpdd(arr, arr_gsum, nsummands, dsummands, nflds, mpi_comm)
Compute the global sum of each field in "arr" using the indicated communicator with a reproducible ye...
integer, parameter ip_i8_p
Performance timer methods.
OASIS reproducible sum method from P. Worley.
logical, public oasis_reprosum_recompute