File: test_adjoint.F90

package info (click to toggle)
ectrans 1.7.0-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,968 kB
  • sloc: f90: 51,064; ansic: 5,942; cpp: 1,112; python: 488; sh: 127; makefile: 47
file content (291 lines) | stat: -rw-r--r-- 9,049 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
! (C) Copyright 2005- ECMWF.
! 
! This software is licensed under the terms of the Apache Licence Version 2.0
! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
! In applying this licence, ECMWF does not waive the privileges and immunities
! granted to it by virtue of its status as an intergovernmental organisation
! nor does it submit to any jurisdiction.
!

! ==================================================================================================
! Adjoint test
! ==================================================================================================
!
! This program performs a rudimentary check of tangent-linear/adjoint correspondence of the inverse
! and direct spectral transform.
!
! The program checks the correspondence of <DIR_TRANS(INV_TRANS(X)), Y> and
! <X, INV_TRANSAD(DIR_TRANSAD(Y))>, which with infinite precision should match exactly. In practice
! there is some divergence due to rounding errors. In this program we check whether the two
! expressions are the same to within a tolerance of 2000 * machine epsilon.
!
! The check is only performed for scalar fields (PSPSCALAR). Wind fields are not checked.
!
! ==================================================================================================

PROGRAM TEST_ADJOINT

USE PARKIND1,        ONLY: JPIM, JPRB
USE MPL_MODULE,      ONLY: MPL_INIT, MPL_MYRANK, MPL_NPROC, MPL_BARRIER, MPL_END
USE ABORT_TRANS_MOD, ONLY: ABORT_TRANS

IMPLICIT NONE

INTEGER(KIND=JPIM) :: NSMAX, NDGL, NPROC, NPRGPNS, NPRGPEW, NPRTRW, NPRTRV
INTEGER(KIND=JPIM) :: NOUT, NERR, MYPROC, NSPECG, NSPEC2G
INTEGER(KIND=JPIM) :: NFLEV, NFLEVG
INTEGER(KIND=JPIM) :: NSPEC2, NGPTOT, NPROMA, NGPBLKS, MYSETV, NUMP
INTEGER(KIND=JPIM), ALLOCATABLE :: IVSET(:)
INTEGER(KIND=JPIM), ALLOCATABLE :: NLOEN(:), MYMS(:), NASM0(:)
INTEGER(KIND=JPIM) :: JLEV

REAL(KIND=JPRB) , ALLOCATABLE :: ZSPECX(:,:), ZSPECY(:,:), ZSPECP(:,:)
REAL(KIND=JPRB) , ALLOCATABLE :: ZGX(:,:,:)
REAL(KIND=JPRB) , ALLOCATABLE :: ZSPECYG(:,:), ZSPECXG(:,:)
REAL(KIND=JPRB) , ALLOCATABLE :: ZRANDSP(:)
REAL(KIND=JPRB) :: ZSC1, ZSC2, ZRELATIVE_ERROR
INTEGER(KIND=JPIM) :: JA, JB, I
LOGICAL :: LUSE_MPI
INTEGER(KIND=JPIM) :: N
INTEGER(KIND=JPIM), ALLOCATABLE :: SEED(:)

#include "setup_trans0.h"
#include "setup_trans.h"
#include "trans_inq.h"
#include "dir_trans.h"
#include "inv_trans.h"
#include "dir_transad.h"
#include "inv_transad.h"
#include "dist_grid.h"
#include "gath_spec.h"
#include "dist_spec.h"
#include "trans_end.h"

! Fix random number seed
CALL RANDOM_SEED(SIZE=N)
ALLOCATE(SEED(N))
SEED(:) = 1
CALL RANDOM_SEED(PUT=SEED)

LUSE_MPI = DETECT_MPIRUN()

NDGL = 32 ! Number of latitudes from pole to equator
NFLEVG = 9 ! Number of levels
NPROMA = 8 ! Gridpoint block size

! Determine spectral space parameters
NSMAX = (2 * NDGL - 1) / 3 ! Full Gaussian grid
NSPECG = (NSMAX+1)*(NSMAX+2)/2
NSPEC2G = NSPECG*2

IF (LUSE_MPI) THEN
  CALL MPL_INIT
  MYPROC = MPL_MYRANK()
  NPROC = MPL_NPROC()
ELSE
  MYPROC = 1
  NPROC  = 1
ENDIF

! STDOUT and STDERR
NOUT = 6
NERR = 0

! Only output to stdout on first task
IF (NPROC > 1) THEN
  IF (MYPROC /= 1) THEN
    OPEN(UNIT=NOUT, FILE='/dev/null')
  ENDIF
ENDIF

! Compute E-W and V-W set sizes
DO JA = INT(SQRT(REAL(NPROC,JPRB))), NPROC
  JB = NPROC / JA
  IF (JA * JB == NPROC) THEN
    NPRGPNS = MAX(JA, JB)
    NPRGPEW = MIN(JA, JB)
    NPRTRW  = MAX(JA, JB)
    NPRTRV  = MIN(JA, JB)
  ENDIF
ENDDO

