← back to index

src/framework/MOM_array_transform.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!> Module for supporting the rotation of a field's index map.
6!! The implementation of each angle is described below.
7!!
8!! +90deg: B(i,j) = A(n-j,i)
9!! = transpose, then row reverse
10!! 180deg: B(i,j) = A(m-i,n-j)
11!! = row reversal + column reversal
12!! -90deg: B(i,j) = A(j,m-i)
13!! = row reverse, then transpose
14!!
15!! 90 degree rotations change the shape of the field, and are handled
16!! separately from 180 degree rotations.
17!!
18!! It also provides the symmetric_sum functions to do a rotationally invariant
19!! sum of the contents of a 1d or 2d array.
20
21module MOM_array_transform
22
23use iso_fortran_env, only : stdout=>output_unit, stderr=>error_unit
24
25implicit none ; private
26
27public rotate_array
28public rotate_array_pair
29public rotate_vector
30public allocate_rotated_array
31public symmetric_sum
32public symmetric_sum_unit_tests
33
34
35!> Rotate the elements of an array to the rotated set of indices.
36!! Rotation is applied across the first and second axes of the array.
37interface rotate_array
38 module procedure rotate_array_real_2d
39 module procedure rotate_array_real_3d
40 module procedure rotate_array_real_4d
41 module procedure rotate_array_integer
42 module procedure rotate_array_logical
43end interface rotate_array
44
45
46!> Rotate a pair of arrays which map to a rotated set of indices.
47!! Rotation is applied across the first and second axes of the array.
48!! This rotation should be applied when one field is mapped onto the other.
49!! For example, a tracer indexed along u or v face points will map from one
50!! to the other after a quarter turn, and back onto itself after a half turn.
51interface rotate_array_pair
52 module procedure rotate_array_pair_real_2d
53 module procedure rotate_array_pair_real_3d
54 module procedure rotate_array_pair_integer
55end interface rotate_array_pair
56
57
58!> Rotate an array pair representing the components of a vector.
59!! Rotation is applied across the first and second axes of the array.
60!! This rotation should be applied when the fields satisfy vector
61!! transformation rules. For example, the u and v components of a velocity
62!! will map from one to the other for quarter turns, with a sign change in one
63!! component. A half turn will map elements onto themselves with sign changes
64!! in both components.
65interface rotate_vector
66 module procedure rotate_vector_real_2d
67 module procedure rotate_vector_real_3d
68 module procedure rotate_vector_real_4d
69end interface rotate_vector
70
71
72!> Allocate an array based on the rotated index map of an unrotated reference array.
73interface allocate_rotated_array
74 module procedure allocate_rotated_array_real_2d
75 module procedure allocate_rotated_array_real_3d
76 module procedure allocate_rotated_array_real_4d
77 module procedure allocate_rotated_array_integer
78end interface allocate_rotated_array
79
80
81!> Return a rotationally symmetric sum of the elements of an array.
82interface symmetric_sum
83 module procedure symmetric_sum_1d, symmetric_sum_2d
84end interface symmetric_sum
85
86
87contains
88
89!> Rotate the elements of a 2d real array along first and second axes.
902360subroutine rotate_array_real_2d(A_in, turns, A)
91 real, intent(in) :: A_in(:,:) !< Unrotated array [arbitrary]
92 integer, intent(in) :: turns !< Number of quarter turns
93 real, intent(out) :: A(:,:) !< Rotated array [arbitrary]
94
95 integer :: m, n
96
972360 m = size(A_in, 1)
982360 n = size(A_in, 2)
99
1004720 select case (modulo(turns, 4))
101 case(0)
10220852970 A(:,:) = A_in(:,:)
103 case(1)
1040 A(:,:) = transpose(A_in)
1050 A(:,:) = A(n:1:-1, :)
106 case(2)
1070 A(:,:) = A_in(m:1:-1, n:1:-1)
108 case(3)
1092360 A(:,:) = transpose(A_in(m:1:-1, :))
110 end select
1112360end subroutine rotate_array_real_2d
112
113
114!> Rotate the elements of a 3d real array along first and second axes.
11531subroutine rotate_array_real_3d(A_in, turns, A)
116 real, intent(in) :: A_in(:,:,:) !< Unrotated array [arbitrary]
117 integer, intent(in) :: turns !< Number of quarter turns
118 real, intent(out) :: A(:,:,:) !< Rotated array [arbitrary]
119
120 integer :: k
121
1222363 do k = 1, size(A_in, 3)
1232363 call rotate_array(A_in(:,:,k), turns, A(:,:,k))
124 enddo
12531end subroutine rotate_array_real_3d
126
127
128!> Rotate the elements of a 4d real array along first and second axes.
1290subroutine rotate_array_real_4d(A_in, turns, A)
130 real, intent(in) :: A_in(:,:,:,:) !< Unrotated array [arbitrary]
131 integer, intent(in) :: turns !< Number of quarter turns
132 real, intent(out) :: A(:,:,:,:) !< Rotated array [arbitrary]
133
134 integer :: n
135
1360 do n = 1, size(A_in, 4)
1370 call rotate_array(A_in(:,:,:,n), turns, A(:,:,:,n))
138 enddo
1390end subroutine rotate_array_real_4d
140
141
142!> Rotate the elements of a 2d integer array along first and second axes.
1430subroutine rotate_array_integer(A_in, turns, A)
144 integer, intent(in) :: A_in(:,:) !< Unrotated array
145 integer, intent(in) :: turns !< Number of quarter turns
146 integer, intent(out) :: A(:,:) !< Rotated array
147
148 integer :: m, n
149
1500 m = size(A_in, 1)
1510 n = size(A_in, 2)
152
1530 select case (modulo(turns, 4))
154 case(0)
1550 A(:,:) = A_in(:,:)
156 case(1)
1570 A(:,:) = transpose(A_in)
1580 A(:,:) = A(n:1:-1, :)
159 case(2)
1600 A(:,:) = A_in(m:1:-1, n:1:-1)
161 case(3)
1620 A(:,:) = transpose(A_in(m:1:-1, :))
163 end select
1640end subroutine rotate_array_integer
165
166
167!> Rotate the elements of a 2d logical array along first and second axes.
1680subroutine rotate_array_logical(A_in, turns, A)
169 logical, intent(in) :: A_in(:,:) !< Unrotated array
170 integer, intent(in) :: turns !< Number of quarter turns
171 logical, intent(out) :: A(:,:) !< Rotated array
172
173 integer :: m, n
174
1750 m = size(A_in, 1)
1760 n = size(A_in, 2)
177
1780 select case (modulo(turns, 4))
179 case(0)
1800 A(:,:) = A_in(:,:)
181 case(1)
1820 A(:,:) = transpose(A_in)
1830 A(:,:) = A(n:1:-1, :)
184 case(2)
1850 A(:,:) = A_in(m:1:-1, n:1:-1)
186 case(3)
1870 A(:,:) = transpose(A_in(m:1:-1, :))
188 end select
1890end subroutine rotate_array_logical
190
191
192!> Rotate the elements of a 2d real array pair along first and second axes.
1930subroutine rotate_array_pair_real_2d(A_in, B_in, turns, A, B)
194 real, intent(in) :: A_in(:,:) !< Unrotated scalar array pair [arbitrary]
195 real, intent(in) :: B_in(:,:) !< Unrotated scalar array pair [arbitrary]
196 integer, intent(in) :: turns !< Number of quarter turns
197 real, intent(out) :: A(:,:) !< Rotated scalar array pair [arbitrary]
198 real, intent(out) :: B(:,:) !< Rotated scalar array pair [arbitrary]
199
2000 if (modulo(turns, 2) /= 0) then
2010 call rotate_array(B_in, turns, A)
2020 call rotate_array(A_in, turns, B)
203 else
2040 call rotate_array(A_in, turns, A)
2050 call rotate_array(B_in, turns, B)
206 endif
2070end subroutine rotate_array_pair_real_2d
208
209
210!> Rotate the elements of a 3d real array pair along first and second axes.
2110subroutine rotate_array_pair_real_3d(A_in, B_in, turns, A, B)
212 real, intent(in) :: A_in(:,:,:) !< Unrotated scalar array pair [arbitrary]
213 real, intent(in) :: B_in(:,:,:) !< Unrotated scalar array pair [arbitrary]
214 integer, intent(in) :: turns !< Number of quarter turns
215 real, intent(out) :: A(:,:,:) !< Rotated scalar array pair [arbitrary]
216 real, intent(out) :: B(:,:,:) !< Rotated scalar array pair [arbitrary]
217
218 integer :: k
219
2200 do k = 1, size(A_in, 3)
221 call rotate_array_pair(A_in(:,:,k), B_in(:,:,k), turns, &
2220 A(:,:,k), B(:,:,k))
223 enddo
2240end subroutine rotate_array_pair_real_3d
225
226
227!> Rotate the elements of a 4d real array pair along first and second axes.
2280subroutine rotate_array_pair_integer(A_in, B_in, turns, A, B)
229 integer, intent(in) :: A_in(:,:) !< Unrotated scalar array pair
230 integer, intent(in) :: B_in(:,:) !< Unrotated scalar array pair
231 integer, intent(in) :: turns !< Number of quarter turns
232 integer, intent(out) :: A(:,:) !< Rotated scalar array pair
233 integer, intent(out) :: B(:,:) !< Rotated scalar array pair
234
2350 if (modulo(turns, 2) /= 0) then
2360 call rotate_array(B_in, turns, A)
2370 call rotate_array(A_in, turns, B)
238 else
2390 call rotate_array(A_in, turns, A)
2400 call rotate_array(B_in, turns, B)
241 endif
2420end subroutine rotate_array_pair_integer
243
244
245!> Rotate the elements of a 2d real vector along first and second axes.
2460subroutine rotate_vector_real_2d(A_in, B_in, turns, A, B)
247 real, intent(in) :: A_in(:,:) !< First component of unrotated vector [arbitrary]
248 real, intent(in) :: B_in(:,:) !< Second component of unrotated vector [arbitrary]
249 integer, intent(in) :: turns !< Number of quarter turns
250 real, intent(out) :: A(:,:) !< First component of rotated vector [arbitrary]
251 real, intent(out) :: B(:,:) !< Second component of unrotated vector [arbitrary]
252
2530 call rotate_array_pair(A_in, B_in, turns, A, B)
254
2550 if (modulo(turns, 4) == 1 .or. modulo(turns, 4) == 2) &
2560 A(:,:) = -A(:,:)
257
2580 if (modulo(turns, 4) == 2 .or. modulo(turns, 4) == 3) &
2590 B(:,:) = -B(:,:)
2600end subroutine rotate_vector_real_2d
261
262
263!> Rotate the elements of a 3d real vector along first and second axes.
2640subroutine rotate_vector_real_3d(A_in, B_in, turns, A, B)
265 real, intent(in) :: A_in(:,:,:) !< First component of unrotated vector [arbitrary]
266 real, intent(in) :: B_in(:,:,:) !< Second component of unrotated vector [arbitrary]
267 integer, intent(in) :: turns !< Number of quarter turns
268 real, intent(out) :: A(:,:,:) !< First component of rotated vector [arbitrary]
269 real, intent(out) :: B(:,:,:) !< Second component of unrotated vector [arbitrary]
270
271 integer :: k
272
2730 do k = 1, size(A_in, 3)
2740 call rotate_vector(A_in(:,:,k), B_in(:,:,k), turns, A(:,:,k), B(:,:,k))
275 enddo
2760end subroutine rotate_vector_real_3d
277
278
279!> Rotate the elements of a 4d real vector along first and second axes.
2800subroutine rotate_vector_real_4d(A_in, B_in, turns, A, B)
281 real, intent(in) :: A_in(:,:,:,:) !< First component of unrotated vector [arbitrary]
282 real, intent(in) :: B_in(:,:,:,:) !< Second component of unrotated vector [arbitrary]
283 integer, intent(in) :: turns !< Number of quarter turns
284 real, intent(out) :: A(:,:,:,:) !< First component of rotated vector [arbitrary]
285 real, intent(out) :: B(:,:,:,:) !< Second component of unrotated vector [arbitrary]
286
287 integer :: n
288
2890 do n = 1, size(A_in, 4)
290 call rotate_vector(A_in(:,:,:,n), B_in(:,:,:,n), turns, &
2910 A(:,:,:,n), B(:,:,:,n))
292 enddo
2930end subroutine rotate_vector_real_4d
294
295
296!> Allocate a 2d real array on the rotated index map of a reference array.
29728subroutine allocate_rotated_array_real_2d(A_in, lb, turns, A)
298 ! NOTE: lb must be declared before A_in
299 integer, intent(in) :: lb(2) !< Lower index bounds of A_in
300 real, intent(in) :: A_in(lb(1):, lb(2):) !< Reference array [arbitrary]
301 integer, intent(in) :: turns !< Number of quarter turns
302 real, allocatable, intent(inout) :: A(:,:) !< Array on rotated index [arbitrary]
303
304 integer :: ub(2)
305
30684 ub(:) = ubound(A_in)
307
30828 if (modulo(turns, 2) /= 0) then
3090 allocate(A(lb(2):ub(2), lb(1):ub(1)))
310 else
31128 allocate(A(lb(1):ub(1), lb(2):ub(2)))
312 endif
31328end subroutine allocate_rotated_array_real_2d
314
315
316!> Allocate a 3d real array on the rotated index map of a reference array.
31731subroutine allocate_rotated_array_real_3d(A_in, lb, turns, A)
318 ! NOTE: lb must be declared before A_in
319 integer, intent(in) :: lb(3) !< Lower index bounds of A_in
320 real, intent(in) :: A_in(lb(1):, lb(2):, lb(3):) !< Reference array [arbitrary]
321 integer, intent(in) :: turns !< Number of quarter turns
322 real, allocatable, intent(inout) :: A(:,:,:) !< Array on rotated index [arbitrary]
323
324 integer :: ub(3)
325
326124 ub(:) = ubound(A_in)
327
32831 if (modulo(turns, 2) /= 0) then
3290 allocate(A(lb(2):ub(2), lb(1):ub(1), lb(3):ub(3)))
330 else
33131 allocate(A(lb(1):ub(1), lb(2):ub(2), lb(3):ub(3)))
332 endif
33331end subroutine allocate_rotated_array_real_3d
334
335
336!> Allocate a 4d real array on the rotated index map of a reference array.
3370subroutine allocate_rotated_array_real_4d(A_in, lb, turns, A)
338 ! NOTE: lb must be declared before A_in
339 integer, intent(in) :: lb(4) !< Lower index bounds of A_in
340 real, intent(in) :: A_in(lb(1):,lb(2):,lb(3):,lb(4):) !< Reference array [arbitrary]
341 integer, intent(in) :: turns !< Number of quarter turns
342 real, allocatable, intent(inout) :: A(:,:,:,:) !< Array on rotated index [arbitrary]
343
344 integer:: ub(4)
345
3460 ub(:) = ubound(A_in)
347
3480 if (modulo(turns, 2) /= 0) then
3490 allocate(A(lb(2):ub(2), lb(1):ub(1), lb(3):ub(3), lb(4):ub(4)))
350 else
3510 allocate(A(lb(1):ub(1), lb(2):ub(2), lb(3):ub(3), lb(4):ub(4)))
352 endif
3530end subroutine allocate_rotated_array_real_4d
354
355
356!> Allocate a 2d integer array on the rotated index map of a reference array.
3570subroutine allocate_rotated_array_integer(A_in, lb, turns, A)
358 integer, intent(in) :: lb(2) !< Lower index bounds of A_in
359 integer, intent(in) :: A_in(lb(1):,lb(2):) !< Reference array
360 integer, intent(in) :: turns !< Number of quarter turns
361 integer, allocatable, intent(inout) :: A(:,:) !< Array on rotated index
362
363 integer :: ub(2)
364
3650 ub(:) = ubound(A_in)
366
3670 if (modulo(turns, 2) /= 0) then
3680 allocate(A(lb(2):ub(2), lb(1):ub(1)))
369 else
3700 allocate(A(lb(1):ub(1), lb(2):ub(2)))
371 endif
3720end subroutine allocate_rotated_array_integer
373
374
375!> Do a rotationally symmetric sum of a 1-d array
3760function symmetric_sum_1d(field) result(sum)
377 real, dimension(1:), intent(in) :: field !< The field to sum in arbitrary units [A ~> a]
378 real :: sum !< The rotationally symmetric sum of the entries in field [A ~> a]
379
380 ! Local variables
381 integer :: i, szi, szi_2
382
3830 szi = size(field, 1)
3840 szi_2 = szi / 2 ! Note that for an odd number szi_2 is rounded down.
3850 sum = 0.0
3860 if (2*szi_2 < szi) sum = field(szi_2+1)
387 ! Add pairs of values, working from the inside out.
3880 do i=szi_2,1,-1
3890 sum = sum + (field(i) + field(szi+1-i))
390 enddo
3910end function symmetric_sum_1d
392
393
394!> Do a rotationally symmetric sum of a 2-d array using a recursive "Union-Jack" pattern of addition.
3950recursive function symmetric_sum_2d(field) result(sum)
396 real, dimension(1:,1:), intent(in) :: field !< The field to sum in arbitrary units [A ~> a]
397 real :: sum !< The rotationally symmetric sum of the entries in field [A ~> a]
398
399 ! Local variables
400 real :: quad_sum(2,2) ! The sums in each of the quadrants [A ~> a]
401 logical :: odd_i, odd_j
402 integer :: ij, szi, szj, szi_2, szj_2, ic, jc
403
4040 szi = size(field, 1) ; szj = size(field, 2)
405 ! These 5 special cases are equivalent to the general case, but they reduce the use
406 ! of complicated logic for common simple cases.
4070 if ((szi == 1) .and. (szj == 1)) then
4080 sum = field(1,1)
4090 elseif ((szi == 2) .and. (szj == 2)) then
4100 sum = (field(1,1) + field(2,2)) + (field(2,1) + field(1,2))
4110 elseif ((szi == 3) .and. (szj == 3)) then
412 sum = (field(2,2) + ((field(1,2) + field(3,2)) + (field(2,1) + field(2,3)))) + &
4130 ((field(1,1) + field(3,3)) + (field(3,1) + field(1,3)))
4140 elseif (szi == 1) then
4150 sum = symmetric_sum_1d(field(1,:))
4160 elseif (szj == 1) then
4170 sum = symmetric_sum_1d(field(:,1))
418 else
419 ! This is the general case.
420 ! Note that for odd numbers szi_2 and szj_2 are rounded down.
4210 szi_2 = szi / 2
4220 szj_2 = szj / 2
423
4240 odd_i = (2*szi_2 < szi) ! This could be (modulo(szi,2) == 1)
4250 odd_j = (2*szj_2 < szj)
426 ! Start by finding the sums along the central axes if there are an odd number of points.
4270 if (odd_i .and. odd_j) then
4280 ic = szi_2+1 ; jc = szj_2+1 ! The index of the central point
4290 sum = field(ic,jc)
430 ! Add pairs of pairs of values, working from the inside out.
4310 do ij=1,min(szi_2,szj_2)
4320 sum = sum + ((field(ic-ij,jc) + field(ic+ij,jc)) + (field(ic,jc-ij) + field(ic,jc+ij)))
433 enddo
434 ! Add extra pairs of values, working from the inside out.
4350 if (szi_2 > szj_2) then
4360 do ij=szj_2+1,szi_2
4370 sum = sum + (field(ic-ij,jc) + field(ic+ij,jc))
438 enddo
4390 elseif (szj_2 > szi_2) then
4400 do ij=szi_2+1,szj_2
4410 sum = sum + (field(ic,jc-ij) + field(ic,jc+ij))
442 enddo
443 endif
4440 elseif (odd_i) then
4450 sum = symmetric_sum_1d(field(szi_2+1,1:szj))
4460 elseif (odd_j) then
4470 sum = symmetric_sum_1d(field(1:szi,szj_2+1))
448 else
4490 sum = 0.0
450 endif
451
452 ! Find the sums in the four quadrants of the array.
4530 if ((szi_2 > 1) .and. (szj_2 > 1)) then
454 ! Use a recursive call to symmetric_sum_2d to determine the sums in the corner quadrants.
4550 quad_sum(1,1) = symmetric_sum_2d(field(1:szi_2,1:szj_2))
4560 quad_sum(2,1) = symmetric_sum_2d(field(szi+1-szi_2:szi,1:szj_2))
4570 quad_sum(1,2) = symmetric_sum_2d(field(1:szi_2,szj+1-szj_2:szj))
4580 quad_sum(2,2) = symmetric_sum_2d(field(szi+1-szi_2:szi,szj+1-szj_2:szj))
4590 elseif (szi_2 > 1) then
4600 quad_sum(1,1) = symmetric_sum_1d(field(1:szi_2,1))
4610 quad_sum(2,1) = symmetric_sum_1d(field(szi+1-szi_2:szi,1))
4620 quad_sum(1,2) = symmetric_sum_1d(field(1:szi_2,szj))
4630 quad_sum(2,2) = symmetric_sum_1d(field(szi+1-szi_2:szi,szj))
4640 elseif (szj_2 > 1) then
4650 quad_sum(1,1) = symmetric_sum_1d(field(1,1:szj_2))
4660 quad_sum(2,1) = symmetric_sum_1d(field(szi,1:szj_2))
4670 quad_sum(1,2) = symmetric_sum_1d(field(1,szj+1-szj_2:szj))
4680 quad_sum(2,2) = symmetric_sum_1d(field(szi,szj+1-szj_2:szj))
469 else
4700 quad_sum(1,1) = field(1,1)
4710 quad_sum(2,1) = field(szi,1)
4720 quad_sum(1,2) = field(1,szj)
4730 quad_sum(2,2) = field(szi,szj)
474 endif
475
4760 sum = sum + ((quad_sum(1,1) + quad_sum(2,2)) + (quad_sum(2,1) + quad_sum(1,2)))
477 endif
4780end function symmetric_sum_2d
479
480
481!> Do a naive non-rotationally symmetric sum of a 2-d array. This function is only here for testing.
4820function naive_sum_2d(field, abs_val) result(sum)
483 real, dimension(1:,1:), intent(in) :: field !< The field to sum in arbitrary units [A ~> a]
484 logical, optional, intent(in) :: abs_val !< If present and true, sum the absolute values
485 real :: sum !< The rotation dependent sum of the entries in field [A ~> a]
486
487 ! Local variables
488 logical :: sum_abs_val
489 integer :: i, j, szi, szj
490
4910 szi = size(field, 1) ; szj = size(field, 2)
4920 sum_abs_val = .false. ; if (present(abs_val)) sum_abs_val = abs_val
4930 sum = 0.0
4940 if (sum_abs_val) then
4950 do j=1,szj ; do i=1,szi
4960 sum = sum + abs(field(i,j))
497 enddo ; enddo
498 else
4990 do j=1,szj ; do i=1,szi
5000 sum = sum + field(i,j)
501 enddo ; enddo
502 endif
5030end function naive_sum_2d
504
505
506!> Returns true if a unit test of the symmetric sums fails.
5070logical function symmetric_sum_unit_tests(verbose)
508 ! Arguments
509 logical, intent(in) :: verbose !< If true, write results to stdout
510 ! Local variables
511 character(len=120) :: fail_message !< Blank or a description of the first failed test.
512 integer, parameter :: sz=13 ! The maximum size of the test arrays
513 real :: array(sz,sz) ! An array of inexact real values for testing in arbitrary units [A]
514 real :: ar_90(sz,sz) ! Array rotated by 90 degrees in arbitrary units [A]
515 real :: ar_180(sz,sz) ! Array rotated by 180 degrees in arbitrary units [A]
516 real :: ar_270(sz,sz) ! Array rotated by 270 degrees in arbitrary units [A]
517 real :: sum(5) ! Different versions of sums over a sub-array [A]
518 real :: abs_sum ! The sum of the absolute values of the array [A]
519 real :: tol ! The tolerance for an inexact test [A]
520
521 character(len=120) :: mesg
522 integer :: i, j, n, m, r
523 logical :: fail
524
5250 fail = .false.
5260 fail_message = ""
527
5280 if (verbose) write(stdout,*) '==== MOM_array_transform: symmetric_sum_unit_tests ===='
529
530 ! Fill the array with real numbers that can not be represented exactly.
5310 do j=1,sz ; do i=1,sz
5320 array(i,j) = 1.0 / (2.0*(j*sz + i) + 1.0)
533 ! Combining positive and negative numbers amplifies differences from the order of arithmetic.
5340 if (modulo(i+j, 2) == 0) array(i,j) = -array(i,j)
535 enddo ; enddo
5360 call rotate_array_real_2d(array, 1, ar_90)
5370 call rotate_array_real_2d(array, 2, ar_180)
5380 call rotate_array_real_2d(array, 3, ar_270)
539
5400 do n = 1, sz ; do m = 1, sz
5410 sum(1) = symmetric_sum(array(1:n,1:m))
5420 sum(2) = symmetric_sum(ar_90(sz+1-m:sz,1:n))
5430 sum(3) = symmetric_sum(ar_180(sz+1-n:sz,sz+1-m:sz))
5440 sum(4) = symmetric_sum(ar_270(1:m,sz+1-n:sz))
5450 sum(5) = naive_sum_2d(array(1:n,1:m))
5460 abs_sum = naive_sum_2d(array(1:n,1:m), abs_val=.true.)
5470 tol = 2.0 * abs_sum * epsilon(abs_sum)
5480 if (abs(sum(1) - sum(5)) > tol) then
549 write(mesg,'(i0," x ",i0," symmetric vs naive sum, sum=",ES13.5," diff=",ES13.5)') &
5500 n, m, sum(1), sum(5) - sum(1)
5510 write(stdout,*) "Symmetric_sum_failure: "//trim(mesg)
5520 write(stderr,*) "Symmetric_sum_failure: "//trim(mesg)
5530 if (.not.fail) fail_message = mesg ! This is the first failed test.
5540 fail = .true.
555 endif
5560 do r = 2, 4 ; if (abs(sum(1) - sum(r)) > 0.0) then
557 write(mesg,'(i0," x ",i0," with ",i0," degree rotation, sum=",ES13.5," diff=",ES13.5)') &
5580 n, m, 90*(r-1), sum(1), sum(r) - sum(1)
5590 write(stdout,*) "Symmetric_sum_failure: "//trim(mesg)
5600 write(stderr,*) "Symmetric_sum_failure: "//trim(mesg)
5610 if (.not.fail) fail_message = mesg ! This is the first failed test.
5620 fail = .true.
563 endif ; enddo
564 enddo ; enddo
565
5660 if (fail) then
5670 write(stdout,*) "MOM_array_transform: One or more symmetric sum tests has failed."
5680 write(stderr,*) "MOM_array_transform: One or more symmetric sum tests has failed."
569 else
5700 if (verbose) write(stdout,*) ("MOM_array_transform: All symmetric sum tests have passed.")
571 endif
5720 symmetric_sum_unit_tests = fail
573
5740end function symmetric_sum_unit_tests
575
576end module MOM_array_transform