← back to index

src/framework/MOM_checksums.F90

portedportable, not yet portedexecuted, not portableexecutable, not hit by this run

1! This file is part of MOM6, the Modular Ocean Model version 6.
2! See the LICENSE file for licensing information.
3! SPDX-License-Identifier: Apache-2.0
4
5!> Routines to calculate checksums of various array and vector types
6module MOM_checksums
7
8use MOM_array_transform, only : rotate_array, rotate_array_pair, rotate_vector
9use MOM_array_transform, only : allocate_rotated_array
10use MOM_coms, only : PE_here, root_PE, num_PEs, sum_across_PEs
11use MOM_coms, only : min_across_PEs, max_across_PEs
12use MOM_coms, only : reproducing_sum, field_chksum
13use MOM_error_handler, only : MOM_error, FATAL, is_root_pe
14use MOM_file_parser, only : log_version, param_file_type
15use MOM_hor_index, only : hor_index_type, rotate_hor_index
16use MOM_murmur_hash, only : murmur_hash
17
18use iso_fortran_env, only : error_unit, int32, int64
19
20implicit none ; private
21
22public :: chksum0, zchksum, rotated_field_chksum, field_checksum
23public :: hchksum, Bchksum, uchksum, vchksum, qchksum, is_NaN, chksum
24public :: hchksum_pair, uvchksum, Bchksum_pair
25public :: MOM_checksums_init
26
27! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional
28! consistency testing. These are noted in comments with units like Z, H, L, and T, along with
29! their mks counterparts with notation like "a velocity [Z T-1 ~> m s-1]". If the units
30! vary with the Boussinesq approximation, the Boussinesq variant is given first.
31! The functions in this module work with variables with arbitrary units, in which case the
32! arbitrary rescaled units are indicated with [A ~> a], while the unscaled units are just [a].
33
34!> Checksums a pair of arrays (2d or 3d) staggered at tracer points
35interface hchksum_pair
36 module procedure chksum_pair_h_2d, chksum_pair_h_3d
37end interface
38
39!> Checksums a pair velocity arrays (2d or 3d) staggered at C-grid locations
40interface uvchksum
41 module procedure chksum_uv_2d, chksum_uv_3d
42end interface
43
44!> Checksums an array (2d or 3d) staggered at C-grid u points.
45interface uchksum
46 module procedure chksum_u_2d, chksum_u_3d
47end interface
48
49!> Checksums an array (2d or 3d) staggered at C-grid v points.
50interface vchksum
51 module procedure chksum_v_2d, chksum_v_3d
52end interface
53
54!> Checksums a pair of arrays (2d or 3d) staggered at corner points
55interface Bchksum_pair
56 module procedure chksum_pair_B_2d, chksum_pair_B_3d
57end interface
58
59!> Checksums an array (2d or 3d) staggered at tracer points.
60interface hchksum
61 module procedure chksum_h_2d, chksum_h_3d
62end interface
63
64!> Checksums an array (2d or 3d) staggered at corner points.
65interface Bchksum
66 module procedure chksum_B_2d, chksum_B_3d
67end interface
68
69!> This is an older interface that has been renamed Bchksum
70interface qchksum
71 module procedure chksum_B_2d, chksum_B_3d
72end interface
73
74!> This is an older interface for 1-, 2-, or 3-D checksums
75interface chksum
76 module procedure chksum1d, chksum2d, chksum3d
77end interface
78
79!> Write a message with either checksums or numerical statistics of arrays
80interface chk_sum_msg
81 module procedure chk_sum_msg1, chk_sum_msg2, chk_sum_msg3, chk_sum_msg5
82end interface
83
84!> Returns .true. if any element of x is a NaN, and .false. otherwise.
85interface is_NaN
86 module procedure is_NaN_0d, is_NaN_1d, is_NaN_2d, is_NaN_3d
87end interface
88
89!> Compute the checksum on all elements of a field that may need to be rotated or unscaled.
90!! This interface uses the field_chksum function that is used to verify file contents, which
91!! may differ from the bitcount function used for other checksums in this module.
92interface rotated_field_chksum
93 module procedure field_checksum_real_0d
94 module procedure field_checksum_real_1d
95 module procedure field_checksum_real_2d
96 module procedure field_checksum_real_3d
97 module procedure field_checksum_real_4d
98end interface rotated_field_chksum
99
100
101!> Compute the checksum on all elements of a field that may need to be rotated or unscaled.
102!! This interface uses the field_chksum function that is used to verify file contents, which
103!! may differ from the bitcount function used for other checksums in this module.
104interface field_checksum
105 module procedure field_checksum_real_0d
106 module procedure field_checksum_real_1d
107 module procedure field_checksum_real_2d
108 module procedure field_checksum_real_3d
109 module procedure field_checksum_real_4d
110end interface field_checksum
111
112integer, parameter :: bc_modulus = 1000000000 !< Modulus of checksum bitcount
113integer, parameter :: default_shift=0 !< The default array shift
114logical :: calculateStatistics=.true. !< If true, report min, max and mean.
115logical :: writeChksums=.true. !< If true, report the bitcount checksum
116logical :: checkForNaNs=.true. !< If true, checks array for NaNs and cause
117 !! FATAL error if any are found
118logical :: writeHash = .false. !< If true, report the murmur hash
119 !! NOTE: writeHash is currently disabled due to non-compliant diagnostics.
120
121contains
122
123!> Checksum a scalar field (consistent with array checksums)
1240subroutine chksum0(scalar, mesg, scale, logunit, unscale)
125 real, intent(in) :: scalar !< The array to be checksummed in
126 !! arbitrary, possibly rescaled units [A ~> a]
127 character(len=*), intent(in) :: mesg !< An identifying message
128 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
129 !! for checksums and output [a A-1 ~> 1]
130 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
131 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
132 !! for checksums and output [a A-1 ~> 1].
133 !! Here scale and unscale are synonymous, but unscale
134 !! takes precedence if both are present.
135
136 ! Local variables
137 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
138 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
139 ! for checksums and output
140 real :: scaling !< Explicit rescaling factor [a A-1 ~> 1]
141 integer :: iounit !< Log IO unit
142 real :: rs !< Rescaled scalar [a]
143 integer :: bc !< Scalar bitcount
144
1450 if (checkForNaNs .and. is_NaN(scalar)) &
1460 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
147
1480 scaling = 1.0
1490 if (present(unscale)) then ; scaling = unscale
1500 elseif (present(scale)) then ; scaling = scale ; endif
151
1520 iounit = error_unit ; if (present(logunit)) iounit = logunit
153
1540 if (calculateStatistics) then
1550 rs = scaling * scalar
1560 if (is_root_pe()) &
1570 call chk_sum_msg(" scalar:", rs, rs, rs, mesg, iounit)
158 endif
159
1600 if (.not. writeChksums) return
161
1620 bc = mod(bitcount(abs(scaling * scalar)), bc_modulus)
1630 if (is_root_pe()) &
1640 call chk_sum_msg(" scalar:", bc, mesg, iounit)
165
1660 if (writeHash .and. is_root_pe()) &
167 write(iounit, '(" scalar: hash=", z8, 1x, a)') &
1680 murmur_hash(scaling * scalar), mesg
1690end subroutine chksum0
170
171
172!> Checksum a 1d array (typically a column).
1730subroutine zchksum(array, mesg, scale, logunit, unscale)
174 real, dimension(:), intent(in) :: array !< The array to be checksummed in
175 !! arbitrary, possibly rescaled units [A ~> a]
176 character(len=*), intent(in) :: mesg !< An identifying message
177 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
178 !! for checksums and output [a A-1 ~> 1]
179 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
180 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
181 !! for checksums and output [a A-1 ~> 1].
182 !! Here scale and unscale are synonymous, but unscale
183 !! takes precedence if both are present.
184
185 ! Local variables
186 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
187 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
188 ! for checksums and output
1890 real, allocatable, dimension(:) :: rescaled_array ! The array with scaling undone [a]
190 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
191 integer :: iounit !< Log IO unit
192 integer :: k
193 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
194 integer :: bc0
195
1960 if (checkForNaNs) then
1970 if (is_NaN(array(:))) &
1980 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
199 endif
200
2010 scaling = 1.0
2020 if (present(unscale)) then ; scaling = unscale
2030 elseif (present(scale)) then ; scaling = scale ; endif
204
2050 iounit = error_unit ; if (present(logunit)) iounit = logunit
206
2070 if (calculateStatistics) then
2080 if (present(unscale) .or. present(scale)) then
2090 allocate(rescaled_array(LBOUND(array,1):UBOUND(array,1)), source=0.0)
2100 do k=1, size(array, 1)
2110 rescaled_array(k) = scaling * array(k)
212 enddo
213
2140 call subStats(rescaled_array, aMean, aMin, aMax)
2150 deallocate(rescaled_array)
216 else
2170 call subStats(array, aMean, aMin, aMax)
218 endif
219
2200 if (is_root_pe()) &
2210 call chk_sum_msg(" column:", aMean, aMin, aMax, mesg, iounit)
222 endif
223
2240 if (.not. writeChksums) return
225
2260 bc0 = subchk(array, scaling)
2270 if (is_root_pe()) call chk_sum_msg(" column:", bc0, mesg, iounit)
228
2290 if (writeHash .and. is_root_pe()) &
230 write(iounit, '(" column: hash=", z8, 1x, a)') &
2310 murmur_hash(scaling * array), mesg
232
233 contains
234
2350 integer function subchk(array, unscale)
236 real, dimension(:), intent(in) :: array !< The array to be checksummed in
237 !! arbitrary, possibly rescaled units [A ~> a]
238 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
239 !! for checksums and output [a A-1 ~> 1]
240 integer :: k, bc
2410 subchk = 0
2420 do k=LBOUND(array, 1), UBOUND(array, 1)
2430 bc = bitcount(abs(unscale * array(k)))
2440 subchk = subchk + bc
245 enddo
2460 subchk=mod(subchk, bc_modulus)
2470 end function subchk
248
2490 subroutine subStats(array, aMean, aMin, aMax)
250 real, dimension(:), intent(in) :: array !< The array to be checksummed [a]
251 real, intent(out) :: aMean !< Array mean [a]
252 real, intent(out) :: aMin !< Array minimum [a]
253 real, intent(out) :: aMax !< Array maximum [a]
254
255 integer :: k, n
256
2570 aMin = array(1)
2580 aMax = array(1)
2590 n = 0
2600 do k=LBOUND(array,1), UBOUND(array,1)
2610 aMin = min(aMin, array(k))
2620 aMax = max(aMax, array(k))
2630 n = n + 1
264 enddo
2650 aMean = sum(array(:)) / real(n)
2660 end subroutine subStats
267end subroutine zchksum
268
269!> Checksums on a pair of 2d arrays staggered at tracer points.
2700subroutine chksum_pair_h_2d(mesg, arrayA, arrayB, HI, haloshift, omit_corners, &
271 scale, logunit, scalar_pair, unscale)
272 character(len=*), intent(in) :: mesg !< Identifying messages
273 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
274 real, dimension(HI%isd:,HI%jsd:), target, intent(in) :: arrayA !< The first array to be checksummed in
275 !! arbitrary, possibly rescaled units [A ~> a]
276 real, dimension(HI%isd:,HI%jsd:), target, intent(in) :: arrayB !< The second array to be checksummed in
277 !! arbitrary, possibly rescaled units [A ~> a]
278 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
279 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
280 real, optional, intent(in) :: scale !< A factor to convert these arrays back to unscaled
281 !! units for checksums and output [a A-1 ~> 1]
282 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
283 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe
284 !! a scalar, rather than vector
285 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
286 !! for checksums and output [a A-1 ~> 1].
287 !! Here scale and unscale are synonymous, but unscale
288 !! takes precedence if both are present.
289 logical :: vector_pair
290 integer :: turns
291 type(hor_index_type), pointer :: HI_in
2920 real, dimension(:,:), pointer :: arrayA_in, arrayB_in ! Rotated arrays [A ~> a]
293
2940 vector_pair = .true.
2950 if (present(scalar_pair)) vector_pair = .not. scalar_pair
296
2970 turns = HI%turns
2980 if (modulo(turns, 4) /= 0) then
299 ! Rotate field back to the input grid
3000 allocate(HI_in)
3010 call rotate_hor_index(HI, -turns, HI_in)
3020 allocate(arrayA_in(HI_in%isd:HI_in%ied, HI_in%jsd:HI_in%jed))
3030 allocate(arrayB_in(HI_in%isd:HI_in%ied, HI_in%jsd:HI_in%jed))
304
3050 if (vector_pair) then
3060 call rotate_vector(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
307 else
3080 call rotate_array_pair(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
309 endif
310 else
3110 HI_in => HI
3120 arrayA_in => arrayA
3130 arrayB_in => arrayB
314 endif
315
3160 if (present(haloshift)) then
317 call chksum_h_2d(arrayA_in, 'x '//mesg, HI_in, haloshift, omit_corners, &
3180 scale=scale, logunit=logunit, unscale=unscale)
319 call chksum_h_2d(arrayB_in, 'y '//mesg, HI_in, haloshift, omit_corners, &
3200 scale=scale, logunit=logunit, unscale=unscale)
321 else
3220 call chksum_h_2d(arrayA_in, 'x '//mesg, HI_in, scale=scale, logunit=logunit, unscale=unscale)
3230 call chksum_h_2d(arrayB_in, 'y '//mesg, HI_in, scale=scale, logunit=logunit, unscale=unscale)
324 endif
3250end subroutine chksum_pair_h_2d
326
327!> Checksums on a pair of 3d arrays staggered at tracer points.
3280subroutine chksum_pair_h_3d(mesg, arrayA, arrayB, HI, haloshift, omit_corners, &
329 scale, logunit, scalar_pair, unscale)
330 character(len=*), intent(in) :: mesg !< Identifying messages
331 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
332 real, dimension(HI%isd:,HI%jsd:, :), target, intent(in) :: arrayA !< The first array to be checksummed in
333 !! arbitrary, possibly rescaled units [A ~> a]
334 real, dimension(HI%isd:,HI%jsd:, :), target, intent(in) :: arrayB !< The second array to be checksummed in
335 !! arbitrary, possibly rescaled units [A ~> a]
336 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
337 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
338 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
339 !! for checksums and output [a A-1 ~> 1]
340 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
341 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe
342 !! a scalar, rather than vector
343 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
344 !! for checksums and output [a A-1 ~> 1].
345 !! Here scale and unscale are synonymous, but unscale
346 !! takes precedence if both are present.
347 ! Local variables
348 logical :: vector_pair
349 integer :: turns
350 type(hor_index_type), pointer :: HI_in
3510 real, dimension(:,:,:), pointer :: arrayA_in, arrayB_in ! Rotated arrays [A ~> a]
352
3530 vector_pair = .true.
3540 if (present(scalar_pair)) vector_pair = .not. scalar_pair
355
3560 turns = HI%turns
3570 if (modulo(turns, 4) /= 0) then
358 ! Rotate field back to the input grid
3590 allocate(HI_in)
3600 call rotate_hor_index(HI, -turns, HI_in)
3610 allocate(arrayA_in(HI_in%isd:HI_in%ied, HI_in%jsd:HI_in%jed, size(arrayA, 3)))
3620 allocate(arrayB_in(HI_in%isd:HI_in%ied, HI_in%jsd:HI_in%jed, size(arrayB, 3)))
363
3640 if (vector_pair) then
3650 call rotate_vector(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
366 else
3670 call rotate_array_pair(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
368 endif
369 else
3700 HI_in => HI
3710 arrayA_in => arrayA
3720 arrayB_in => arrayB
373 endif
374
3750 if (present(haloshift)) then
376 call chksum_h_3d(arrayA_in, 'x '//mesg, HI_in, haloshift, omit_corners, &
3770 scale=scale, logunit=logunit, unscale=unscale)
378 call chksum_h_3d(arrayB_in, 'y '//mesg, HI_in, haloshift, omit_corners, &
3790 scale=scale, logunit=logunit, unscale=unscale)
380 else
3810 call chksum_h_3d(arrayA_in, 'x '//mesg, HI_in, scale=scale, logunit=logunit, unscale=unscale)
3820 call chksum_h_3d(arrayB_in, 'y '//mesg, HI_in, scale=scale, logunit=logunit, unscale=unscale)
383 endif
384
385 ! NOTE: automatic deallocation of array[AB]_in
3860end subroutine chksum_pair_h_3d
387
388!> Checksums a 2d array staggered at tracer points.
3890subroutine chksum_h_2d(array_m, mesg, HI_m, haloshift, omit_corners, scale, logunit, unscale)
390 type(hor_index_type), target, intent(in) :: HI_m !< Horizontal index bounds of the model grid
391 real, dimension(HI_m%isd:,HI_m%jsd:), target, intent(in) :: array_m !< Field array on the model grid in
392 !! arbitrary, possibly rescaled units [A ~> a]
393 character(len=*), intent(in) :: mesg !< An identifying message
394 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
395 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
396 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
397 !! for checksums and output [a A-1 ~> 1]
398 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
399 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
400 !! for checksums and output [a A-1 ~> 1].
401 !! Here scale and unscale are synonymous, but unscale
402 !! takes precedence if both are present.
403
404 ! Local variables
405 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
406 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
407 ! for checksums and output
4080 real, pointer :: array(:,:) ! Field array on the input grid [A ~> a]
4090 real, allocatable, dimension(:,:) :: rescaled_array ! The array with scaling undone [a]
4100 real, allocatable :: hash_array(:,:) ! Subarray used to compute hash [a]
411 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
412 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
413 integer :: iounit !< Log IO unit
414 integer :: i, j
415 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
416 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
417 integer :: bcN, bcS, bcE, bcW
418 logical :: do_corners
419 integer :: turns ! Quarter turns from input to model grid
420
421
422 ! Rotate array to the input grid
4230 turns = HI_m%turns
4240 if (modulo(turns, 4) /= 0) then
4250 allocate(HI)
4260 call rotate_hor_index(HI_m, -turns, HI)
4270 allocate(array(HI%isd:HI%ied, HI%jsd:HI%jed))
4280 call rotate_array(array_m, -turns, array)
429 else
4300 HI => HI_m
4310 array => array_m
432 endif
433
4340 if (checkForNaNs) then
4350 if (is_NaN(array(HI%isc:HI%iec,HI%jsc:HI%jec))) &
4360 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
437! if (is_NaN(array)) &
438! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
439 endif
440
4410 scaling = 1.0
4420 if (present(unscale)) then ; scaling = unscale
4430 elseif (present(scale)) then ; scaling = scale ; endif
444
4450 iounit = error_unit ; if (present(logunit)) iounit = logunit
446
4470 if (calculateStatistics) then
4480 if (present(unscale) .or. present(scale)) then
4490 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
4500 LBOUND(array,2):UBOUND(array,2)), source=0.0 )
4510 do j=HI%jsc,HI%jec ; do i=HI%isc,HI%iec
4520 rescaled_array(i,j) = scaling*array(i,j)
453 enddo ; enddo
4540 call subStats(HI, rescaled_array, aMean, aMin, aMax)
4550 deallocate(rescaled_array)
456 else
4570 call subStats(HI, array, aMean, aMin, aMax)
458 endif
459
4600 if (is_root_pe()) &
4610 call chk_sum_msg("h-point:", aMean, aMin, aMax, mesg, iounit)
462 endif
463
4640 if (.not.writeChksums) return
465
4660 hshift = default_shift
4670 if (present(haloshift)) hshift = haloshift
4680 if (hshift<0) hshift = HI%ied-HI%iec
469
470 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
4710 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
4720 write(0,*) 'chksum_h_2d: haloshift =',hshift
4730 write(0,*) 'chksum_h_2d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
4740 write(0,*) 'chksum_h_2d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
4750 call chksum_error(FATAL,'Error in chksum_h_2d '//trim(mesg))
476 endif
477
4780 bc0 = subchk(array, HI, 0, 0, scaling)
479
4800 if (hshift==0) then
4810 if (is_root_pe()) call chk_sum_msg("h-point:", bc0, mesg, iounit)
482 else
4830 do_corners = .true.
4840 if (present(omit_corners)) do_corners = .not. omit_corners
485
4860 if (do_corners) then
4870 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
4880 bcSE = subchk(array, HI, hshift, -hshift, scaling)
4890 bcNW = subchk(array, HI, -hshift, hshift, scaling)
4900 bcNE = subchk(array, HI, hshift, hshift, scaling)
491
4920 if (is_root_pe()) &
4930 call chk_sum_msg("h-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
494 else
4950 bcS = subchk(array, HI, 0, -hshift, scaling)
4960 bcE = subchk(array, HI, hshift, 0, scaling)
4970 bcW = subchk(array, HI, -hshift, 0, scaling)
4980 bcN = subchk(array, HI, 0, hshift, scaling)
499
5000 if (is_root_pe()) &
5010 call chk_sum_msg_NSEW("h-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
502 endif
503 endif
504
5050 if (writeHash .and. is_root_pe()) then
5060 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec))
5070 hash_array(:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec)
508
509 write(iounit, '("h-point: hash=", z8, 1x, a)') &
5100 murmur_hash(hash_array), mesg
5110 deallocate(hash_array)
512 endif
513
514 contains
5150 integer function subchk(array, HI, di, dj, unscale)
516 type(hor_index_type), intent(in) :: HI !< A horizontal index type
517 real, dimension(HI%isd:,HI%jsd:), intent(in) :: array !< The array to be checksummed in
518 !! arbitrary, possibly rescaled units [A ~> a]
519 integer, intent(in) :: di !< i- direction array shift for this checksum
520 integer, intent(in) :: dj !< j- direction array shift for this checksum
521 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
522 !! for checksums and output [a A-1 ~> 1]
523 integer :: i, j, bc
5240 subchk = 0
5250 do j=HI%jsc+dj,HI%jec+dj ; do i=HI%isc+di,HI%iec+di
5260 bc = bitcount(abs(unscale*array(i,j)))
5270 subchk = subchk + bc
528 enddo ; enddo
5290 call sum_across_PEs(subchk)
5300 subchk=mod(subchk, bc_modulus)
5310 end function subchk
532
5330 subroutine subStats(HI, array, aMean, aMin, aMax)
534 type(hor_index_type), intent(in) :: HI !< A horizontal index type
535 real, dimension(HI%isd:,HI%jsd:), intent(in) :: array !< The array to be checksummed [a]
536 real, intent(out) :: aMean !< Array mean [a]
537 real, intent(out) :: aMin !< Array minimum [a]
538 real, intent(out) :: aMax !< Array maximum [a]
539
540 integer :: i, j, n
541
5420 aMin = array(HI%isc,HI%jsc)
5430 aMax = array(HI%isc,HI%jsc)
5440 n = 0
5450 do j=HI%jsc,HI%jec ; do i=HI%isc,HI%iec
5460 aMin = min(aMin, array(i,j))
5470 aMax = max(aMax, array(i,j))
5480 n = n + 1
549 enddo ; enddo
5500 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec))
5510 call sum_across_PEs(n)
5520 call min_across_PEs(aMin)
5530 call max_across_PEs(aMax)
5540 aMean = aMean / real(n)
5550 end subroutine subStats
556
557end subroutine chksum_h_2d
558
559!> Checksums on a pair of 2d arrays staggered at q-points.
5600subroutine chksum_pair_B_2d(mesg, arrayA, arrayB, HI, haloshift, symmetric, &
561 omit_corners, scale, logunit, scalar_pair, unscale)
562 character(len=*), intent(in) :: mesg !< Identifying messages
563 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
564 real, dimension(HI%isd:,HI%jsd:), target, intent(in) :: arrayA !< The first array to be checksummed in
565 !! arbitrary, possibly rescaled units [A ~> a]
566 real, dimension(HI%isd:,HI%jsd:), target, intent(in) :: arrayB !< The second array to be checksummed in
567 !! arbitrary, possibly rescaled units [A ~> a]
568 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
569 !! symmetric computational domain.
570 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
571 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
572 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
573 !! for checksums and output [a A-1 ~> 1]
574 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
575 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe
576 !! a scalar, rather than vector
577 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
578 !! for checksums and output [a A-1 ~> 1].
579 !! Here scale and unscale are synonymous, but unscale
580 !! takes precedence if both are present.
581
582 logical :: sym
583 logical :: vector_pair
584 integer :: turns
585 type(hor_index_type), pointer :: HI_in
5860 real, dimension(:,:), pointer :: arrayA_in, arrayB_in ! Rotated arrays [A ~> a]
587
5880 vector_pair = .true.
5890 if (present(scalar_pair)) vector_pair = .not. scalar_pair
590
5910 turns = HI%turns
5920 if (modulo(turns, 4) /= 0) then
593 ! Rotate field back to the input grid
5940 allocate(HI_in)
5950 call rotate_hor_index(HI, -turns, HI_in)
5960 allocate(arrayA_in(HI_in%IsdB:HI_in%IedB, HI_in%JsdB:HI_in%JedB))
5970 allocate(arrayB_in(HI_in%IsdB:HI_in%IedB, HI_in%JsdB:HI_in%JedB))
598
5990 if (vector_pair) then
6000 call rotate_vector(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
601 else
6020 call rotate_array_pair(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
603 endif
604 else
6050 HI_in => HI
6060 arrayA_in => arrayA
6070 arrayB_in => arrayB
608 endif
609
6100 sym = .false. ; if (present(symmetric)) sym = symmetric
611
6120 if (present(haloshift)) then
613 call chksum_B_2d(arrayA_in, 'x '//mesg, HI_in, haloshift, symmetric=sym, &
6140 omit_corners=omit_corners, scale=scale, logunit=logunit, unscale=unscale)
615 call chksum_B_2d(arrayB_in, 'y '//mesg, HI_in, haloshift, symmetric=sym, &
6160 omit_corners=omit_corners, scale=scale, logunit=logunit, unscale=unscale)
617 else
618 call chksum_B_2d(arrayA_in, 'x '//mesg, HI_in, symmetric=sym, &
6190 scale=scale, logunit=logunit, unscale=unscale)
620 call chksum_B_2d(arrayB_in, 'y '//mesg, HI_in, symmetric=sym, &
6210 scale=scale, logunit=logunit, unscale=unscale)
622 endif
623
6240end subroutine chksum_pair_B_2d
625
626!> Checksums on a pair of 3d arrays staggered at q-points.
6270subroutine chksum_pair_B_3d(mesg, arrayA, arrayB, HI, haloshift, symmetric, &
628 omit_corners, scale, logunit, scalar_pair, unscale)
629 character(len=*), intent(in) :: mesg !< Identifying messages
630 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
631 real, dimension(HI%IsdB:,HI%JsdB:, :), target, intent(in) :: arrayA !< The first array to be checksummed in
632 !! arbitrary, possibly rescaled units [A ~> a]
633 real, dimension(HI%IsdB:,HI%JsdB:, :), target, intent(in) :: arrayB !< The second array to be checksummed in
634 !! arbitrary, possibly rescaled units [A ~> a]
635 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
636 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
637 !! symmetric computational domain.
638 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
639 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
640 !! for checksums and output [a A-1 ~> 1]
641 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
642 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe
643 !! a scalar, rather than vector
644 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
645 !! for checksums and output [a A-1 ~> 1].
646 !! Here scale and unscale are synonymous, but unscale
647 !! takes precedence if both are present.
648 ! Local variables
649 logical :: vector_pair
650 integer :: turns
651 type(hor_index_type), pointer :: HI_in
6520 real, dimension(:,:,:), pointer :: arrayA_in, arrayB_in ! Rotated arrays [A ~> a]
653
6540 vector_pair = .true.
6550 if (present(scalar_pair)) vector_pair = .not. scalar_pair
656
6570 turns = HI%turns
6580 if (modulo(turns, 4) /= 0) then
659 ! Rotate field back to the input grid
6600 allocate(HI_in)
6610 call rotate_hor_index(HI, -turns, HI_in)
6620 allocate(arrayA_in(HI_in%IsdB:HI_in%IedB, HI_in%JsdB:HI_in%JedB, size(arrayA, 3)))
6630 allocate(arrayB_in(HI_in%IsdB:HI_in%IedB, HI_in%JsdB:HI_in%JedB, size(arrayB, 3)))
664
6650 if (vector_pair) then
6660 call rotate_vector(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
667 else
6680 call rotate_array_pair(arrayA, arrayB, -turns, arrayA_in, arrayB_in)
669 endif
670 else
6710 HI_in => HI
6720 arrayA_in => arrayA
6730 arrayB_in => arrayB
674 endif
675
6760 if (present(haloshift)) then
677 call chksum_B_3d(arrayA_in, 'x '//mesg, HI_in, haloshift, symmetric, &
6780 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
679 call chksum_B_3d(arrayB_in, 'y '//mesg, HI_in, haloshift, symmetric, &
6800 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
681 else
682 call chksum_B_3d(arrayA_in, 'x '//mesg, HI_in, symmetric=symmetric, &
6830 scale=scale, logunit=logunit, unscale=unscale)
684 call chksum_B_3d(arrayB_in, 'y '//mesg, HI_in, symmetric=symmetric, &
6850 scale=scale, logunit=logunit, unscale=unscale)
686 endif
6870end subroutine chksum_pair_B_3d
688
689!> Checksums a 2d array staggered at corner points.
6900subroutine chksum_B_2d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
691 scale, logunit, unscale)
692 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
693 real, dimension(HI_m%IsdB:,HI_m%JsdB:), &
694 target, intent(in) :: array_m !< The array to be checksummed in
695 !! arbitrary, possibly rescaled units [A ~> a]
696 character(len=*), intent(in) :: mesg !< An identifying message
697 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
698 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the
699 !! full symmetric computational domain.
700 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
701 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
702 !! for checksums and output [a A-1 ~> 1]
703 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
704 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
705 !! for checksums and output [a A-1 ~> 1].
706 !! Here scale and unscale are synonymous, but unscale
707 !! takes precedence if both are present.
708
709 ! Local variables
710 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
711 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
712 ! for checksums and output
7130 real, pointer :: array(:,:) ! Field array on the input grid [A ~> a]
7140 real, allocatable, dimension(:,:) :: rescaled_array ! The array with scaling undone [a]
7150 real, allocatable :: hash_array(:,:) ! Subarray used to compute hash [a]
716 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
717 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
718 integer :: iounit !< Log IO unit
719 integer :: i, j, Is, Js
720 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
721 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
722 integer :: bcN, bcS, bcE, bcW
723 logical :: do_corners, sym, sym_stats
724 integer :: turns ! Quarter turns from input to model grid
725
726 ! Rotate array to the input grid
7270 turns = HI_m%turns
7280 if (modulo(turns, 4) /= 0) then
7290 allocate(HI)
7300 call rotate_hor_index(HI_m, -turns, HI)
7310 allocate(array(HI%IsdB:HI%IedB, HI%JsdB:HI%JedB))
7320 call rotate_array(array_m, -turns, array)
733 else
7340 HI => HI_m
7350 array => array_m
736 endif
737
7380 if (checkForNaNs) then
7390 if (is_NaN(array(HI%IscB:HI%IecB,HI%JscB:HI%JecB))) &
7400 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
741! if (is_NaN(array)) &
742! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
743 endif
744
7450 scaling = 1.0
7460 if (present(unscale)) then ; scaling = unscale
7470 elseif (present(scale)) then ; scaling = scale ; endif
748
7490 iounit = error_unit ; if (present(logunit)) iounit = logunit
7500 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
7510 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
752
7530 if (calculateStatistics) then
7540 if (present(unscale) .or. present(scale)) then
7550 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
7560 LBOUND(array,2):UBOUND(array,2)), source=0.0 )
7570 Is = HI%isc ; if (sym_stats) Is = HI%isc-1
7580 Js = HI%jsc ; if (sym_stats) Js = HI%jsc-1
7590 do J=Js,HI%JecB ; do I=Is,HI%IecB
7600 rescaled_array(I,J) = scaling*array(I,J)
761 enddo ; enddo
7620 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
7630 deallocate(rescaled_array)
764 else
7650 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
766 endif
7670 if (is_root_pe()) &
7680 call chk_sum_msg("B-point:", aMean, aMin, aMax, mesg, iounit)
769 endif
770
7710 if (.not.writeChksums) return
772
7730 hshift = default_shift
7740 if (present(haloshift)) hshift = haloshift
7750 if (hshift<0) hshift = HI%ied-HI%iec
776
777 if ( HI%iscB-hshift<HI%isdB .or. HI%iecB+hshift>HI%iedB .or. &
7780 HI%jscB-hshift<HI%jsdB .or. HI%jecB+hshift>HI%jedB ) then
7790 write(0,*) 'chksum_B_2d: haloshift =',hshift
7800 write(0,*) 'chksum_B_2d: isd,isc,iec,ied=',HI%isdB,HI%iscB,HI%iecB,HI%iedB
7810 write(0,*) 'chksum_B_2d: jsd,jsc,jec,jed=',HI%jsdB,HI%jscB,HI%jecB,HI%jedB
7820 call chksum_error(FATAL,'Error in chksum_B_2d '//trim(mesg))
783 endif
784
7850 bc0 = subchk(array, HI, 0, 0, scaling)
786
7870 sym = .false. ; if (present(symmetric)) sym = symmetric
788
7890 if ((hshift==0) .and. .not.sym) then
7900 if (is_root_pe()) call chk_sum_msg("B-point:", bc0, mesg, iounit)
791 else
7920 do_corners = .true.
7930 if (present(omit_corners)) do_corners = .not. omit_corners
794
7950 if (do_corners) then
7960 if (sym) then
7970 bcSW = subchk(array, HI, -hshift-1, -hshift-1, scaling)
7980 bcSE = subchk(array, HI, hshift, -hshift-1, scaling)
7990 bcNW = subchk(array, HI, -hshift-1, hshift, scaling)
800 else
8010 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
8020 bcSE = subchk(array, HI, hshift, -hshift, scaling)
8030 bcNW = subchk(array, HI, -hshift, hshift, scaling)
804 endif
8050 bcNE = subchk(array, HI, hshift, hshift, scaling)
806
8070 if (is_root_pe()) &
8080 call chk_sum_msg("B-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
809 else
8100 bcS = subchk(array, HI, 0, -hshift, scaling)
8110 bcE = subchk(array, HI, hshift, 0, scaling)
8120 bcW = subchk(array, HI, -hshift, 0, scaling)
8130 bcN = subchk(array, HI, 0, hshift, scaling)
814
8150 if (is_root_pe()) &
8160 call chk_sum_msg_NSEW("B-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
817 endif
818 endif
819
8200 if (writeHash .and. is_root_pe()) then
8210 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec))
8220 hash_array(:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec)
823
824 write(iounit, '("B-point: hash=", z8, 1x, a)') &
8250 murmur_hash(hash_array), mesg
8260 deallocate(hash_array)
827 endif
828
829 contains
830
8310 integer function subchk(array, HI, di, dj, unscale)
832 type(hor_index_type), intent(in) :: HI !< A horizontal index type
833 real, dimension(HI%IsdB:,HI%JsdB:), intent(in) :: array !< The array to be checksummed in
834 !! arbitrary, possibly rescaled units [A ~> a]
835 integer, intent(in) :: di !< i- direction array shift for this checksum
836 integer, intent(in) :: dj !< j- direction array shift for this checksum
837 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
838 !! for checksums and output [a A-1 ~> 1]
839 integer :: i, j, bc
8400 subchk = 0
841 ! This line deliberately uses the h-point computational domain.
8420 do J=HI%jsc+dj,HI%jec+dj ; do I=HI%isc+di,HI%iec+di
8430 bc = bitcount(abs(unscale*array(I,J)))
8440 subchk = subchk + bc
845 enddo ; enddo
8460 call sum_across_PEs(subchk)
8470 subchk=mod(subchk, bc_modulus)
8480 end function subchk
849
8500 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
851 type(hor_index_type), intent(in) :: HI !< A horizontal index type
852 real, dimension(HI%IsdB:,HI%JsdB:), intent(in) :: array !< The array to be checksummed [a]
853 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
854 !! full symmetric computational domain.
855 real, intent(out) :: aMean !< Array mean [a]
856 real, intent(out) :: aMin !< Array minimum [a]
857 real, intent(out) :: aMax !< Array maximum [a]
858
859 integer :: i, j, n, IsB, JsB
860
8610 IsB = HI%isc ; if (sym_stats) IsB = HI%isc-1
8620 JsB = HI%jsc ; if (sym_stats) JsB = HI%jsc-1
863
8640 aMin = array(HI%isc,HI%jsc) ; aMax = aMin
8650 do J=JsB,HI%JecB ; do I=IsB,HI%IecB
8660 aMin = min(aMin, array(I,J))
8670 aMax = max(aMax, array(I,J))
868 enddo ; enddo
869 ! This line deliberately uses the h-point computational domain.
8700 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec))
8710 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc)
8720 call sum_across_PEs(n)
8730 call min_across_PEs(aMin)
8740 call max_across_PEs(aMax)
8750 aMean = aMean / real(n)
8760 end subroutine subStats
877
878end subroutine chksum_B_2d
879
880!> Checksums a pair of 2d velocity arrays staggered at C-grid locations
8810subroutine chksum_uv_2d(mesg, arrayU, arrayV, HI, haloshift, symmetric, &
882 omit_corners, scale, logunit, scalar_pair, unscale)
883 character(len=*), intent(in) :: mesg !< Identifying messages
884 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
885 real, dimension(HI%IsdB:,HI%jsd:), target, intent(in) :: arrayU !< The u-component array to be checksummed in
886 !! arbitrary, possibly rescaled units [A ~> a]
887 real, dimension(HI%isd:,HI%JsdB:), target, intent(in) :: arrayV !< The v-component array to be checksummed in
888 !! arbitrary, possibly rescaled units [A ~> a]
889 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
890 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
891 !! symmetric computational domain.
892 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
893 real, optional, intent(in) :: scale !< A factor to convert these arrays back to unscaled
894 !! units for checksums and output [a A-1 ~> 1]
895 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
896 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe a
897 !! a scalar, rather than vector
898 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
899 !! for checksums and output [a A-1 ~> 1].
900 !! Here scale and unscale are synonymous, but unscale
901 !! takes precedence if both are present.
902 ! Local variables
903 logical :: vector_pair
904 integer :: turns
905 type(hor_index_type), pointer :: HI_in
9060 real, dimension(:,:), pointer :: arrayU_in, arrayV_in ! Rotated arrays [A ~> a]
907
9080 vector_pair = .true.
9090 if (present(scalar_pair)) vector_pair = .not. scalar_pair
910
9110 turns = HI%turns
9120 if (modulo(turns, 4) /= 0) then
913 ! Rotate field back to the input grid
9140 allocate(HI_in)
9150 call rotate_hor_index(HI, -turns, HI_in)
9160 allocate(arrayU_in(HI_in%IsdB:HI_in%IedB, HI_in%jsd:HI_in%jed))
9170 allocate(arrayV_in(HI_in%isd:HI_in%ied, HI_in%JsdB:HI_in%JedB))
918
9190 if (vector_pair) then
9200 call rotate_vector(arrayU, arrayV, -turns, arrayU_in, arrayV_in)
921 else
9220 call rotate_array_pair(arrayU, arrayV, -turns, arrayU_in, arrayV_in)
923 endif
924 else
9250 HI_in => HI
9260 arrayU_in => arrayU
9270 arrayV_in => arrayV
928 endif
929
9300 if (present(haloshift)) then
931 call chksum_u_2d(arrayU_in, 'u '//mesg, HI_in, haloshift, symmetric, &
9320 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
933 call chksum_v_2d(arrayV_in, 'v '//mesg, HI_in, haloshift, symmetric, &
9340 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
935 else
936 call chksum_u_2d(arrayU_in, 'u '//mesg, HI_in, symmetric=symmetric, &
9370 scale=scale, logunit=logunit, unscale=unscale)
938 call chksum_v_2d(arrayV_in, 'v '//mesg, HI_in, symmetric=symmetric, &
9390 scale=scale, logunit=logunit, unscale=unscale)
940 endif
9410end subroutine chksum_uv_2d
942
943!> Checksums a pair of 3d velocity arrays staggered at C-grid locations
9440subroutine chksum_uv_3d(mesg, arrayU, arrayV, HI, haloshift, symmetric, &
945 omit_corners, scale, logunit, scalar_pair, unscale)
946 character(len=*), intent(in) :: mesg !< Identifying messages
947 type(hor_index_type), target, intent(in) :: HI !< A horizontal index type
948 real, dimension(HI%IsdB:,HI%jsd:,:), target, intent(in) :: arrayU !< The u-component array to be checksummed in
949 !! arbitrary, possibly rescaled units [A ~> a]
950 real, dimension(HI%isd:,HI%JsdB:,:), target, intent(in) :: arrayV !< The v-component array to be checksummed in
951 !! arbitrary, possibly rescaled units [A ~> a]
952 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
953 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
954 !! symmetric computational domain.
955 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
956 real, optional, intent(in) :: scale !< A factor to convert these arrays back to unscaled
957 !! units for checksums and output [a A-1 ~> 1]
958 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
959 logical, optional, intent(in) :: scalar_pair !< If true, then the arrays describe a
960 !! a scalar, rather than vector
961 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
962 !! for checksums and output [a A-1 ~> 1].
963 !! Here scale and unscale are synonymous, but unscale
964 !! takes precedence if both are present.
965 ! Local variables
966 logical :: vector_pair
967 integer :: turns
968 type(hor_index_type), pointer :: HI_in
9690 real, dimension(:,:,:), pointer :: arrayU_in, arrayV_in ! Rotated arrays [A ~> a]
970
9710 vector_pair = .true.
9720 if (present(scalar_pair)) vector_pair = .not. scalar_pair
973
9740 turns = HI%turns
9750 if (modulo(turns, 4) /= 0) then
976 ! Rotate field back to the input grid
9770 allocate(HI_in)
9780 call rotate_hor_index(HI, -turns, HI_in)
9790 allocate(arrayU_in(HI_in%IsdB:HI_in%IedB, HI_in%jsd:HI_in%jed, size(arrayU, 3)))
9800 allocate(arrayV_in(HI_in%isd:HI_in%ied, HI_in%JsdB:HI_in%JedB, size(arrayV, 3)))
981
9820 if (vector_pair) then
9830 call rotate_vector(arrayU, arrayV, -turns, arrayU_in, arrayV_in)
984 else
9850 call rotate_array_pair(arrayU, arrayV, -turns, arrayU_in, arrayV_in)
986 endif
987 else
9880 HI_in => HI
9890 arrayU_in => arrayU
9900 arrayV_in => arrayV
991 endif
992
9930 if (present(haloshift)) then
994 call chksum_u_3d(arrayU_in, 'u '//mesg, HI_in, haloshift, symmetric, &
9950 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
996 call chksum_v_3d(arrayV_in, 'v '//mesg, HI_in, haloshift, symmetric, &
9970 omit_corners, scale=scale, logunit=logunit, unscale=unscale)
998 else
999 call chksum_u_3d(arrayU_in, 'u '//mesg, HI_in, symmetric=symmetric, &
10000 scale=scale, logunit=logunit, unscale=unscale)
1001 call chksum_v_3d(arrayV_in, 'v '//mesg, HI_in, symmetric=symmetric, &
10020 scale=scale, logunit=logunit, unscale=unscale)
1003 endif
10040end subroutine chksum_uv_3d
1005
1006!> Checksums a 2d array staggered at C-grid u points.
10070subroutine chksum_u_2d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
1008 scale, logunit, unscale)
1009 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1010 real, dimension(HI_m%IsdB:,HI_m%jsd:), target, intent(in) :: array_m !< The array to be checksummed in
1011 !! arbitrary, possibly rescaled units [A ~> a]
1012 character(len=*), intent(in) :: mesg !< An identifying message
1013 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1014 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
1015 !! symmetric computational domain.
1016 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1017 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1018 !! for checksums and output [a A-1 ~> 1]
1019 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
1020 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1021 !! for checksums and output [a A-1 ~> 1].
1022 !! Here scale and unscale are synonymous, but unscale
1023 !! takes precedence if both are present.
1024
1025 ! Local variables
1026 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
1027 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
1028 ! for checksums and output
10290 real, pointer :: array(:,:) ! Field array on the input grid [A ~> a]
10300 real, allocatable, dimension(:,:) :: rescaled_array ! The array with scaling undone [a]
10310 real, allocatable :: hash_array(:,:) ! Subarray used to compute hash [a]
1032 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
1033 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
1034 integer :: iounit !< Log IO unit
1035 integer :: i, j, Is
1036 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
1037 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
1038 integer :: bcN, bcS, bcE, bcW
1039 logical :: do_corners, sym, sym_stats
1040 integer :: turns ! Quarter turns from input to model grid
1041
1042 ! Rotate array to the input grid
10430 turns = HI_m%turns
10440 if (modulo(turns, 4) /= 0) then
10450 allocate(HI)
10460 call rotate_hor_index(HI_m, -turns, HI)
10470 if (modulo(turns, 2) /= 0) then
1048 ! Arrays originating from v-points must be handled by vchksum
10490 allocate(array(HI%isd:HI%ied, HI%JsdB:HI%JedB))
10500 call rotate_array(array_m, -turns, array)
1051 call vchksum(array, mesg, HI, haloshift, symmetric, omit_corners, &
10520 scale=scale, logunit=logunit, unscale=unscale)
10530 return
1054 else
10550 allocate(array(HI%IsdB:HI%IedB, HI%jsd:HI%jed))
10560 call rotate_array(array_m, -turns, array)
1057 endif
1058 else
10590 HI => HI_m
10600 array => array_m
1061 endif
1062
10630 if (checkForNaNs) then
10640 if (is_NaN(array(HI%IscB:HI%IecB,HI%jsc:HI%jec))) &
10650 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
1066! if (is_NaN(array)) &
1067! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
1068 endif
1069
10700 scaling = 1.0
10710 if (present(unscale)) then ; scaling = unscale
10720 elseif (present(scale)) then ; scaling = scale ; endif
1073
10740 iounit = error_unit ; if (present(logunit)) iounit = logunit
10750 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
10760 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
1077
10780 if (calculateStatistics) then
10790 if (present(unscale) .or. present(scale)) then
10800 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
10810 LBOUND(array,2):UBOUND(array,2)), source=0.0 )
10820 Is = HI%isc ; if (sym_stats) Is = HI%isc-1
10830 do j=HI%jsc,HI%jec ; do I=Is,HI%IecB
10840 rescaled_array(I,j) = scaling*array(I,j)
1085 enddo ; enddo
10860 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
10870 deallocate(rescaled_array)
1088 else
10890 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
1090 endif
1091
10920 if (is_root_pe()) &
10930 call chk_sum_msg("u-point:", aMean, aMin, aMax, mesg, iounit)
1094 endif
1095
10960 if (.not.writeChksums) return
1097
10980 hshift = default_shift
10990 if (present(haloshift)) hshift = haloshift
11000 if (hshift<0) hshift = HI%iedB-HI%iecB
1101
1102 if ( HI%iscB-hshift<HI%isdB .or. HI%iecB+hshift>HI%iedB .or. &
11030 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
11040 write(0,*) 'chksum_u_2d: haloshift =',hshift
11050 write(0,*) 'chksum_u_2d: isd,isc,iec,ied=',HI%isdB,HI%iscB,HI%iecB,HI%iedB
11060 write(0,*) 'chksum_u_2d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
11070 call chksum_error(FATAL,'Error in chksum_u_2d '//trim(mesg))
1108 endif
1109
11100 bc0 = subchk(array, HI, 0, 0, scaling)
1111
11120 sym = .false. ; if (present(symmetric)) sym = symmetric
1113
11140 if ((hshift==0) .and. .not.sym) then
11150 if (is_root_pe()) call chk_sum_msg("u-point:", bc0, mesg, iounit)
1116 else
11170 do_corners = .true.
11180 if (present(omit_corners)) do_corners = .not. omit_corners
1119
11200 if (hshift==0) then
11210 bcW = subchk(array, HI, -hshift-1, 0, scaling)
11220 if (is_root_pe()) call chk_sum_msg_W("u-point:", bc0, bcW, mesg, iounit)
11230 elseif (do_corners) then
11240 if (sym) then
11250 bcSW = subchk(array, HI, -hshift-1, -hshift, scaling)
11260 bcNW = subchk(array, HI, -hshift-1, hshift, scaling)
1127 else
11280 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
11290 bcNW = subchk(array, HI, -hshift, hshift, scaling)
1130 endif
11310 bcSE = subchk(array, HI, hshift, -hshift, scaling)
11320 bcNE = subchk(array, HI, hshift, hshift, scaling)
1133
11340 if (is_root_pe()) &
11350 call chk_sum_msg("u-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
1136 else
11370 bcS = subchk(array, HI, 0, -hshift, scaling)
11380 bcE = subchk(array, HI, hshift, 0, scaling)
11390 if (sym) then
11400 bcW = subchk(array, HI, -hshift-1, 0, scaling)
1141 else
11420 bcW = subchk(array, HI, -hshift, 0, scaling)
1143 endif
11440 bcN = subchk(array, HI, 0, hshift, scaling)
1145
11460 if (is_root_pe()) &
11470 call chk_sum_msg_NSEW("u-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
1148 endif
1149 endif
1150
11510 if (writeHash .and. is_root_pe()) then
11520 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec))
11530 hash_array(:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec)
1154
1155 write(iounit, '("u-point: hash=", z8, 1x, a)') &
11560 murmur_hash(hash_array), mesg
11570 deallocate(hash_array)
1158 endif
1159
1160 contains
1161
11620 integer function subchk(array, HI, di, dj, unscale)
1163 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1164 real, dimension(HI%IsdB:,HI%jsd:), intent(in) :: array !< The array to be checksummed in
1165 !! arbitrary, possibly rescaled units [A ~> a]
1166 integer, intent(in) :: di !< i- direction array shift for this checksum
1167 integer, intent(in) :: dj !< j- direction array shift for this checksum
1168 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1169 !! for checksums and output [a A-1 ~> 1]
1170 integer :: i, j, bc
11710 subchk = 0
1172 ! This line deliberately uses the h-point computational domain.
11730 do j=HI%jsc+dj,HI%jec+dj ; do I=HI%isc+di,HI%iec+di
11740 bc = bitcount(abs(unscale*array(I,j)))
11750 subchk = subchk + bc
1176 enddo ; enddo
11770 call sum_across_PEs(subchk)
11780 subchk=mod(subchk, bc_modulus)
11790 end function subchk
1180
11810 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
1182 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1183 real, dimension(HI%IsdB:,HI%jsd:), intent(in) :: array !< The array to be checksummed [a]
1184 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
1185 !! full symmetric computational domain.
1186 real, intent(out) :: aMean !< Array mean [a]
1187 real, intent(out) :: aMin !< Array minimum [a]
1188 real, intent(out) :: aMax !< Array maximum [a]
1189
1190 integer :: i, j, n, IsB
1191
11920 IsB = HI%isc ; if (sym_stats) IsB = HI%isc-1
1193
11940 aMin = array(HI%isc,HI%jsc) ; aMax = aMin
11950 do j=HI%jsc,HI%jec ; do I=IsB,HI%IecB
11960 aMin = min(aMin, array(I,j))
11970 aMax = max(aMax, array(I,j))
1198 enddo ; enddo
1199 ! This line deliberately uses the h-point computational domain.
12000 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec))
12010 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc)
12020 call sum_across_PEs(n)
12030 call min_across_PEs(aMin)
12040 call max_across_PEs(aMax)
12050 aMean = aMean / real(n)
12060 end subroutine subStats
1207
1208end subroutine chksum_u_2d
1209
1210!> Checksums a 2d array staggered at C-grid v points.
12110subroutine chksum_v_2d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
1212 scale, logunit, unscale)
1213 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1214 real, dimension(HI_m%isd:,HI_m%JsdB:), target, intent(in) :: array_m !< The array to be checksummed in
1215 !! arbitrary, possibly rescaled units [A ~> a]
1216 character(len=*), intent(in) :: mesg !< An identifying message
1217 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1218 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
1219 !! symmetric computational domain.
1220 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1221 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1222 !! for checksums and output [a A-1 ~> 1]
1223 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
1224 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1225 !! for checksums and output [a A-1 ~> 1].
1226 !! Here scale and unscale are synonymous, but unscale
1227 !! takes precedence if both are present.
1228
1229 ! Local variables
1230 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
1231 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
1232 ! for checksums and output
12330 real, pointer :: array(:,:) ! Field array on the input grid [A ~> a]
12340 real, allocatable, dimension(:,:) :: rescaled_array ! The array with scaling undone [a]
12350 real, allocatable :: hash_array(:,:) ! Subarray used to compute hash [a]
1236 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
1237 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
1238 integer :: iounit !< Log IO unit
1239 integer :: i, j, Js
1240 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
1241 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
1242 integer :: bcN, bcS, bcE, bcW
1243 logical :: do_corners, sym, sym_stats
1244 integer :: turns ! Quarter turns from input to model grid
1245
1246 ! Rotate array to the input grid
12470 turns = HI_m%turns
12480 if (modulo(turns, 4) /= 0) then
12490 allocate(HI)
12500 call rotate_hor_index(HI_m, -turns, HI)
12510 if (modulo(turns, 2) /= 0) then
1252 ! Arrays originating from u-points must be handled by uchksum
12530 allocate(array(HI%IsdB:HI%IedB, HI%jsd:HI%jed))
12540 call rotate_array(array_m, -turns, array)
1255 call uchksum(array, mesg, HI, haloshift, symmetric, omit_corners, &
12560 scale=scale, logunit=logunit, unscale=unscale)
12570 return
1258 else
12590 allocate(array(HI%isd:HI%ied, HI%JsdB:HI%JedB))
12600 call rotate_array(array_m, -turns, array)
1261 endif
1262 else
12630 HI => HI_m
12640 array => array_m
1265 endif
1266
12670 if (checkForNaNs) then
12680 if (is_NaN(array(HI%isc:HI%iec,HI%JscB:HI%JecB))) &
12690 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
1270! if (is_NaN(array)) &
1271! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
1272 endif
1273
12740 scaling = 1.0
12750 if (present(unscale)) then ; scaling = unscale
12760 elseif (present(scale)) then ; scaling = scale ; endif
1277
12780 iounit = error_unit ; if (present(logunit)) iounit = logunit
12790 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
12800 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
1281
12820 if (calculateStatistics) then
12830 if (present(unscale) .or. present(scale)) then
12840 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
12850 LBOUND(array,2):UBOUND(array,2)), source=0.0 )
12860 Js = HI%jsc ; if (sym_stats) Js = HI%jsc-1
12870 do J=Js,HI%JecB ; do i=HI%isc,HI%iec
12880 rescaled_array(i,J) = scaling*array(i,J)
1289 enddo ; enddo
12900 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
12910 deallocate(rescaled_array)
1292 else
12930 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
1294 endif
1295
12960 if (is_root_pe()) &
12970 call chk_sum_msg("v-point:", aMean, aMin, aMax, mesg, iounit)
1298 endif
1299
13000 if (.not.writeChksums) return
1301
13020 hshift = default_shift
13030 if (present(haloshift)) hshift = haloshift
13040 if (hshift<0) hshift = HI%ied-HI%iec
1305
1306 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
13070 HI%jscB-hshift<HI%jsdB .or. HI%jecB+hshift>HI%jedB ) then
13080 write(0,*) 'chksum_v_2d: haloshift =',hshift
13090 write(0,*) 'chksum_v_2d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
13100 write(0,*) 'chksum_v_2d: jsd,jsc,jec,jed=',HI%jsdB,HI%jscB,HI%jecB,HI%jedB
13110 call chksum_error(FATAL,'Error in chksum_v_2d '//trim(mesg))
1312 endif
1313
13140 bc0 = subchk(array, HI, 0, 0, scaling)
1315
13160 sym = .false. ; if (present(symmetric)) sym = symmetric
1317
13180 if ((hshift==0) .and. .not.sym) then
13190 if (is_root_pe()) call chk_sum_msg("v-point:", bc0, mesg, iounit)
1320 else
13210 do_corners = .true.
13220 if (present(omit_corners)) do_corners = .not. omit_corners
1323
13240 if (hshift==0) then
13250 bcS = subchk(array, HI, 0, -hshift-1, scaling)
13260 if (is_root_pe()) call chk_sum_msg_S("v-point:", bc0, bcS, mesg, iounit)
13270 elseif (do_corners) then
13280 if (sym) then
13290 bcSW = subchk(array, HI, -hshift, -hshift-1, scaling)
13300 bcSE = subchk(array, HI, hshift, -hshift-1, scaling)
1331 else
13320 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
13330 bcSE = subchk(array, HI, hshift, -hshift, scaling)
1334 endif
13350 bcNW = subchk(array, HI, -hshift, hshift, scaling)
13360 bcNE = subchk(array, HI, hshift, hshift, scaling)
1337
13380 if (is_root_pe()) &
13390 call chk_sum_msg("v-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
1340 else
13410 if (sym) then
13420 bcS = subchk(array, HI, 0, -hshift-1, scaling)
1343 else
13440 bcS = subchk(array, HI, 0, -hshift, scaling)
1345 endif
13460 bcE = subchk(array, HI, hshift, 0, scaling)
13470 bcW = subchk(array, HI, -hshift, 0, scaling)
13480 bcN = subchk(array, HI, 0, hshift, scaling)
1349
13500 if (is_root_pe()) &
13510 call chk_sum_msg_NSEW("v-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
1352 endif
1353 endif
1354
13550 if (writeHash .and. is_root_pe()) then
13560 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec))
13570 hash_array(:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec)
1358
1359 write(iounit, '("v-point: hash=", z8, 1x, a)') &
13600 murmur_hash(hash_array), mesg
13610 deallocate(hash_array)
1362 endif
1363
1364 contains
1365
13660 integer function subchk(array, HI, di, dj, unscale)
1367 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1368 real, dimension(HI%isd:,HI%JsdB:), intent(in) :: array !< The array to be checksummed in
1369 !! arbitrary, possibly rescaled units [A ~> a]
1370 integer, intent(in) :: di !< i- direction array shift for this checksum
1371 integer, intent(in) :: dj !< j- direction array shift for this checksum
1372 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1373 !! for checksums and output [a A-1 ~> 1]
1374 integer :: i, j, bc
13750 subchk = 0
1376 ! This line deliberately uses the h-point computational domain.
13770 do J=HI%jsc+dj,HI%jec+dj ; do i=HI%isc+di,HI%iec+di
13780 bc = bitcount(abs(unscale*array(i,J)))
13790 subchk = subchk + bc
1380 enddo ; enddo
13810 call sum_across_PEs(subchk)
13820 subchk=mod(subchk, bc_modulus)
13830 end function subchk
1384
13850 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
1386 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1387 real, dimension(HI%isd:,HI%JsdB:), intent(in) :: array !< The array to be checksummed [a]
1388 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
1389 !! full symmetric computational domain.
1390 real, intent(out) :: aMean !< Array mean [a]
1391 real, intent(out) :: aMin !< Array minimum [a]
1392 real, intent(out) :: aMax !< Array maximum [a]
1393
1394 integer :: i, j, n, JsB
1395
13960 JsB = HI%jsc ; if (sym_stats) JsB = HI%jsc-1
1397
13980 aMin = array(HI%isc,HI%jsc) ; aMax = aMin
13990 do J=JsB,HI%JecB ; do i=HI%isc,HI%iec
14000 aMin = min(aMin, array(i,J))
14010 aMax = max(aMax, array(i,J))
1402 enddo ; enddo
1403 ! This line deliberately uses the h-computational domain.
14040 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec))
14050 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc)
14060 call sum_across_PEs(n)
14070 call min_across_PEs(aMin)
14080 call max_across_PEs(aMax)
14090 aMean = aMean / real(n)
14100 end subroutine subStats
1411
1412end subroutine chksum_v_2d
1413
1414!> Checksums a 3d array staggered at tracer points.
14150subroutine chksum_h_3d(array_m, mesg, HI_m, haloshift, omit_corners, scale, logunit, unscale)
1416 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1417 real, dimension(HI_m%isd:,HI_m%jsd:,:), target, intent(in) :: array_m !< The array to be checksummed in
1418 !! arbitrary, possibly rescaled units [A ~> a]
1419 character(len=*), intent(in) :: mesg !< An identifying message
1420 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1421 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1422 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1423 !! for checksums and output [a A-1 ~> 1]
1424 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
1425 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1426 !! for checksums and output [a A-1 ~> 1].
1427 !! Here scale and unscale are synonymous, but unscale
1428 !! takes precedence if both are present.
1429
1430 ! Local variables
1431 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
1432 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
1433 ! for checksums and output
14340 real, pointer :: array(:,:,:) ! Field array on the input grid [A ~> a]
14350 real, allocatable, dimension(:,:,:) :: rescaled_array ! The array with scaling undone [a]
14360 real, allocatable :: hash_array(:,:,:) ! Subarray used to compute hash [a]
1437 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
1438 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
1439 integer :: iounit !< Log IO unit
1440 integer :: i, j, k
1441 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
1442 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
1443 integer :: bcN, bcS, bcE, bcW
1444 logical :: do_corners
1445 integer :: turns ! Quarter turns from input to model grid
1446
1447 ! Rotate array to the input grid
14480 turns = HI_m%turns
14490 if (modulo(turns, 4) /= 0) then
14500 allocate(HI)
14510 call rotate_hor_index(HI_m, -turns, HI)
14520 allocate(array(HI%isd:HI%ied, HI%jsd:HI%jed, size(array_m, 3)))
14530 call rotate_array(array_m, -turns, array)
1454 else
14550 HI => HI_m
14560 array => array_m
1457 endif
1458
14590 if (checkForNaNs) then
14600 if (is_NaN(array(HI%isc:HI%iec,HI%jsc:HI%jec,:))) &
14610 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
1462! if (is_NaN(array)) &
1463! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
1464 endif
1465
14660 scaling = 1.0
14670 if (present(unscale)) then ; scaling = unscale
14680 elseif (present(scale)) then ; scaling = scale ; endif
1469
14700 iounit = error_unit ; if (present(logunit)) iounit = logunit
1471
14720 if (calculateStatistics) then
14730 if (present(unscale) .or. present(scale)) then
14740 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
1475 LBOUND(array,2):UBOUND(array,2), &
14760 LBOUND(array,3):UBOUND(array,3)), source=0.0 )
14770 do k=1,size(array,3) ; do j=HI%jsc,HI%jec ; do i=HI%isc,HI%iec
14780 rescaled_array(i,j,k) = scaling*array(i,j,k)
1479 enddo ; enddo ; enddo
1480
14810 call subStats(HI, rescaled_array, aMean, aMin, aMax)
14820 deallocate(rescaled_array)
1483 else
14840 call subStats(HI, array, aMean, aMin, aMax)
1485 endif
1486
14870 if (is_root_pe()) &
14880 call chk_sum_msg("h-point:", aMean, aMin, aMax, mesg, iounit)
1489 endif
1490
14910 if (.not.writeChksums) return
1492
14930 hshift = default_shift
14940 if (present(haloshift)) hshift = haloshift
14950 if (hshift<0) hshift = HI%ied-HI%iec
1496
1497 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
14980 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
14990 write(0,*) 'chksum_h_3d: haloshift =',hshift
15000 write(0,*) 'chksum_h_3d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
15010 write(0,*) 'chksum_h_3d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
15020 call chksum_error(FATAL,'Error in chksum_h_3d '//trim(mesg))
1503 endif
1504
15050 bc0 = subchk(array, HI, 0, 0, scaling)
1506
15070 if (hshift==0) then
15080 if (is_root_pe()) call chk_sum_msg("h-point:", bc0, mesg, iounit)
1509 else
15100 do_corners = .true.
15110 if (present(omit_corners)) do_corners = .not. omit_corners
1512
15130 if (do_corners) then
15140 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
15150 bcSE = subchk(array, HI, hshift, -hshift, scaling)
15160 bcNW = subchk(array, HI, -hshift, hshift, scaling)
15170 bcNE = subchk(array, HI, hshift, hshift, scaling)
1518
15190 if (is_root_pe()) &
15200 call chk_sum_msg("h-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
1521 else
15220 bcS = subchk(array, HI, 0, -hshift, scaling)
15230 bcE = subchk(array, HI, hshift, 0, scaling)
15240 bcW = subchk(array, HI, -hshift, 0, scaling)
15250 bcN = subchk(array, HI, 0, hshift, scaling)
1526
15270 if (is_root_pe()) &
15280 call chk_sum_msg_NSEW("h-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
1529 endif
1530 endif
1531
15320 if (writeHash .and. is_root_pe()) then
15330 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec, size(array, 3)))
15340 hash_array(:,:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec, :)
1535
1536 write(iounit, '("h-point: hash=", z8, 1x, a)') &
15370 murmur_hash(hash_array), mesg
15380 deallocate(hash_array)
1539 endif
1540
1541 contains
1542
15430 integer function subchk(array, HI, di, dj, unscale)
1544 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1545 real, dimension(HI%isd:,HI%jsd:,:), intent(in) :: array !< The array to be checksummed in
1546 !! arbitrary, possibly rescaled units [A ~> a]
1547 integer, intent(in) :: di !< i- direction array shift for this checksum
1548 integer, intent(in) :: dj !< j- direction array shift for this checksum
1549 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1550 !! for checksums and output [a A-1 ~> 1]
1551 integer :: i, j, k, bc
15520 subchk = 0
15530 do k=LBOUND(array,3),UBOUND(array,3) ; do j=HI%jsc+dj,HI%jec+dj ; do i=HI%isc+di,HI%iec+di
15540 bc = bitcount(abs(unscale*array(i,j,k)))
15550 subchk = subchk + bc
1556 enddo ; enddo ; enddo
15570 call sum_across_PEs(subchk)
15580 subchk=mod(subchk, bc_modulus)
15590 end function subchk
1560
15610 subroutine subStats(HI, array, aMean, aMin, aMax)
1562 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1563 real, dimension(HI%isd:,HI%jsd:,:), intent(in) :: array !< The array to be checksummed [a]
1564 real, intent(out) :: aMean !< Array mean [a]
1565 real, intent(out) :: aMin !< Array minimum [a]
1566 real, intent(out) :: aMax !< Array maximum [a]
1567
1568 integer :: i, j, k, n
1569
15700 aMin = array(HI%isc,HI%jsc,1)
15710 aMax = array(HI%isc,HI%jsc,1)
15720 n = 0
15730 do k=LBOUND(array,3),UBOUND(array,3) ; do j=HI%jsc,HI%jec ; do i=HI%isc,HI%iec
15740 aMin = min(aMin, array(i,j,k))
15750 aMax = max(aMax, array(i,j,k))
15760 n = n + 1
1577 enddo ; enddo ; enddo
15780 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec,:))
15790 call sum_across_PEs(n)
15800 call min_across_PEs(aMin)
15810 call max_across_PEs(aMax)
15820 aMean = aMean / real(n)
15830 end subroutine subStats
1584
1585end subroutine chksum_h_3d
1586
1587!> Checksums a 3d array staggered at corner points.
15880subroutine chksum_B_3d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
1589 scale, logunit, unscale)
1590 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1591 real, dimension(HI_m%IsdB:,HI_m%JsdB:,:), target, intent(in) :: array_m !< The array to be checksummed in
1592 !! arbitrary, possibly rescaled units [A ~> a]
1593 character(len=*), intent(in) :: mesg !< An identifying message
1594 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1595 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
1596 !! symmetric computational domain.
1597 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1598 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1599 !! for checksums and output [a A-1 ~> 1]
1600 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
1601 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1602 !! for checksums and output [a A-1 ~> 1].
1603 !! Here scale and unscale are synonymous, but unscale
1604 !! takes precedence if both are present.
1605
1606 ! Local variables
1607 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
1608 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
1609 ! for checksums and output
16100 real, pointer :: array(:,:,:) ! Field array on the input grid [A ~> a]
16110 real, allocatable, dimension(:,:,:) :: rescaled_array ! The array with scaling undone [a]
16120 real, allocatable :: hash_array(:,:,:) ! Subarray used to compute hash [a]
1613 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
1614 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
1615 integer :: iounit !< Log IO unit
1616 integer :: i, j, k, Is, Js
1617 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
1618 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
1619 integer :: bcN, bcS, bcE, bcW
1620 logical :: do_corners, sym, sym_stats
1621 integer :: turns ! Quarter turns from input to model grid
1622
1623 ! Rotate array to the input grid
16240 turns = HI_m%turns
16250 if (modulo(turns, 4) /= 0) then
16260 allocate(HI)
16270 call rotate_hor_index(HI_m, -turns, HI)
16280 allocate(array(HI%IsdB:HI%IedB, HI%JsdB:HI%JedB, size(array_m, 3)))
16290 call rotate_array(array_m, -turns, array)
1630 else
16310 HI => HI_m
16320 array => array_m
1633 endif
1634
16350 if (checkForNaNs) then
16360 if (is_NaN(array(HI%IscB:HI%IecB,HI%JscB:HI%JecB,:))) &
16370 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
1638! if (is_NaN(array)) &
1639! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
1640 endif
1641
16420 scaling = 1.0
16430 if (present(unscale)) then ; scaling = unscale
16440 elseif (present(scale)) then ; scaling = scale ; endif
1645
16460 iounit = error_unit ; if (present(logunit)) iounit = logunit
16470 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
16480 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
1649
16500 if (calculateStatistics) then
16510 if (present(unscale) .or. present(scale)) then
16520 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
1653 LBOUND(array,2):UBOUND(array,2), &
16540 LBOUND(array,3):UBOUND(array,3)), source=0.0 )
16550 Is = HI%isc ; if (sym_stats) Is = HI%isc-1
16560 Js = HI%jsc ; if (sym_stats) Js = HI%jsc-1
16570 do k=1,size(array,3) ; do J=Js,HI%JecB ; do I=Is,HI%IecB
16580 rescaled_array(I,J,k) = scaling*array(I,J,k)
1659 enddo ; enddo ; enddo
16600 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
16610 deallocate(rescaled_array)
1662 else
16630 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
1664 endif
1665
16660 if (is_root_pe()) &
16670 call chk_sum_msg("B-point:", aMean, aMin, aMax, mesg, iounit)
1668 endif
1669
16700 if (.not.writeChksums) return
1671
16720 hshift = default_shift
16730 if (present(haloshift)) hshift = haloshift
16740 if (hshift<0) hshift = HI%ied-HI%iec
1675
1676 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
16770 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
16780 write(0,*) 'chksum_B_3d: haloshift =',hshift
16790 write(0,*) 'chksum_B_3d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
16800 write(0,*) 'chksum_B_3d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
16810 call chksum_error(FATAL,'Error in chksum_B_3d '//trim(mesg))
1682 endif
1683
16840 bc0 = subchk(array, HI, 0, 0, scaling)
1685
16860 sym = .false. ; if (present(symmetric)) sym = symmetric
1687
16880 if ((hshift==0) .and. .not.sym) then
16890 if (is_root_pe()) call chk_sum_msg("B-point:", bc0, mesg, iounit)
1690 else
16910 do_corners = .true.
16920 if (present(omit_corners)) do_corners = .not. omit_corners
1693
16940 if (do_corners) then
16950 if (sym) then
16960 bcSW = subchk(array, HI, -hshift-1, -hshift-1, scaling)
16970 bcSE = subchk(array, HI, hshift, -hshift-1, scaling)
16980 bcNW = subchk(array, HI, -hshift-1, hshift, scaling)
1699 else
17000 bcSW = subchk(array, HI, -hshift-1, -hshift-1, scaling)
17010 bcSE = subchk(array, HI, hshift, -hshift-1, scaling)
17020 bcNW = subchk(array, HI, -hshift-1, hshift, scaling)
1703 endif
17040 bcNE = subchk(array, HI, hshift, hshift, scaling)
1705
17060 if (is_root_pe()) &
17070 call chk_sum_msg("B-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
1708 else
17090 if (sym) then
17100 bcS = subchk(array, HI, 0, -hshift-1, scaling)
17110 bcW = subchk(array, HI, -hshift-1, 0, scaling)
1712 else
17130 bcS = subchk(array, HI, 0, -hshift, scaling)
17140 bcW = subchk(array, HI, -hshift, 0, scaling)
1715 endif
17160 bcE = subchk(array, HI, hshift, 0, scaling)
17170 bcN = subchk(array, HI, 0, hshift, scaling)
1718
17190 if (is_root_pe()) &
17200 call chk_sum_msg_NSEW("B-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
1721 endif
1722 endif
1723
17240 if (writeHash .and. is_root_pe()) then
17250 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec, size(array, 3)))
17260 hash_array(:,:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec, :)
1727
1728 write(iounit, '("B-point: hash=", z8, 1x, a)') &
17290 murmur_hash(hash_array), mesg
17300 deallocate(hash_array)
1731 endif
1732
1733 contains
1734
17350 integer function subchk(array, HI, di, dj, unscale)
1736 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1737 real, dimension(HI%IsdB:,HI%JsdB:,:), intent(in) :: array !< The array to be checksummed in
1738 !! arbitrary, possibly rescaled units [A ~> a]
1739 integer, intent(in) :: di !< i- direction array shift for this checksum
1740 integer, intent(in) :: dj !< j- direction array shift for this checksum
1741 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1742 !! for checksums and output [a A-1 ~> 1]
1743 integer :: i, j, k, bc
17440 subchk = 0
1745 ! This line deliberately uses the h-point computational domain.
17460 do k=LBOUND(array,3),UBOUND(array,3) ; do J=HI%jsc+dj,HI%jec+dj ; do I=HI%isc+di,HI%iec+di
17470 bc = bitcount(abs(unscale*array(I,J,k)))
17480 subchk = subchk + bc
1749 enddo ; enddo ; enddo
17500 call sum_across_PEs(subchk)
17510 subchk=mod(subchk, bc_modulus)
17520 end function subchk
1753
17540 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
1755 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1756 real, dimension(HI%IsdB:,HI%JsdB:,:), intent(in) :: array !< The array to be checksummed [a]
1757 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
1758 !! full symmetric computational domain.
1759 real, intent(out) :: aMean !< Array mean [a]
1760 real, intent(out) :: aMin !< Array minimum [a]
1761 real, intent(out) :: aMax !< Array maximum [a]
1762
1763 integer :: i, j, k, n, IsB, JsB
1764
17650 IsB = HI%isc ; if (sym_stats) IsB = HI%isc-1
17660 JsB = HI%jsc ; if (sym_stats) JsB = HI%jsc-1
1767
17680 aMin = array(HI%isc,HI%jsc,1) ; aMax = aMin
17690 do k=LBOUND(array,3),UBOUND(array,3) ; do J=JsB,HI%JecB ; do I=IsB,HI%IecB
17700 aMin = min(aMin, array(I,J,k))
17710 aMax = max(aMax, array(I,J,k))
1772 enddo ; enddo ; enddo
17730 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec,:))
17740 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc) * size(array,3)
17750 call sum_across_PEs(n)
17760 call min_across_PEs(aMin)
17770 call max_across_PEs(aMax)
17780 aMean = aMean / real(n)
17790 end subroutine subStats
1780
1781end subroutine chksum_B_3d
1782
1783!> Checksums a 3d array staggered at C-grid u points.
17840subroutine chksum_u_3d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
1785 scale, logunit, unscale)
1786 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1787 real, dimension(HI_m%isdB:,HI_m%Jsd:,:), target, intent(in) :: array_m !< The array to be checksummed in
1788 !! arbitrary, possibly rescaled units [A ~> a]
1789 character(len=*), intent(in) :: mesg !< An identifying message
1790 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1791 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
1792 !! symmetric computational domain.
1793 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1794 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1795 !! for checksums and output [a A-1 ~> 1]
1796 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
1797 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1798 !! for checksums and output [a A-1 ~> 1].
1799 !! Here scale and unscale are synonymous, but unscale
1800 !! takes precedence if both are present.
1801
1802 ! Local variables
1803 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
1804 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
1805 ! for checksums and output
18060 real, pointer :: array(:,:,:) ! Field array on the input grid [A ~> a]
18070 real, allocatable, dimension(:,:,:) :: rescaled_array ! The array with scaling undone [a]
18080 real, allocatable :: hash_array(:,:,:) ! Subarray used to compute hash [a]
1809 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
1810 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
1811 integer :: iounit !< Log IO unit
1812 integer :: i, j, k, Is
1813 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
1814 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
1815 integer :: bcN, bcS, bcE, bcW
1816 logical :: do_corners, sym, sym_stats
1817 integer :: turns ! Quarter turns from input to model grid
1818
1819 ! Rotate array to the input grid
18200 turns = HI_m%turns
18210 if (modulo(turns, 4) /= 0) then
18220 allocate(HI)
18230 call rotate_hor_index(HI_m, -turns, HI)
18240 if (modulo(turns, 2) /= 0) then
1825 ! Arrays originating from v-points must be handled by vchksum
18260 allocate(array(HI%isd:HI%ied, HI%JsdB:HI%JedB, size(array_m, 3)))
18270 call rotate_array(array_m, -turns, array)
1828 call vchksum(array, mesg, HI, haloshift, symmetric, omit_corners, &
18290 scale=scale, logunit=logunit, unscale=unscale)
18300 return
1831 else
18320 allocate(array(HI%IsdB:HI%IedB, HI%jsd:HI%jed, size(array_m, 3)))
18330 call rotate_array(array_m, -turns, array)
1834 endif
1835 else
18360 HI => HI_m
18370 array => array_m
1838 endif
1839
18400 if (checkForNaNs) then
18410 if (is_NaN(array(HI%IscB:HI%IecB,HI%jsc:HI%jec,:))) &
18420 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
1843! if (is_NaN(array)) &
1844! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
1845 endif
1846
18470 scaling = 1.0
18480 if (present(unscale)) then ; scaling = unscale
18490 elseif (present(scale)) then ; scaling = scale ; endif
1850
18510 iounit = error_unit ; if (present(logunit)) iounit = logunit
18520 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
18530 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
1854
18550 if (calculateStatistics) then
18560 if (present(unscale) .or. present(scale)) then
18570 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
1858 LBOUND(array,2):UBOUND(array,2), &
18590 LBOUND(array,3):UBOUND(array,3)), source=0.0 )
18600 Is = HI%isc ; if (sym_stats) Is = HI%isc-1
18610 do k=1,size(array,3) ; do j=HI%jsc,HI%jec ; do I=Is,HI%IecB
18620 rescaled_array(I,j,k) = scaling*array(I,j,k)
1863 enddo ; enddo ; enddo
18640 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
18650 deallocate(rescaled_array)
1866 else
18670 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
1868 endif
18690 if (is_root_pe()) &
18700 call chk_sum_msg("u-point:", aMean, aMin, aMax, mesg, iounit)
1871 endif
1872
18730 if (.not.writeChksums) return
1874
18750 hshift = default_shift
18760 if (present(haloshift)) hshift = haloshift
18770 if (hshift<0) hshift = HI%ied-HI%iec
1878
1879 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
18800 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
18810 write(0,*) 'chksum_u_3d: haloshift =',hshift
18820 write(0,*) 'chksum_u_3d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
18830 write(0,*) 'chksum_u_3d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
18840 call chksum_error(FATAL,'Error in chksum_u_3d '//trim(mesg))
1885 endif
1886
18870 bc0 = subchk(array, HI, 0, 0, scaling)
1888
18890 sym = .false. ; if (present(symmetric)) sym = symmetric
1890
18910 if ((hshift==0) .and. .not.sym) then
18920 if (is_root_pe()) call chk_sum_msg("u-point:", bc0, mesg, iounit)
1893 else
18940 do_corners = .true.
18950 if (present(omit_corners)) do_corners = .not. omit_corners
1896
18970 if (hshift==0) then
18980 bcW = subchk(array, HI, -hshift-1, 0, scaling)
18990 if (is_root_pe()) call chk_sum_msg_W("u-point:", bc0, bcW, mesg, iounit)
19000 elseif (do_corners) then
19010 if (sym) then
19020 bcSW = subchk(array, HI, -hshift-1, -hshift, scaling)
19030 bcNW = subchk(array, HI, -hshift-1, hshift, scaling)
1904 else
19050 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
19060 bcNW = subchk(array, HI, -hshift, hshift, scaling)
1907 endif
19080 bcSE = subchk(array, HI, hshift, -hshift, scaling)
19090 bcNE = subchk(array, HI, hshift, hshift, scaling)
1910
19110 if (is_root_pe()) &
19120 call chk_sum_msg("u-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
1913 else
19140 bcS = subchk(array, HI, 0, -hshift, scaling)
19150 bcE = subchk(array, HI, hshift, 0, scaling)
19160 if (sym) then
19170 bcW = subchk(array, HI, -hshift-1, 0, scaling)
1918 else
19190 bcW = subchk(array, HI, -hshift, 0, scaling)
1920 endif
19210 bcN = subchk(array, HI, 0, hshift, scaling)
1922
19230 if (is_root_pe()) &
19240 call chk_sum_msg_NSEW("u-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
1925 endif
1926 endif
1927
19280 if (writeHash .and. is_root_pe()) then
19290 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec, size(array, 3)))
19300 hash_array(:,:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec, :)
1931
1932 write(iounit, '("u-point: hash=", z8, 1x, a)') &
19330 murmur_hash(hash_array), mesg
19340 deallocate(hash_array)
1935 endif
1936
1937 contains
1938
19390 integer function subchk(array, HI, di, dj, unscale)
1940 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1941 real, dimension(HI%IsdB:,HI%jsd:,:), intent(in) :: array !< The array to be checksummed in
1942 !! arbitrary, possibly rescaled units [A ~> a]
1943 integer, intent(in) :: di !< i- direction array shift for this checksum
1944 integer, intent(in) :: dj !< j- direction array shift for this checksum
1945 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
1946 !! for checksums and output [a A-1 ~> 1]
1947 integer :: i, j, k, bc
19480 subchk = 0
1949 ! This line deliberately uses the h-point computational domain.
19500 do k=LBOUND(array,3),UBOUND(array,3) ; do j=HI%jsc+dj,HI%jec+dj ; do I=HI%isc+di,HI%iec+di
19510 bc = bitcount(abs(unscale*array(I,j,k)))
19520 subchk = subchk + bc
1953 enddo ; enddo ; enddo
19540 call sum_across_PEs(subchk)
19550 subchk=mod(subchk, bc_modulus)
19560 end function subchk
1957
19580 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
1959 type(hor_index_type), intent(in) :: HI !< A horizontal index type
1960 real, dimension(HI%IsdB:,HI%jsd:,:), intent(in) :: array !< The array to be checksummed [a]
1961 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
1962 !! full symmetric computational domain.
1963 real, intent(out) :: aMean !< Array mean [a]
1964 real, intent(out) :: aMin !< Array minimum [a]
1965 real, intent(out) :: aMax !< Array maximum [a]
1966
1967 integer :: i, j, k, n, IsB
1968
19690 IsB = HI%isc ; if (sym_stats) IsB = HI%isc-1
1970
19710 aMin = array(HI%isc,HI%jsc,1) ; aMax = aMin
19720 do k=LBOUND(array,3),UBOUND(array,3) ; do j=HI%jsc,HI%jec ; do I=IsB,HI%IecB
19730 aMin = min(aMin, array(I,j,k))
19740 aMax = max(aMax, array(I,j,k))
1975 enddo ; enddo ; enddo
1976 ! This line deliberately uses the h-point computational domain.
19770 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec,:))
19780 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc) * size(array,3)
19790 call sum_across_PEs(n)
19800 call min_across_PEs(aMin)
19810 call max_across_PEs(aMax)
19820 aMean = aMean / real(n)
19830 end subroutine subStats
1984
1985end subroutine chksum_u_3d
1986
1987!> Checksums a 3d array staggered at C-grid v points.
19880subroutine chksum_v_3d(array_m, mesg, HI_m, haloshift, symmetric, omit_corners, &
1989 scale, logunit, unscale)
1990 type(hor_index_type), target, intent(in) :: HI_m !< A horizontal index type
1991 real, dimension(HI_m%isd:,HI_m%JsdB:,:), target, intent(in) :: array_m !< The array to be checksummed in
1992 !! arbitrary, possibly rescaled units [A ~> a]
1993 character(len=*), intent(in) :: mesg !< An identifying message
1994 integer, optional, intent(in) :: haloshift !< The width of halos to check (default 0)
1995 logical, optional, intent(in) :: symmetric !< If true, do the checksums on the full
1996 !! symmetric computational domain.
1997 logical, optional, intent(in) :: omit_corners !< If true, avoid checking diagonal shifts
1998 real, optional, intent(in) :: scale !< A factor to convert this array back to unscaled units
1999 !! for checksums and output [a A-1 ~> 1]
2000 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
2001 real, optional, intent(in) :: unscale !< A factor to convert this array back to unscaled units
2002 !! for checksums and output [a A-1 ~> 1].
2003 !! Here scale and unscale are synonymous, but unscale
2004 !! takes precedence if both are present.
2005
2006 ! Local variables
2007 ! In the following comments, [A] is used to indicate the arbitrary, possibly rescaled units
2008 ! of the input array while [a] indicates the unscaled (e.g., mks) units that should be used
2009 ! for checksums and output
20100 real, pointer :: array(:,:,:) ! Field array on the input grid [A ~> a]
20110 real, allocatable, dimension(:,:,:) :: rescaled_array ! The array with scaling undone [a]
20120 real, allocatable :: hash_array(:,:,:) ! Subarray used to compute hash [a]
2013 type(hor_index_type), pointer :: HI ! Horizontal index bounds of the input grid
2014 real :: scaling ! Explicit rescaling factor [a A-1 ~> 1]
2015 integer :: iounit !< Log IO unit
2016 integer :: i, j, k, Js
2017 integer :: bc0, bcSW, bcSE, bcNW, bcNE, hshift
2018 integer :: bcN, bcS, bcE, bcW
2019 real :: aMean, aMin, aMax ! Array mean, global minimum and global maximum [a]
2020 logical :: do_corners, sym, sym_stats
2021 integer :: turns ! Quarter turns from input to model grid
2022
2023 ! Rotate array to the input grid
20240 turns = HI_m%turns
20250 if (modulo(turns, 4) /= 0) then
20260 allocate(HI)
20270 call rotate_hor_index(HI_m, -turns, HI)
20280 if (modulo(turns, 2) /= 0) then
2029 ! Arrays originating from u-points must be handled by uchksum
20300 allocate(array(HI%IsdB:HI%IedB, HI%jsd:HI%jed, size(array_m, 3)))
20310 call rotate_array(array_m, -turns, array)
2032 call uchksum(array, mesg, HI, haloshift, symmetric, omit_corners, &
20330 scale=scale, logunit=logunit, unscale=unscale)
20340 return
2035 else
20360 allocate(array(HI%isd:HI%ied, HI%JsdB:HI%JedB, size(array_m, 3)))
20370 call rotate_array(array_m, -turns, array)
2038 endif
2039 else
20400 HI => HI_m
20410 array => array_m
2042 endif
2043
20440 if (checkForNaNs) then
20450 if (is_NaN(array(HI%isc:HI%iec,HI%JscB:HI%JecB,:))) &
20460 call chksum_error(FATAL, 'NaN detected: '//trim(mesg))
2047! if (is_NaN(array)) &
2048! call chksum_error(FATAL, 'NaN detected in halo: '//trim(mesg))
2049 endif
2050
20510 scaling = 1.0
20520 if (present(unscale)) then ; scaling = unscale
20530 elseif (present(scale)) then ; scaling = scale ; endif
2054
20550 iounit = error_unit ; if (present(logunit)) iounit = logunit
20560 sym_stats = .false. ; if (present(symmetric)) sym_stats = symmetric
20570 if (present(haloshift)) then ; if (haloshift > 0) sym_stats = .true. ; endif
2058
20590 if (calculateStatistics) then
20600 if (present(unscale) .or. present(scale)) then
20610 allocate( rescaled_array(LBOUND(array,1):UBOUND(array,1), &
2062 LBOUND(array,2):UBOUND(array,2), &
20630 LBOUND(array,3):UBOUND(array,3)), source=0.0 )
20640 Js = HI%jsc ; if (sym_stats) Js = HI%jsc-1
20650 do k=1,size(array,3) ; do J=Js,HI%JecB ; do i=HI%isc,HI%iec
20660 rescaled_array(i,J,k) = scaling*array(i,J,k)
2067 enddo ; enddo ; enddo
20680 call subStats(HI, rescaled_array, sym_stats, aMean, aMin, aMax)
20690 deallocate(rescaled_array)
2070 else
20710 call subStats(HI, array, sym_stats, aMean, aMin, aMax)
2072 endif
20730 if (is_root_pe()) &
20740 call chk_sum_msg("v-point:", aMean, aMin, aMax, mesg, iounit)
2075 endif
2076
20770 if (.not.writeChksums) return
2078
20790 hshift = default_shift
20800 if (present(haloshift)) hshift = haloshift
20810 if (hshift<0) hshift = HI%ied-HI%iec
2082
2083 if ( HI%isc-hshift<HI%isd .or. HI%iec+hshift>HI%ied .or. &
20840 HI%jsc-hshift<HI%jsd .or. HI%jec+hshift>HI%jed ) then
20850 write(0,*) 'chksum_v_3d: haloshift =',hshift
20860 write(0,*) 'chksum_v_3d: isd,isc,iec,ied=',HI%isd,HI%isc,HI%iec,HI%ied
20870 write(0,*) 'chksum_v_3d: jsd,jsc,jec,jed=',HI%jsd,HI%jsc,HI%jec,HI%jed
20880 call chksum_error(FATAL,'Error in chksum_v_3d '//trim(mesg))
2089 endif
2090
20910 bc0 = subchk(array, HI, 0, 0, scaling)
2092
20930 sym = .false. ; if (present(symmetric)) sym = symmetric
2094
20950 if ((hshift==0) .and. .not.sym) then
20960 if (is_root_pe()) call chk_sum_msg("v-point:", bc0, mesg, iounit)
2097 else
20980 do_corners = .true.
20990 if (present(omit_corners)) do_corners = .not. omit_corners
2100
21010 if (hshift==0) then
21020 bcS = subchk(array, HI, 0, -hshift-1, scaling)
21030 if (is_root_pe()) call chk_sum_msg_S("v-point:", bc0, bcS, mesg, iounit)
21040 elseif (do_corners) then
21050 if (sym) then
21060 bcSW = subchk(array, HI, -hshift, -hshift-1, scaling)
21070 bcSE = subchk(array, HI, hshift, -hshift-1, scaling)
2108 else
21090 bcSW = subchk(array, HI, -hshift, -hshift, scaling)
21100 bcSE = subchk(array, HI, hshift, -hshift, scaling)
2111 endif
21120 bcNW = subchk(array, HI, -hshift, hshift, scaling)
21130 bcNE = subchk(array, HI, hshift, hshift, scaling)
2114
21150 if (is_root_pe()) &
21160 call chk_sum_msg("v-point:", bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
2117 else
21180 if (sym) then
21190 bcS = subchk(array, HI, 0, -hshift-1, scaling)
2120 else
21210 bcS = subchk(array, HI, 0, -hshift, scaling)
2122 endif
21230 bcE = subchk(array, HI, hshift, 0, scaling)
21240 bcW = subchk(array, HI, -hshift, 0, scaling)
21250 bcN = subchk(array, HI, 0, hshift, scaling)
2126
21270 if (is_root_pe()) &
21280 call chk_sum_msg_NSEW("v-point:", bc0, bcN, bcS, bcE, bcW, mesg, iounit)
2129 endif
2130 endif
2131
21320 if (writeHash .and. is_root_pe()) then
21330 allocate(hash_array(HI%isc:HI%iec, HI%jsc:HI%jec, size(array, 3)))
21340 hash_array(:,:,:) = scaling * array(HI%isc:HI%iec, HI%jsc:HI%jec, :)
2135
2136 write(iounit, '("v-point: hash=", z8, 1x, a)') &
21370 murmur_hash(hash_array), mesg
21380 deallocate(hash_array)
2139 endif
2140
2141 contains
2142
21430 integer function subchk(array, HI, di, dj, unscale)
2144 type(hor_index_type), intent(in) :: HI !< A horizontal index type
2145 real, dimension(HI%isd:,HI%JsdB:,:), intent(in) :: array !< The array to be checksummed in
2146 !! arbitrary, possibly rescaled units [A ~> a]
2147 integer, intent(in) :: di !< i- direction array shift for this checksum
2148 integer, intent(in) :: dj !< j- direction array shift for this checksum
2149 real, intent(in) :: unscale !< A factor to convert this array back to unscaled units
2150 !! for checksums and output [a A-1 ~> 1]
2151 integer :: i, j, k, bc
21520 subchk = 0
2153 ! This line deliberately uses the h-point computational domain.
21540 do k=LBOUND(array,3),UBOUND(array,3) ; do J=HI%jsc+dj,HI%jec+dj ; do i=HI%isc+di,HI%iec+di
21550 bc = bitcount(abs(unscale*array(i,J,k)))
21560 subchk = subchk + bc
2157 enddo ; enddo ; enddo
21580 call sum_across_PEs(subchk)
21590 subchk=mod(subchk, bc_modulus)
21600 end function subchk
2161
2162 !subroutine subStats(HI, array, mesg, sym_stats)
21630 subroutine subStats(HI, array, sym_stats, aMean, aMin, aMax)
2164 type(hor_index_type), intent(in) :: HI !< A horizontal index type
2165 real, dimension(HI%isd:,HI%JsdB:,:), intent(in) :: array !< The array to be checksummed [a]
2166 logical, intent(in) :: sym_stats !< If true, evaluate the statistics on the
2167 !! full symmetric computational domain.
2168 real, intent(out) :: aMean !< Mean of array over domain [a]
2169 real, intent(out) :: aMin !< Minimum of array over domain [a]
2170 real, intent(out) :: aMax !< Maximum of array over domain [a]
2171
2172 integer :: i, j, k, n, JsB
2173
21740 JsB = HI%jsc ; if (sym_stats) JsB = HI%jsc-1
2175
21760 aMin = array(HI%isc,HI%jsc,1) ; aMax = aMin
21770 do k=LBOUND(array,3),UBOUND(array,3) ; do J=JsB,HI%JecB ; do i=HI%isc,HI%iec
21780 aMin = min(aMin, array(i,J,k))
21790 aMax = max(aMax, array(i,J,k))
2180 enddo ; enddo ; enddo
2181 ! This line deliberately uses the h-point computational domain.
21820 aMean = reproducing_sum(array(HI%isc:HI%iec,HI%jsc:HI%jec,:))
21830 n = (1 + HI%jec - HI%jsc) * (1 + HI%iec - HI%isc) * size(array,3)
21840 call sum_across_PEs(n)
21850 call min_across_PEs(aMin)
21860 call max_across_PEs(aMax)
21870 aMean = aMean / real(n)
21880 end subroutine subStats
2189
2190end subroutine chksum_v_3d
2191
2192! These are the older version of chksum that do not take the grid staggering
2193! into account.
2194
2195!> chksum1d does a checksum of a 1-dimensional array.
21960subroutine chksum1d(array, mesg, start_i, end_i, compare_PEs, logunit)
2197 real, dimension(:), intent(in) :: array !< The array to be summed (index starts at 1) in arbitrary units [A].
2198 character(len=*), intent(in) :: mesg !< An identifying message.
2199 integer, optional, intent(in) :: start_i !< The starting index for the sum (default 1)
2200 integer, optional, intent(in) :: end_i !< The ending index for the sum (default all)
2201 logical, optional, intent(in) :: compare_PEs !< If true, compare across PEs instead of summing
2202 !! and list the root_PE value (default true)
2203 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
2204
2205 integer :: is, ie, i, bc, sum1, sum_bc, ioUnit
2206 real :: sum ! The global sum of the array [A]
22070 real, allocatable :: sum_here(:) ! The sum on each PE [A]
2208 logical :: compare
2209 integer :: pe_num ! pe number of the data
2210 integer :: nPEs ! Total number of processsors
2211
22120 is = LBOUND(array,1) ; ie = UBOUND(array,1)
22130 if (present(start_i)) is = start_i
22140 if (present(end_i)) ie = end_i
22150 compare = .true. ; if (present(compare_PEs)) compare = compare_PEs
22160 iounit = error_unit ; if (present(logunit)) iounit = logunit
2217
22180 sum = 0.0 ; sum_bc = 0
22190 do i=is,ie
22200 sum = sum + array(i)
22210 bc = bitcount(ABS(array(i)))
22220 sum_bc = sum_bc + bc
2223 enddo
2224
22250 pe_num = pe_here() + 1 - root_pe() ; nPEs = num_pes()
22260 allocate(sum_here(nPEs), source=0.0) ; sum_here(pe_num) = sum
22270 call sum_across_PEs(sum_here,nPEs)
2228
22290 sum1 = sum_bc
22300 call sum_across_PEs(sum1)
2231
22320 if (.not.compare) then
22330 sum = 0.0
22340 do i=1,nPEs ; sum = sum + sum_here(i) ; enddo
22350 sum_bc = sum1
22360 elseif (is_root_pe()) then
22370 if (sum1 /= nPEs*sum_bc) &
2238 write(iounit, '(A40," bitcounts do not match across PEs: ",I12,1X,I12)') &
22390 mesg, sum1, nPEs*sum_bc
22400 do i=1,nPEs ; if (sum /= sum_here(i)) then
2241 write(iounit, '(A40," PE ",I0," sum mismatches root_PE: ",3(ES22.13,1X))') &
22420 mesg, i, sum_here(i), sum, sum_here(i)-sum
2243 endif ; enddo
2244 endif
22450 deallocate(sum_here)
2246
22470 if (is_root_pe()) &
22480 write(iounit,'(A50,1X,ES25.16,1X,I12)') mesg, sum, sum_bc
2249
22500end subroutine chksum1d
2251
2252! These are the older version of chksum that do not take the grid staggering
2253! into account.
2254
2255!> chksum2d does a checksum of all data in a 2-d array.
22560subroutine chksum2d(array, mesg, logunit)
2257
2258 real, dimension(:,:), intent(in) :: array !< The array to be checksummed in arbitrary units [A]
2259 character(len=*), intent(in) :: mesg !< An identifying message
2260 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
2261
2262 integer :: xs, xe, ys, ye, i, j, sum1, bc, iounit
2263 real :: sum ! The global sum of the array [A]
2264
22650 iounit = error_unit ; if (present(logunit)) iounit = logunit
2266
22670 xs = LBOUND(array,1) ; xe = UBOUND(array,1)
22680 ys = LBOUND(array,2) ; ye = UBOUND(array,2)
2269
22700 sum = 0.0 ; sum1 = 0
22710 do i=xs,xe ; do j=ys,ye
22720 bc = bitcount(abs(array(i,j)))
22730 sum1 = sum1 + bc
2274 enddo ; enddo
22750 call sum_across_PEs(sum1)
2276
22770 sum = reproducing_sum(array(:,:))
2278
22790 if (is_root_pe()) &
22800 write(iounit,'(A50,1X,ES25.16,1X,I12)') mesg, sum, sum1
2281! write(iounit,'(A40,1X,Z16.16,1X,Z16.16,1X,ES25.16,1X,I12)') &
2282! mesg, sum, sum1, sum, sum1
2283
22840end subroutine chksum2d
2285
2286!> chksum3d does a checksum of all data in a 2-d array.
22870subroutine chksum3d(array, mesg, logunit)
2288
2289 real, dimension(:,:,:), intent(in) :: array !< The array to be checksummed in arbitrary units [A]
2290 character(len=*), intent(in) :: mesg !< An identifying message
2291 integer, optional, intent(in) :: logunit !< IO unit for checksum logging
2292
2293 integer :: xs, xe, ys, ye, zs, ze, i, j, k, bc, sum1, iounit
2294 real :: sum ! The global sum of the array [A]
2295
22960 iounit = error_unit ; if (present(logunit)) iounit = logunit
2297
22980 xs = LBOUND(array,1) ; xe = UBOUND(array,1)
22990 ys = LBOUND(array,2) ; ye = UBOUND(array,2)
23000 zs = LBOUND(array,3) ; ze = UBOUND(array,3)
2301
23020 sum = 0.0 ; sum1 = 0
23030 do i=xs,xe ; do j=ys,ye ; do k=zs,ze
23040 bc = bitcount(ABS(array(i,j,k)))
23050 sum1 = sum1 + bc
2306 enddo ; enddo ; enddo
2307
23080 call sum_across_PEs(sum1)
23090 sum = reproducing_sum(array(:,:,:))
2310
23110 if (is_root_pe()) &
23120 write(iounit, '(A50,1X,ES25.16,1X,I12)') mesg, sum, sum1
2313! write(iounit, '(A40,1X,Z16.16,1X,Z16.16,1X,ES25.16,1X,I12)') &
2314! mesg, sum, sum1, sum, sum1
2315
23160end subroutine chksum3d
2317
2318!> This function returns .true. if x is a NaN, and .false. otherwise.
23193function is_NaN_0d(x)
2320 real, intent(in) :: x !< The value to be checked for NaNs in arbitrary units [A]
2321 logical :: is_NaN_0d
2322
2323 !is_NaN_0d = (((x < 0.0) .and. (x >= 0.0)) .or. &
2324 ! (.not.(x < 0.0) .and. .not.(x >= 0.0)))
23253 if (((x < 0.0) .and. (x >= 0.0)) .or. &
2326 (.not.(x < 0.0) .and. .not.(x >= 0.0))) then
23270 is_NaN_0d = .true.
2328 else
23293 is_NaN_0d = .false.
2330 endif
2331
23323end function is_NaN_0d
2333
2334!> Returns .true. if any element of x is a NaN, and .false. otherwise.
23350function is_NaN_1d(x, skip_mpp)
2336 real, dimension(:), intent(in) :: x !< The array to be checked for NaNs in arbitrary units [A]
2337 logical, optional, intent(in) :: skip_mpp !< If true, only check this array only
2338 !! on the local PE (default false).
2339 logical :: is_NaN_1d
2340
2341 integer :: i, n
2342 logical :: global_check
2343
23440 n = 0
23450 do i = LBOUND(x,1), UBOUND(x,1)
23460 if (is_NaN_0d(x(i))) n = n + 1
2347 enddo
23480 global_check = .true.
23490 if (present(skip_mpp)) global_check = .not.skip_mpp
2350
23510 if (global_check) call sum_across_PEs(n)
23520 is_NaN_1d = .false.
23530 if (n>0) is_NaN_1d = .true.
2354
23550end function is_NaN_1d
2356
2357!> Returns .true. if any element of x is a NaN, and .false. otherwise.
23580function is_NaN_2d(x)
2359 real, dimension(:,:), intent(in) :: x !< The array to be checked for NaNs in arbitrary units [A]
2360 logical :: is_NaN_2d
2361
2362 integer :: i, j, n
2363
23640 n = 0
23650 do j = LBOUND(x,2), UBOUND(x,2) ; do i = LBOUND(x,1), UBOUND(x,1)
23660 if (is_NaN_0d(x(i,j))) n = n + 1
2367 enddo ; enddo
23680 call sum_across_PEs(n)
23690 is_NaN_2d = .false.
23700 if (n>0) is_NaN_2d = .true.
2371
23720end function is_NaN_2d
2373
2374!> Returns .true. if any element of x is a NaN, and .false. otherwise.
23750function is_NaN_3d(x)
2376 real, dimension(:,:,:), intent(in) :: x !< The array to be checked for NaNs in arbitrary units [A]
2377 logical :: is_NaN_3d
2378
2379 integer :: i, j, k, n
2380
23810 n = 0
23820 do k = LBOUND(x,3), UBOUND(x,3)
23830 do j = LBOUND(x,2), UBOUND(x,2) ; do i = LBOUND(x,1), UBOUND(x,1)
23840 if (is_NaN_0d(x(i,j,k))) n = n + 1
2385 enddo ; enddo
2386 enddo
23870 call sum_across_PEs(n)
23880 is_NaN_3d = .false.
23890 if (n>0) is_NaN_3d = .true.
2390
23910end function is_NaN_3d
2392
2393! The following set of routines do a checksum across all elements of a field,
2394! with the potential for the unscaling and rotation of this field and masking.
2395
2396!> Compute the field checksum of a scalar that may need to be unscaled.
2397!! This uses the field_chksum function that is used to verify file contents, which may differ
2398!! from the bitcount function used for other checksums in this module.
23994function field_checksum_real_0d(field, pelist, mask_val, turns, unscale) &
2400 result(chksum)
2401 real, intent(in) :: field !< Input scalar to be checksummed in arbitrary,
2402 !! possibly rescaled units [A ~> a]
2403 integer, optional, intent(in) :: pelist(:) !< PE list of ranks to checksum
2404 real, optional, intent(in) :: mask_val !< FMS mask value [nondim]
2405 integer, optional, intent(in) :: turns !< Number of quarter turns
2406 real, optional, intent(in) :: unscale !< A factor to convert this array back to
2407 !! unscaled units for checksums [a A-1 ~> 1]
2408 integer(kind=int64) :: chksum !< checksum of scalar
2409
2410 real :: scale_fac ! A local copy of unscale if it is present [a A-1 ~> 1] or 1 otherwise
2411
24124 if (present(turns)) call MOM_error(FATAL, "Rotation not supported for 0d fields.")
2413
24144 scale_fac = 1.0 ; if (present(unscale)) scale_fac = unscale
2415
24164 chksum = field_chksum(scale_fac*field, pelist=pelist, mask_val=mask_val)
24174end function field_checksum_real_0d
2418
2419
2420!> Compute the field checksum of an entire 1d array that may need to be unscaled.
2421!! This uses the field_chksum function that is used to verify file contents, which may differ
2422!! from the bitcount function used for other checksums in this module.
24230function field_checksum_real_1d(field, pelist, mask_val, turns, unscale) &
2424 result(chksum)
2425 real, dimension(:), intent(in) :: field !< Input array to be checksummed in arbitrary,
2426 !! possibly rescaled units [A ~> a]
2427 integer, optional, intent(in) :: pelist(:) !< PE list of ranks to checksum
2428 real, optional, intent(in) :: mask_val !< FMS mask value [nondim]
2429 integer, optional, intent(in) :: turns !< Number of quarter turns
2430 real, optional, intent(in) :: unscale !< A factor to convert this array back to
2431 !! unscaled units for checksums [a A-1 ~> 1]
2432 integer(kind=int64) :: chksum !< checksum of array
2433
2434 real :: scale_fac ! A local copy of unscale if it is present [a A-1 ~> 1] or 1 otherwise
2435
24360 if (present(turns)) call MOM_error(FATAL, "Rotation not supported for 1d fields.")
2437
24380 scale_fac = 1.0 ; if (present(unscale)) scale_fac = unscale
2439
24400 chksum = field_chksum(scale_fac*field(:), pelist=pelist, mask_val=mask_val)
24410end function field_checksum_real_1d
2442
2443
2444!> Compute the field checksum of an entire 2d array that may need to be rotated or unscaled.
2445!! This uses the field_chksum function that is used to verify file contents, which may differ
2446!! from the bitcount function used for other checksums in this module.
244728function field_checksum_real_2d(field, pelist, mask_val, turns, unscale) &
2448 result(chksum)
2449 real, dimension(:,:), intent(in) :: field !< Unrotated input field to be checksummed in
2450 !! arbitrary, possibly rescaled units [A ~> a]
2451 integer, optional, intent(in) :: pelist(:) !< PE list of ranks to checksum
2452 real, optional, intent(in) :: mask_val !< FMS mask value [nondim]
2453 integer, optional, intent(in) :: turns !< Number of quarter turns
2454 real, optional, intent(in) :: unscale !< A factor to convert this array back to
2455 !! unscaled units for checksums [a A-1 ~> 1]
2456 integer(kind=int64) :: chksum !< checksum of array
2457
2458 ! Local variables
245928 real, allocatable :: field_rot(:,:) ! A rotated version of field, with the same units [A ~> a]
2460 integer :: qturns ! The number of quarter turns through which to rotate field
2461 logical :: do_unscale ! If true, unscale the variable before it is checksummed
2462
246328 qturns = 0
246428 if (present(turns)) &
246528 qturns = modulo(turns, 4)
2466
246728 do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0)
2468
246928 if (qturns == 0) then
247028 if (do_unscale) then
24710 chksum = field_chksum(unscale*field(:,:), pelist=pelist, mask_val=mask_val)
2472 else
247328 chksum = field_chksum(field, pelist=pelist, mask_val=mask_val)
2474 endif
2475 else
24760 call allocate_rotated_array(field, [1,1], qturns, field_rot)
24770 call rotate_array(field, qturns, field_rot)
24780 if (do_unscale) field_rot(:,:) = unscale*field_rot(:,:)
24790 chksum = field_chksum(field_rot, pelist=pelist, mask_val=mask_val)
24800 deallocate(field_rot)
2481 endif
248228end function field_checksum_real_2d
2483
2484!> Compute the field checksum of an entire 3d array that may need to be rotated or unscaled.
2485!! This uses the field_chksum function that is used to verify file contents, which may differ
2486!! from the bitcount function used for other checksums in this module.
248731function field_checksum_real_3d(field, pelist, mask_val, turns, unscale) &
2488 result(chksum)
2489 real, dimension(:,:,:), intent(in) :: field !< Unrotated input field to be checksummed in
2490 !! arbitrary, possibly rescaled units [A ~> a]
2491 integer, optional, intent(in) :: pelist(:) !< PE list of ranks to checksum
2492 real, optional, intent(in) :: mask_val !< FMS mask value [nondim]
2493 integer, optional, intent(in) :: turns !< Number of quarter turns
2494 real, optional, intent(in) :: unscale !< A factor to convert this array back to
2495 !! unscaled units for checksums [a A-1 ~> 1]
2496 integer(kind=int64) :: chksum !< checksum of array
2497
2498 ! Local variables
249931 real, allocatable :: field_rot(:,:,:) ! A rotated version of field, with the same units [A ~> a]
2500 integer :: qturns ! The number of quarter turns through which to rotate field
2501 logical :: do_unscale ! If true, unscale the variable before it is checksummed
2502
250331 qturns = 0
250431 if (present(turns)) &
250531 qturns = modulo(turns, 4)
2506
250731 do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0)
2508
250931 if (qturns == 0) then
251031 if (do_unscale) then
25110 chksum = field_chksum(unscale*field(:,:,:), pelist=pelist, mask_val=mask_val)
2512 else
251331 chksum = field_chksum(field, pelist=pelist, mask_val=mask_val)
2514 endif
2515 else
25160 call allocate_rotated_array(field, [1,1,1], qturns, field_rot)
25170 call rotate_array(field, qturns, field_rot)
25180 if (do_unscale) field_rot(:,:,:) = unscale*field_rot(:,:,:)
25190 chksum = field_chksum(field_rot, pelist=pelist, mask_val=mask_val)
25200 deallocate(field_rot)
2521 endif
252231end function field_checksum_real_3d
2523
2524!> Compute the field checksum of an entire 4d array that may need to be rotated or unscaled.
2525!! This uses the field_chksum function that is used to verify file contents, which may differ
2526!! from the bitcount function used for other checksums in this module.
25270function field_checksum_real_4d(field, pelist, mask_val, turns, unscale) &
2528 result(chksum)
2529 real, dimension(:,:,:,:), intent(in) :: field !< Unrotated input field to be checksummed in
2530 !! arbitrary, possibly rescaled units [A ~> a]
2531 integer, optional, intent(in) :: pelist(:) !< PE list of ranks to checksum
2532 real, optional, intent(in) :: mask_val !< FMS mask value [nondim]
2533 integer, optional, intent(in) :: turns !< Number of quarter turns
2534 real, optional, intent(in) :: unscale !< A factor to convert this array back to
2535 !! unscaled units for checksums [a A-1 ~> 1]
2536 integer(kind=int64) :: chksum !< checksum of array
2537
2538 ! Local variables
25390 real, allocatable :: field_rot(:,:,:,:) ! A rotated version of field, with the same units [A ~> a]
2540 integer :: qturns ! The number of quarter turns through which to rotate field
2541 logical :: do_unscale ! If true, unscale the variable before it is checksummed
2542
25430 qturns = 0
25440 if (present(turns)) &
25450 qturns = modulo(turns, 4)
2546
25470 do_unscale = .false. ; if (present(unscale)) do_unscale = (unscale /= 1.0)
2548
25490 if (qturns == 0) then
25500 if (do_unscale) then
25510 chksum = field_chksum(unscale*field(:,:,:,:), pelist=pelist, mask_val=mask_val)
2552 else
25530 chksum = field_chksum(field, pelist=pelist, mask_val=mask_val)
2554 endif
2555 else
25560 call allocate_rotated_array(field, [1,1,1,1], qturns, field_rot)
25570 call rotate_array(field, qturns, field_rot)
25580 if (do_unscale) field_rot(:,:,:,:) = unscale*field_rot(:,:,:,:)
25590 chksum = field_chksum(field_rot, pelist=pelist, mask_val=mask_val)
25600 deallocate(field_rot)
2561 endif
25620end function field_checksum_real_4d
2563
2564
2565!> Write a message including the checksum of the non-shifted array
25660subroutine chk_sum_msg1(fmsg, bc0, mesg, iounit)
2567 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2568 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2569 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2570 integer, intent(in) :: iounit !< Checksum logger IO unit
2571
25720 if (is_root_pe()) &
25730 write(iounit, '(a,1(a,i10,1x),a)') fmsg, " c=", bc0, trim(mesg)
25740end subroutine chk_sum_msg1
2575
2576!> Write a message including checksums of non-shifted and diagonally shifted arrays
25770subroutine chk_sum_msg5(fmsg, bc0, bcSW, bcSE, bcNW, bcNE, mesg, iounit)
2578 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2579 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2580 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2581 integer, intent(in) :: bcSW !< The bitcount for SW shifted array
2582 integer, intent(in) :: bcSE !< The bitcount for SE shifted array
2583 integer, intent(in) :: bcNW !< The bitcount for NW shifted array
2584 integer, intent(in) :: bcNE !< The bitcount for NE shifted array
2585 integer, intent(in) :: iounit !< Checksum logger IO unit
2586
25870 if (is_root_pe()) write(iounit, '(A,5(A,I10,1X),A)') &
25880 fmsg, " c=", bc0, "sw=", bcSW, "se=", bcSE, "nw=", bcNW, "ne=", bcNE, trim(mesg)
25890end subroutine chk_sum_msg5
2590
2591!> Write a message including checksums of non-shifted and laterally shifted arrays
25920subroutine chk_sum_msg_NSEW(fmsg, bc0, bcN, bcS, bcE, bcW, mesg, iounit)
2593 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2594 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2595 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2596 integer, intent(in) :: bcN !< The bitcount for N shifted array
2597 integer, intent(in) :: bcS !< The bitcount for S shifted array
2598 integer, intent(in) :: bcE !< The bitcount for E shifted array
2599 integer, intent(in) :: bcW !< The bitcount for W shifted array
2600 integer, intent(in) :: iounit !< Checksum logger IO unit
2601
26020 if (is_root_pe()) write(iounit, '(A,5(A,I10,1X),A)') &
26030 fmsg, " c=", bc0, "N=", bcN, "S=", bcS, "E=", bcE, "W=", bcW, trim(mesg)
26040end subroutine chk_sum_msg_NSEW
2605
2606!> Write a message including checksums of non-shifted and southward shifted arrays
26070subroutine chk_sum_msg_S(fmsg, bc0, bcS, mesg, iounit)
2608 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2609 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2610 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2611 integer, intent(in) :: bcS !< The bitcount of the south-shifted array
2612 integer, intent(in) :: iounit !< Checksum logger IO unit
2613
26140 if (is_root_pe()) write(iounit, '(A,2(A,I10,1X),A)') &
26150 fmsg, " c=", bc0, "S=", bcS, trim(mesg)
26160end subroutine chk_sum_msg_S
2617
2618!> Write a message including checksums of non-shifted and westward shifted arrays
26190subroutine chk_sum_msg_W(fmsg, bc0, bcW, mesg, iounit)
2620 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2621 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2622 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2623 integer, intent(in) :: bcW !< The bitcount of the west-shifted array
2624 integer, intent(in) :: iounit !< Checksum logger IO unit
2625
26260 if (is_root_pe()) write(iounit, '(A,2(A,I10,1X),A)') &
26270 fmsg, " c=", bc0, "W=", bcW, trim(mesg)
26280end subroutine chk_sum_msg_W
2629
2630!> Write a message including checksums of non-shifted and southwestward shifted arrays
26310subroutine chk_sum_msg2(fmsg, bc0, bcSW, mesg, iounit)
2632 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2633 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2634 integer, intent(in) :: bc0 !< The bitcount of the non-shifted array
2635 integer, intent(in) :: bcSW !< The bitcount of the southwest-shifted array
2636 integer, intent(in) :: iounit !< Checksum logger IO unit
2637
26380 if (is_root_pe()) write(iounit, '(A,2(A,I9,1X),A)') &
26390 fmsg, " c=", bc0, "s/w=", bcSW, trim(mesg)
26400end subroutine chk_sum_msg2
2641
2642!> Write a message including the global mean, maximum and minimum of an array
26430subroutine chk_sum_msg3(fmsg, aMean, aMin, aMax, mesg, iounit)
2644 character(len=*), intent(in) :: fmsg !< A checksum code-location specific preamble
2645 character(len=*), intent(in) :: mesg !< An identifying message supplied by top-level caller
2646 real, intent(in) :: aMean !< The mean value of the array in arbitrary units [A]
2647 real, intent(in) :: aMin !< The minimum value of the array [A]
2648 real, intent(in) :: aMax !< The maximum value of the array [A]
2649 integer, intent(in) :: iounit !< Checksum logger IO unit
2650
2651 ! NOTE: We add zero to aMin and aMax to remove any negative zeros.
2652 ! This is due to inconsistencies of signed zero in local vs MPI calculations.
2653
26540 if (is_root_pe()) write(iounit, '(A,3(A,ES25.16,1X),A)') &
26550 fmsg, " mean=", aMean, "min=", (0. + aMin), "max=", (0. + aMax), trim(mesg)
26560end subroutine chk_sum_msg3
2657
2658!> MOM_checksums_init initializes the MOM_checksums module. As it happens, the
2659!! only thing that it does is to log the version of this module.
26601subroutine MOM_checksums_init(param_file)
2661 type(param_file_type), intent(in) :: param_file !< A structure to parse for run-time parameters
2662 ! This include declares and sets the variable "version".
2663# include "version_variable.h"
2664 character(len=40) :: mdl = "MOM_checksums" ! This module's name.
2665
26661 call log_version(param_file, mdl, version)
2667
26681end subroutine MOM_checksums_init
2669
2670!> A wrapper for MOM_error used in the checksum code
26710subroutine chksum_error(signal, message)
2672 ! Wrapper for MOM_error to help place specific break points in debuggers
2673 integer, intent(in) :: signal !< An error severity level, such as FATAL or WARNING
2674 character(len=*), intent(in) :: message !< An error message
26750 call MOM_error(signal, message)
26760end subroutine chksum_error
2677
2678!> Does a bitcount of a number by first casting to an integer and then using BTEST
2679!! to check bit by bit
26800integer function bitcount(x)
2681 real, intent(in) :: x !< Number to be bitcount in arbitrary units [A]
2682
2683 integer, parameter :: xk = kind(x) !< Kind type of x
2684
2685 ! NOTE: Assumes that reals and integers of kind=xk are the same size
26860 bitcount = popcnt(transfer(x, 1_xk))
26870end function bitcount
2688
2689end module MOM_checksums