MYSETV = MOD(MYPROC-1,NPRTRV)+1

! Allocate global spectral arrays
ALLOCATE(ZSPECYG(NFLEVG,NSPEC2G))
ALLOCATE(ZSPECXG(NFLEVG,NSPEC2G))

! Array for storing random perturbations
ALLOCATE(ZRANDSP(NSPEC2G))

! Use a full Gaussian grid
ALLOCATE(NLOEN(NDGL))
NLOEN(:) = 2*NDGL

! Initialise ecTrans
CALL SETUP_TRANS0(KOUT=NOUT, KERR=NERR, KPRINTLEV=0, KMAX_RESOL=1, KPRGPNS=NPRGPNS, &
  &               KPRGPEW=NPRGPEW, KPRTRW=NPRTRW, LDMPOFF=.NOT. LUSE_MPI)
CALL SETUP_TRANS(KSMAX=NSMAX, KDGL=NDGL, KLOEN=NLOEN, LDSPLIT=.TRUE.)

CALL TRANS_INQ(KSPEC2=NSPEC2, KGPTOT=NGPTOT, KNUMP=NUMP)

! Get Ms I'm responsible for (MYMS) and spectral packing indices (NASM0)
ALLOCATE(MYMS(NUMP))
ALLOCATE(NASM0(0:NSMAX))
CALL TRANS_INQ(KMYMS=MYMS, KASM0=NASM0)

! Calculate number of NPROMA blocks
NGPBLKS = (NGPTOT - 1) / NPROMA + 1

! Determine VSET allocation and number of local levels
ALLOCATE(IVSET(NFLEVG))
NFLEV = 0
DO JLEV = 1, NFLEVG
  IVSET(JLEV) = MOD(JLEV,NPRTRV) + 1
  IF (IVSET(JLEV) == MYSETV) THEN
    NFLEV = NFLEV + 1
  ENDIF
ENDDO

! Local spectral arrays
ALLOCATE(ZSPECX(NFLEV,NSPEC2))
ALLOCATE(ZSPECY(NFLEV,NSPEC2))
ALLOCATE(ZSPECP(NFLEV,NSPEC2))

! Temporary grid point array
ALLOCATE(ZGX(NPROMA,NFLEVG,NGPBLKS))

! Prepare perturbations (random numbers between -1 and +1)
IF (MYPROC == 1) THEN
  DO JLEV=1,NFLEVG
    CALL RANDOM_NUMBER(ZRANDSP)
    ZSPECYG(JLEV,:) = (1.0_JPRB-2.0_JPRB*ZRANDSP(:))
    CALL RANDOM_NUMBER(ZRANDSP)
    ZSPECXG(JLEV,:) = (1.0_JPRB-2.0_JPRB*ZRANDSP(:))
  ENDDO
ENDIF

! Distribute global spectral arrays
CALL DIST_SPEC(PSPECG=ZSPECXG, KFDISTG=NFLEVG, KFROM=(/ (1, I = 1, NFLEVG) /), PSPEC=ZSPECX, &
  &            KVSET=IVSET)
CALL DIST_SPEC(PSPECG=ZSPECYG, KFDISTG=NFLEVG, KFROM=(/ (1, I = 1, NFLEVG) /), PSPEC=ZSPECY, &
  &            KVSET=IVSET)

! Calculate DIR_TRANS(INV_TRANS(X))
CALL INV_TRANS(PSPSCALAR=ZSPECX, PGP=ZGX, KPROMA=NPROMA, KVSETSC=IVSET)
CALL DIR_TRANS(PSPSCALAR=ZSPECP, PGP=ZGX, KPROMA=NPROMA, KVSETSC=IVSET)

! Calculate <DIR_TRANS(INV_TRANS(X)), Y>
CALL SCALPRODSP(ZSPECP, ZSPECY, ZSC1, IVSET)

ZSPECP = 0.0_JPRB
! Calculate INV_TRANSAD(DIR_TRANSAD(Y))
CALL DIR_TRANSAD(PSPSCALAR=ZSPECY, PGP=ZGX, KPROMA=NPROMA, KVSETSC=IVSET)
CALL INV_TRANSAD(PSPSCALAR=ZSPECP, PGP=ZGX, KPROMA=NPROMA, KVSETSC=IVSET)

! Calculate <X, INV_TRANSAD(DIR_TRANSAD(Y))>
CALL SCALPRODSP(ZSPECX, ZSPECP, ZSC2, IVSET)

! If I'm the first task, do the error check
IF (MYPROC == 1) THEN
  ! Calculate relative error between <DIR_TRANS(INV_TRANS(X)), Y> and <X, INV_TRANSAD(DIR_TRANSAD(Y))>
  ZRELATIVE_ERROR = ABS(ZSC1 - ZSC2)/ABS(ZSC1)

  WRITE(NOUT, '(A,1E9.2)') '<Fx,y>  = ', ZSC1
  WRITE(NOUT, '(A,1E9.2)') '<x,F*y> = ', ZSC2
  WRITE(NOUT, '(A,1E9.2)') 'Relative error = ', ZRELATIVE_ERROR

  ! Abort if relative error is > 2000 * machine epsilon
  ! All tested compilers seem to be happy with a threshold of 2000, though it is a bit arbitrary
  IF (ZRELATIVE_ERROR > 2000.0*EPSILON(1.0_JPRB)) THEN
    WRITE(NERR, '(A)') '*******************************'
    WRITE(NERR, '(A)') 'Adjoint test failed'
    WRITE(NERR, '(A)') 'Relative error greater than 2000 * machine epsilon'
    WRITE(NERR, '(1E9.2,A3,1E9.2)') ZRELATIVE_ERROR, ' > ', 2000.0*EPSILON(1.0_JPRB)
    WRITE(NERR, '(A)') '*******************************'
    FLUSH(NERR)
    CALL TRANS_END
    CALL ABORT_TRANS("Adjoint test failed")
  ENDIF
ENDIF

CALL TRANS_END

IF (LUSE_MPI) THEN
  CALL MPL_BARRIER()
  CALL MPL_END
ENDIF

CONTAINS

SUBROUTINE SCALPRODSP(PSP1, PSP2, PSC, KVSET)
  ! Scalar product in spectral space
  REAL(KIND=JPRB), INTENT(IN) :: PSP1(:,:), PSP2(:,:)
  REAL(KIND=JPRB), INTENT(OUT) :: PSC
  INTEGER(KIND=JPIM), INTENT(IN) :: KVSET(:)

  INTEGER(KIND=JPIM) :: JMLOC, IM, JIR, JN, INM, JLEV
  REAL(KIND=JPRB) :: ZMFACT, ZSP(NFLEV,NSPEC2), ZSPG(NFLEVG,NSPEC2G)

  PSC = 0.0_JPRB
  ZSP(:,:) = 0.0_JPRB

  !$OMP PARALLEL DO SCHEDULE(STATIC,1) PRIVATE(JLEV,JMLOC,IM,ZMFACT,JIR,JN,INM)
  DO JLEV=1,NFLEV
    DO JMLOC=1,NUMP
      IM = MYMS(JMLOC)
      ZMFACT=1.0_JPRB+REAL(MIN(1,IM),JPRB)
      DO JIR=0,MIN(1,IM)
        DO JN=IM,NSMAX
          INM = NASM0(IM)+(JN-IM)*2+JIR
          ZSP(JLEV,INM) = PSP1(JLEV,INM)*PSP2(JLEV,INM)*ZMFACT/2.0_JPRB
        ENDDO
      ENDDO
    ENDDO
  ENDDO
  !$OMP END PARALLEL DO

  CALL GATH_SPEC(PSPECG=ZSPG, KFGATHG=NFLEVG, KTO=(/ (1, I = 1, NFLEVG) /), PSPEC=ZSP, KVSET=KVSET)

  IF (MYPROC == 1) THEN
    PSC = SUM(ZSPG)
  ELSE
    PSC = 0.0_JPRB
  ENDIF
END SUBROUTINE SCALPRODSP

FUNCTION DETECT_MPIRUN() RESULT(LMPI_REQUIRED)
  USE EC_ENV_MOD, ONLY : EC_PUTENV
  LOGICAL :: LMPI_REQUIRED
  INTEGER :: ILEN
  INTEGER, PARAMETER :: NVARS = 4
  CHARACTER(LEN=32), DIMENSION(NVARS) :: CMPIRUN_DETECT
  CHARACTER(LEN=4) :: CLENV
  INTEGER :: IVAR

  ! Environment variables that are set when mpirun, srun, aprun, ... are used
  CMPIRUN_DETECT(1) = 'OMPI_COMM_WORLD_SIZE'  ! OPENMPI
  CMPIRUN_DETECT(2) = 'ALPS_APP_PE'           ! CRAY PE
  CMPIRUN_DETECT(3) = 'PMI_SIZE'              ! INTEL
  CMPIRUN_DETECT(4) = 'SLURM_NTASKS'          ! SLURM

  LMPI_REQUIRED = .FALSE.
  DO IVAR = 1, NVARS
    CALL GET_ENVIRONMENT_VARIABLE(NAME=TRIM(CMPIRUN_DETECT(IVAR)), LENGTH=ILEN)
    IF (ILEN > 0) THEN
      LMPI_REQUIRED = .TRUE.
      EXIT ! Break
    ENDIF
  ENDDO

  CALL GET_ENVIRONMENT_VARIABLE(NAME="ECTRANS_USE_MPI", VALUE=CLENV, LENGTH=ILEN )
  IF (ILEN > 0) THEN
      LMPI_REQUIRED = .TRUE.
      IF( TRIM(CLENV) == "0" .OR. TRIM(CLENV) == "OFF" .OR. TRIM(CLENV) == "OFF" .OR. TRIM(CLENV) == "F" ) THEN
        LMPI_REQUIRED = .FALSE.
      ENDIF
      CALL EC_PUTENV("DR_HOOK_ASSERT_MPI_INITIALIZED=0", OVERWRITE=.TRUE.)
  ENDIF
END FUNCTION

END PROGRAM TEST_ADJOINT