1module calcStatsGlb_mod
2 ! MODULE calcStatsGlb_mod (prefix='csg' category='1. High-level functionality')
3 !
4 !:Purpose: To compute homogeneous and isotropic background error covariances
5 ! from forecast error estimate in model variable space (global
6 ! version).
7 !
8 use codePrecision_mod
9 use midasMpi_mod
10 use gridStateVector_mod
11 use gridStateVectorFileIO_mod
12 use ensembleStateVector_mod
13 use globalSpectralTransform_mod
14 use mathPhysConstants_mod
15 use horizontalCoord_mod
16 use verticalCoord_mod
17 use varNameList_mod
18 use calcHeightAndPressure_mod
19 use earthConstants_mod
20 use utilities_mod
21 use spectralFilter_mod
22 use menetrierDiag_mod
23 use fileNames_mod
24 use timeCoord_mod
25 use gridVariableTransforms_mod
26 use gridBinning_mod
27 use verticalModes_mod
28 implicit none
29 save
30 private
31
32 ! Public Subroutines
33 public :: csg_setup, csg_computeBhi, csg_toolbox
34
35 type(struct_hco), pointer :: hco_ens ! Ensemble horizontal grid parameters
36 type(struct_vco), pointer :: vco_ens ! Ensemble horizontal grid parameters
37
38 integer :: nens,ni,nj,nLevEns_M,nLevEns_T,nLevPtoT,nkgdimEns,varLevOffset(6),nla
39 integer :: myLatBeg, myLatEnd, myLonBeg, myLonEnd, latPerPE, latPerPEmax, lonPerPE, lonPerPEmax
40 integer :: mymBeg, mymEnd, mymSkip, mymCount, mynBeg, mynEnd, mynSkip, mynCount
41 integer :: myMemberBeg, myMemberEnd, myMemberCount, maxMyMemberCount
42 integer :: nEnsOverDimension
43 integer :: nla_mpilocal, maxMyNla
44 integer, pointer :: ilaList_mpiglobal(:), ilaList_mpilocal(:)
45
46 character(len=256), allocatable :: cflensin(:)
47 integer :: gstID_nkgdimEns, gstID_nLevEns_M, gstID_nLevEns_T_P1
48 integer, allocatable :: nip1_M(:),nip1_T(:)
49 real(8), pointer :: pressureProfile_M(:), pressureProfile_T(:)
50 integer,external :: get_max_rss
51
52 integer,parameter :: nvar3d=4, nvar2d=1, nvar=nvar3d+nvar2d
53 character(len=4) :: nomvar3d(nvar3d,3), nomvar2d(nvar2d,3), nomvar(nvar,3)
54 integer, parameter :: modelSpace = 1
55 integer, parameter :: cvSpace = 2
56 integer, parameter :: cvUnbalSpace = 3
57
58 real(8) :: gridSpacingInKm
59
60 ! For wave band decomposition
61 integer, parameter :: maxNumLocalLength = 20
62 integer :: nWaveBand
63 integer :: nVertWaveBand
64
65 logical :: initialized = .false.
66
67 ! Namelist variables
68 integer :: ntrunc
69 integer :: waveBandPeaks(maxNumLocalLength) ! For horizontal wave band decomposition
70 integer :: vertWaveBandPeaks(maxNumLocalLength) ! For vertical wave band decomposition
71
72 contains
73
74 !--------------------------------------------------------------------------
75 ! CSG_SETUP
76 !--------------------------------------------------------------------------
77 subroutine csg_setup(nens_in, hco_in, vco_in)
78 !
79 !:Purpose: Main setup routine for this module
80 !
81 implicit none
82
83 ! Arguments:
84 integer, intent(in) :: nens_in
85 type(struct_vco), pointer, intent(in) :: vco_in
86 type(struct_hco), pointer, intent(in) :: hco_in
87
88 ! Locals:
89 integer :: nulnam, ierr, waveBandIndex, memberIndex
90 integer :: fclos, fnom
91 real(8) :: zps
92
93 NAMELIST /NAMCALCSTATS_GLB/ntrunc,waveBandPeaks,vertWaveBandPeaks
94
95 write(*,*)
96 write(*,*) 'csg_setup: Starting...'
97
98 nens=nens_in
99 allocate(cflensin(nens))
100 do memberIndex = 1, nEns
101 call fln_ensfileName(cflensin(memberIndex), 'ensemble', memberIndex_opt=memberIndex)
102 end do
103
104 if ( mmpi_myid == 0 ) then
105 call mpc_printConstants(6)
106 call pre_printPrecisions
107 end if
108
109 ! parameters from namelist (date in filename should come directly from sequencer?)
110 ntrunc=108
111 waveBandPeaks(:) = -1.0d0
112 vertWaveBandPeaks(:) = -1.0d0
113
114 nulnam=0
115 ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
116 read(nulnam,nml=NAMCALCSTATS_GLB)
117 if (ierr /= 0) call utl_abort('csg_setup: Error reading namelist NAMCALCSTATS_GLB')
118 if (mmpi_myid == 0) write(*,nml=NAMCALCSTATS_GLB)
119 ierr=fclos(nulnam)
120
121 !- Setup horizontal grid
122 hco_ens => hco_in
123 ni=hco_in%ni
124 nj=hco_in%nj
125
126 gridSpacingInKm = ec_ra * hco_in%dlon / 1000.d0
127 write(*,*) 'Grid Spacing in Km = ', gridSpacingInKm
128
129 ! setup mpi local grid parameters
130 call mmpi_setup_latbands(nj, latPerPE, latPerPEmax, myLatBeg, myLatEnd)
131 call mmpi_setup_lonbands(ni, lonPerPE, lonPerPEmax, myLonBeg, myLonEnd)
132
133 !- Setup vertical levels
134 vco_ens => vco_in
135 nLevEns_M=vco_in%nlev_M
136 nLevEns_T=vco_in%nlev_T
137 nLevPtot=nLevEns_M-1 ! ignore streamfunction at hyb=1, since highly correlated with next level
138 varLevOffset(1) = 0
139 varLevOffset(2) = 1*nLevEns_M
140 varLevOffset(3) = 2*nLevEns_M
141 varLevOffset(4) = 2*nLevEns_M+1*nLevEns_T
142 varLevOffset(5) = 2*nLevEns_M+2*nLevEns_T
143 nkgdimEns=nLevEns_M*2+nLevEns_T*2+1 ! NO TG !!!
144 nla=(ntrunc+1)*(ntrunc+2)/2
145
146 !- Setup the global spectral transform
147 gstID_nkgdimEns = gst_setup(ni,nj,ntrunc,nkgdimEns)
148 gstID_nLevEns_M = gst_setup(ni,nj,ntrunc,nLevEns_M)
149 gstID_nLevEns_T_P1 = gst_setup(ni,nj,ntrunc,nLevEns_T+1)
150
151 ! setup mpi local spectral vector parameters
152 call mmpi_setup_m(ntrunc, mymBeg, mymEnd, mymSkip, mymCount)
153 call mmpi_setup_n(ntrunc, mynBeg, mynEnd, mynSkip, mynCount)
154 call gst_ilaList_mpiglobal(ilaList_mpiglobal, nla_mpilocal, maxMyNla, &
155 gstID_nkgdimEns, mymBeg, mymEnd, mymSkip, mynBeg, mynEnd, mynSkip)
156 call gst_ilaList_mpilocal(ilaList_mpilocal, &
157 gstID_nkgdimEns, mymBeg, mymEnd, mymSkip, mynBeg, mynEnd, mynSkip)
158
159 ! setup ensemble members mpi partinionning (when working with struct_ens)
160 call mmpi_setup_levels(nEns,myMemberBeg,myMemberEnd,myMemberCount)
161 call rpn_comm_allreduce(myMemberCount, maxMyMemberCount, &
162 1,"MPI_INTEGER","MPI_MAX","GRID",ierr)
163 nEnsOverDimension = mmpi_npex * maxMyMemberCount
164
165 !- Setup ip1s
166 allocate(nip1_M(nLevEns_M))
167 nip1_M(:)=vco_in%ip1_M(:)
168 allocate(nip1_T(nLevEns_T))
169 nip1_T(:)=vco_in%ip1_T(:)
170
171 !- Estimate the pressure profile for each vertical grid
172 zps = 101000.D0
173 call czp_fetch1DLevels(vco_in, zps, &
174 profM_opt=pressureProfile_M, profT_opt=pressureProfile_T)
175
176 !
177 !- Setup variable names
178 !
179 nomvar3d(1,modelSpace)='UU'
180 nomvar3d(2,modelSpace)='VV'
181 nomvar3d(3,modelSpace)='TT'
182 nomvar3d(4,modelSpace)='LQ'
183 nomvar2d(1,modelSpace)='P0'
184
185 nomvar3d(1,cvSpace)='PP'
186 nomvar3d(2,cvSpace)='CC'
187 nomvar3d(3,cvSpace)='TT'
188 nomvar3d(4,cvSpace)='LQ'
189 nomvar2d(1,cvSpace)='P0'
190
191 nomvar3d(1,cvUnbalSpace)='PP'
192 nomvar3d(2,cvUnbalSpace)='UC'
193 nomvar3d(3,cvUnbalSpace)='UT'
194 nomvar3d(4,cvUnbalSpace)='LQ'
195 nomvar2d(1,cvUnbalSpace)='UP'
196
197 nomvar(1:4,:) = nomvar3d(1:4,:)
198 nomvar(5,:) = nomvar2d(1,:)
199
200 !
201 !- Horizontal wave band decomposition option
202 !
203 nWaveBand = count(waveBandPeaks >= 0)
204 if ( nWaveBand < 1 ) then
205 nWaveBand = 1
206 else if (nWaveBand == 1) then
207 write(*,*) 'You have specified only ONE waveBandPeaks'
208 call utl_abort('calbmatrix_glb')
209 else
210 write(*,*)
211 write(*,*) 'Horizontal waveBand decomposition is ACTIVATED'
212 end if
213
214 ! Make sure that the wavenumbers are in the correct (decreasing) order
215 do waveBandIndex = 1, nWaveBand-1
216 if ( waveBandPeaks(waveBandIndex)-waveBandPeaks(waveBandIndex+1) <= 0 ) then
217 write(*,*) 'csg_setup: waveBandPeaks are not in decreasing wavenumber order'
218 call utl_abort('calbmatrix_glb')
219 end if
220 end do
221
222 ! Make sure the truncation is compatible with the waveBandPeaks
223 if ( ntrunc < nj-1 .and. nWaveBand > 1 ) then
224 write(*,*) 'csg_setup: The truncation is not compatible with wave band decomposition'
225 write(*,*) ' ntrunc should = ', nj-1
226 call utl_abort('calbmatrix_glb')
227 end if
228
229 !
230 !- Vertical wave band decomposition option
231 !
232 nVertWaveBand = count(vertWaveBandPeaks >= 0)
233 if ( nVertWaveBand < 1 ) then
234 nVertWaveBand = 1
235 else if (nVertWaveBand == 1) then
236 write(*,*) 'You have specified only ONE waveBandPeaks'
237 call utl_abort('calbmatrix_glb')
238 else
239 write(*,*)
240 write(*,*) 'Vertical waveBand decomposition is ACTIVATED'
241 end if
242
243 ! Make sure that the wavenumbers are in the correct (decreasing) order
244 do waveBandIndex = 1, nVertWaveBand-1
245 if ( vertWaveBandPeaks(waveBandIndex)-vertWaveBandPeaks(waveBandIndex+1) <= 0 ) then
246 write(*,*) 'csg_setup: vertWaveBandPeaks are not in decreasing mode order'
247 call utl_abort('calbmatrix_glb')
248 end if
249 end do
250
251 !
252 !- Ending
253 !
254 initialized = .true.
255
256 write(*,*)
257 write(*,*) 'csg_setup: Done!'
258
259 end subroutine csg_setup
260
261 !--------------------------------------------------------------------------
262 ! csg_computeBhi
263 !--------------------------------------------------------------------------
264 subroutine csg_computeBhi()
265 !
266 !:Purpose: Master routine for Bhi computation in global mode
267 !
268 implicit none
269
270 ! Locals:
271 integer :: ierr, nulnam
272 integer :: fclos, fnom
273 character(len=12) :: formulation ! Bhi formulation
274
275 NAMELIST /NAMCOMPUTEBHI/formulation
276
277 ! parameters from namelist
278 formulation='legacy'
279
280 nulnam=0
281 ierr=fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
282 read(nulnam,nml=NAMCOMPUTEBHI)
283 if (ierr /= 0) call utl_abort('csg_computeBhi: Error reading namelist NAMCOMPUTEBHI')
284 if (mmpi_myid == 0) write(*,nml=NAMCOMPUTEBHI)
285 ierr=fclos(nulnam)
286
287 select case(trim(formulation))
288 case ('legacy')
289 call csg_computeBhiLegacy
290 case ('latbands')
291 call csg_computeBhiLatBands
292 case default
293 write(*,*)
294 write(*,*) 'csg_computeBhi: Unknown value of FORMULATION for Bhi computation: ',formulation
295 write(*,*) 'Please select legacy or latbands'
296 call utl_abort('csg_computeBhi')
297 end select
298
299 end subroutine csg_computeBhi
300
301 !--------------------------------------------------------------------------
302 ! csg_computeBhiLegacy
303 !--------------------------------------------------------------------------
304 subroutine csg_computeBhiLegacy
305 !
306 !:Purpose: Computation of Bhi using the legacy formulation
307 !
308 implicit none
309
310 ! Locals:
311 real(4), pointer :: ensPerturbations(:,:,:,:), ens_ptr(:,:,:,:)
312 real(4), pointer :: ensBalPerturbations(:,:,:,:)
313 real(8), pointer :: stddev3d(:,:,:), stddev3dBal(:,:,:), stddev3dUnbal(:,:,:)
314 real(8), pointer :: stddev3d_ptr(:,:,:)
315 real(8), pointer :: stddevZonAvg(:,:), stddevZonAvgBal(:,:), stddevZonAvgUnbal(:,:)
316 real(8), allocatable :: PtoT(:,:,:),theta1(:,:),theta2(:,:)
317 real(8), allocatable :: corns(:,:,:),rstddev(:,:)
318 integer :: variableType
319
320 allocate(ensPerturbations(myLonBeg:myLonEnd, myLatBeg:myLatEnd, nkgdimEns, nens))
321 allocate(ensBalPerturbations(myLonBeg:myLonEnd, myLatBeg:myLatEnd, nLevEns_T+1, nens))
322 allocate(theta1(nlevEns_M,nj))
323 allocate(stddev3d(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nkgdimEns))
324 allocate(stddevZonAvg(nkgdimEns,nj))
325 allocate(PtoT(nlevEns_T+1,nlevEns_M,nj))
326 allocate(theta2(nlevEns_M,nj))
327 allocate(stddev3dBal(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nLevEns_T+1))
328 allocate(stddev3dUnbal(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nkgdimEns))
329 allocate(stddevZonAvgBal(nLevEns_T+1,nj))
330 allocate(stddevZonAvgUnbal(nkgdimEns,nj))
331 allocate(corns(nkgdimEns,nkgdimEns,0:ntrunc))
332 allocate(rstddev(nkgdimEns,0:ntrunc))
333
334 write(*,*) 'Initializing ensemble arrays to claim memory'
335 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
336 ensPerturbations(:,:,:,:) = 0.0
337 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
338 ensBalPerturbations(:,:,:,:) = 0.0
339 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
340
341 call readEnsemble(ensPerturbations)
342 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
343
344 call removeMean(ensPerturbations)
345
346 call uv_to_psichi(ensPerturbations)
347
348 call calcStddev3d(ensPerturbations,stddev3d,nkgdimens)
349
350 call calcZonAvg(stddevZonAvg,stddev3d,nkgdimens)
351
352 call calcTheta(ensPerturbations,theta1) ! theta1 is put in glbcov and used for analysis!
353 if (mmpi_myid == 0) write(301,*) theta1
354
355 call removeBalancedChi(ensPerturbations,theta1)
356
357 call normalize3d(ensPerturbations,stddev3d)
358
359 call calcPtoT(ensPerturbations,PtoT)
360 if (mmpi_myid == 0) write(303,*) PTOT(:,:,1)
361 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
362
363! call calcTheta(ensPerturbations,theta2) ! theta2 is used previously for computing unbalanced Chi!
364! if (mmpi_myid == 0) write(302,*) theta2
365
366 call removeBalancedT_Ps(ensPerturbations,ensBalPerturbations,PtoT)
367 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
368
369! call removeBalancedChi(ensPerturbations,theta2)
370
371 call multiply3d(ensPerturbations,stddev3d,nkgdimens)
372
373 ens_ptr(myLonBeg:,myLatBeg:,1:,1:) => ensBalPerturbations(:,:,1:nLevEns_T,:)
374 stddev3d_ptr(myLonBeg:,myLatBeg:,1:) => stddev3d(:,:,(2*nLevEns_M+1):(2*nLevEns_M+nLevEns_T))
375 call multiply3d(ens_ptr, stddev3d_ptr, nLevEns_T)
376
377 ens_ptr(myLonBeg:,myLatBeg:,1:,1:) => ensBalPerturbations(:,:,(nLevEns_T+1):(nLevEns_T+1),:)
378 stddev3d_ptr(myLonBeg:,myLatBeg:,1:) => stddev3d(:,:,(2*nLevEns_M+2*nLevEns_T+1):(2*nLevEns_M+2*nLevEns_T+1))
379 call multiply3d(ens_ptr, stddev3d_ptr, 1)
380
381 call spectralFilter(ensPerturbations,nkgdimens)
382
383 call spectralFilter(ensBalPerturbations,nLevEns_T+1)
384
385 call calcStddev3d(ensPerturbations,stddev3dUnbal,nkgdimens)
386
387 call calcStddev3d(ensBalPerturbations,stddev3dBal,nLevEns_T+1)
388
389 call calcZonAvg(stddevZonAvgUnbal,stddev3dUnbal,nkgdimens)
390
391 call calcZonAvg(stddevZonAvgBal,stddev3dBal,nLevEns_T+1)
392
393 call normalize3d(ensPerturbations,stddev3dUnbal)
394
395 call removeGlobalMean(ensPerturbations)
396
397 call calcCorrelations(ensPerturbations,corns,rstddev)
398
399 variableType = cvUnbalSpace
400 call writeStats(corns,rstddev,ptot,theta1)
401
402 call writeStddev(stddevZonAvg,stddev3d,stddevZonAvgUnbal,stddev3dUnbal)
403
404 call writeStddevBal(stddevZonAvgBal,stddev3dBal)
405
406 call writeSpStats(ptot,theta1)
407 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
408
409 if (mmpi_myid == 0) then
410 write(200,*) stddevZonAvg(1:nlevEns_M,:)
411 write(201,*) stddevZonAvg((1+1*nlevEns_M):(2*nlevEns_M),:)
412 write(202,*) stddevZonAvg((1+2*nlevEns_M):(3*nlevEns_T),:)
413 write(203,*) stddevZonAvg((1+2*nlevEns_M+1*nlevEns_T):(2*nlevEns_M+2*nlevEns_T),:)
414 write(204,*) stddevZonAvg((1+2*nlevEns_M+2*nlevEns_T),:)/1.0d2
415
416 write(400,*) stddevZonAvgUnbal(1:nlevEns_M,:)
417 write(401,*) stddevZonAvgUnbal((1+1*nlevEns_M):(2*nlevEns_M),:)
418 write(402,*) stddevZonAvgUnbal((1+2*nlevEns_M):(3*nlevEns_T),:)
419 write(403,*) stddevZonAvgUnbal((1+2*nlevEns_M+1*nlevEns_T):(2*nlevEns_M+2*nlevEns_T),:)
420 write(404,*) stddevZonAvgUnbal((1+2*nlevEns_M+2*nlevEns_T),:)/1.0d2
421 end if
422
423 end subroutine csg_computeBhiLegacy
424
425 !--------------------------------------------------------------------------
426 ! csg_computeBhiLatBands
427 !--------------------------------------------------------------------------
428 subroutine csg_computeBhiLatBands
429 !
430 !:Purpose: Computation of Bhi on a set of latitude bands
431 !
432 implicit none
433
434 ! Locals:
435 integer :: variableType, latIndex, jlatband, lat1, lat2, lat3
436 real(4), pointer :: ensPerturbations(:,:,:,:)
437 real(8), pointer :: stddev3d(:,:,:)
438 real(8), pointer :: stddevZonAvg(:,:)
439 real(8), allocatable :: corns(:,:,:),rstddev(:,:)
440 real(8) :: latMask(nj)
441
442 allocate(ensPerturbations(ni,nj,nkgdimEns,nens))
443 allocate(stddev3d(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nkgdimEns))
444 allocate(stddevZonAvg(nkgdimEns,nj))
445 allocate(corns(nkgdimEns,nkgdimEns,0:ntrunc))
446 allocate(rstddev(nkgdimEns,0:ntrunc))
447
448 call readEnsemble(ensPerturbations)
449 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
450
451 call removeMean(ensPerturbations)
452
453 call uv_to_psichi(ensPerturbations)
454
455 call calcStddev3d(ensPerturbations,stddev3d,nkgdimens)
456
457 call calcZonAvg(stddevZonAvg,stddev3d,nkgdimens)
458
459 call normalize3d(ensPerturbations,stddev3d)
460
461 call removeGlobalMean(ensPerturbations)
462
463 do jlatband = 1, 3
464 write(*,*) 'csg_computeBhiLatBands: selected LATBAND = ',jlatband
465 lat1=nj/4
466 lat2=nj/2
467 lat3=3*nj/4
468 write(*,*) 'lat1,2,3=',lat1,lat2,lat3
469 if(jlatband==1) then
470 ! Southern extratropics
471 latMask(1:lat1) = 1.0d0
472 do latIndex = lat1, lat2
473 !latMask(latIndex) = sqrt(dble((lat2-latIndex)*4)/dble(nj))
474 latMask(latIndex) = sqrt(0.5d0*(1.0d0+cos(dble((latIndex-lat1)*4)*MPC_PI_R8/dble(nj))))
475 end do
476 latMask(lat2:nj) = 0.0d0
477 else if(jlatband==2) then
478 ! Tropics
479 !latMask(1:lat1) = 0.0d0
480 !do latIndex = lat1, lat2
481 ! latMask(latIndex) = sqrt(dble((latIndex-lat1)*4)/dble(nj))
482 !end do
483 !do latIndex = lat2,lat3
484 ! latMask(latIndex) = sqrt(dble((lat3-latIndex)*4)/dble(nj))
485 !end do
486 !latMask(lat3:nj) = 0.0d0
487
488 ! NOTE: use much broader band for tropics to avoid shortening horizontal correlations
489 ! ok, since the masks do not have to sum to one for calculation, but they do
490 ! when used in bmatrixhi_mod
491 ! Tropics
492 do latIndex = 1, lat1
493 !latMask(latIndex) = sqrt(dble((latIndex-1)*4)/dble(nj))
494 latMask(latIndex) = sqrt(0.5d0*(1.0d0+cos(dble((lat1-latIndex)*4)*MPC_PI_R8/dble(nj))))
495 end do
496 latMask(lat1:lat3) = 1.0d0
497 do latIndex = lat3,nj
498 !latMask(latIndex) = sqrt(dble((nj-latIndex)*4)/dble(nj))
499 latMask(latIndex) = sqrt(0.5d0*(1.0d0+cos(dble((latIndex-lat3)*4)*MPC_PI_R8/dble(nj))))
500 end do
501 else if(jlatband==3) then
502 ! Northern extratropics
503 latMask(1:lat2) = 0.0d0
504 do latIndex = lat2, lat3
505 !latMask(latIndex) = sqrt(dble((latIndex-lat2)*4)/dble(nj))
506 latMask(latIndex) = sqrt(0.5d0*(1.0d0+cos(dble((lat3-latIndex)*4)*MPC_PI_R8/dble(nj))))
507 end do
508 latMask(lat3:nj) = 1.0d0
509 end if
510 write(*,*) 'latMask = ',latMask(:)
511 call calcCorrelations(ensPerturbations,corns,rstddev,latMask_opt=latMask)
512
513 variableType = cvSpace
514 call writeStats(corns,rstddev,latBand_opt=jlatBand)
515 end do
516
517 call writeStddev(stddevZonAvg,stddev3d)
518
519 if (mmpi_myid == 0) then
520 write(200,*) stddevZonAvg(1:nlevEns_M,:)
521 write(201,*) stddevZonAvg((1+1*nlevEns_M):(2*nlevEns_M),:)
522 write(202,*) stddevZonAvg((1+2*nlevEns_M):(3*nlevEns_T),:)
523 write(203,*) stddevZonAvg((1+2*nlevEns_M+1*nlevEns_T):(2*nlevEns_M+2*nlevEns_T),:)
524 write(204,*) stddevZonAvg((1+2*nlevEns_M+2*nlevEns_T),:)/1.0d2
525 end if
526
527 end subroutine csg_computeBhiLatBands
528
529 !--------------------------------------------------------------------------
530 ! CSG_TOOLBOOX
531 !--------------------------------------------------------------------------
532 subroutine csg_toolbox
533 !
534 !:Purpose: High-level routine to do a variety of diagnostic operations
535 ! on the ensemble of error samples
536 !
537 implicit none
538
539 ! NOTE: The diagnostic computed here are in model variable space
540 ! (no variable transform!!!)
541
542 ! Locals:
543 integer :: waveBandIndex
544 integer :: nulnam, ierr, fclos, fnom, numStep
545 real(8), allocatable :: corns(:,:,:), rstddev(:,:), powerSpec(:,:)
546 integer, allocatable :: dateStampList(:)
547 type(struct_ens), target :: ensPerts, ensPertsFilt
548 type(struct_ens), pointer :: ensPerts_ptr
549 type(struct_gsv) :: statevector_template
550 type(struct_gbi) :: gbi_zonalMean
551 type(struct_gbi) :: gbi_globalMean
552 type(struct_vms) :: vModes
553 integer :: variableType
554 logical :: ensContainsFullField
555 logical :: makeBiPeriodic
556 logical :: doSpectralFilter
557 real(8) :: vertModesLengthScale(2)
558 real(8) :: lengthScaleTop, lengthScaleBot
559 character(len=60) :: tool
560 character(len=2) :: wbnum
561 character(len=2) :: ctrlVarHumidity
562
563 NAMELIST /NAMTOOLBOX/tool, ensContainsFullField, doSpectralFilter, vertModesLengthScale, &
564 ctrlVarHumidity
565
566 write(*,*)
567 write(*,*) 'csg_toolbox'
568 write(*,*)
569
570 !
571 !- Set options
572 !
573 variableType = modelSpace ! hardwired
574 ensContainsFullField = .true. ! default value
575 doSpectralFilter = .true.
576 vertModesLengthScale(1) = 6.d0
577 vertModesLengthScale(2) = -1.d0
578 ctrlVarHumidity = 'HU'
579
580 nulnam = 0
581 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
582 read(nulnam,nml=NAMTOOLBOX)
583 if (ierr /= 0) call utl_abort('csg_toolbox: Error reading namelist NAMTOOLBOX')
584 if (mmpi_myid == 0) write(*,nml=NAMTOOLBOX)
585 ierr = fclos(nulnam)
586
587 lengthScaleTop = vertModesLengthScale(1)
588 if (vertModesLengthScale(2) == -1.d0) then
589 lengthScaleBot = lengthScaleTop
590 else
591 lengthScaleBot = vertModesLengthScale(2)
592 end if
593
594 !
595 !- Read ensemble
596 !
597 numStep = 1
598 allocate(dateStampList(numStep))
599 dateStampList(:) = -1
600 call ens_allocate(ensPerts, nEns, numStep, hco_ens, vco_ens, dateStampList)
601
602 if (nWaveBand > 1 .or. nVertWaveBand > 1) then
603 call ens_allocate(ensPertsFilt, nEns, numStep, hco_ens, vco_ens, dateStampList)
604 ensPerts_ptr => ensPertsFilt
605 else
606 ensPerts_ptr => ensPerts
607 end if
608
609 makeBiPeriodic = .false.
610 call ens_readEnsemble(ensPerts, './ensemble', makeBiPeriodic, &
611 containsFullField_opt=ensContainsFullField)
612
613 if ( ctrlVarHumidity == 'LQ' .and. ensContainsFullField ) then
614 call gvt_transform(ensPerts,'HUtoLQ')
615 call ens_modifyVarName(ensPerts, 'HU', 'LQ')
616 if (nWaveBand > 1 .or. nVertWaveBand > 1) then
617 call ens_modifyVarName(ensPerts_ptr, 'HU', 'LQ')
618 end if
619 end if
620
621 !
622 !- Compute and remove the ensemble mean; compute the stdDev
623 !
624 call ens_computeMean(ensPerts)
625 call ens_removeMean (ensPerts)
626
627 !
628 !- Tool selection
629 !
630 select case(trim(tool))
631 case ('HVCORREL_HI')
632 write(*,*)
633 write(*,*) 'Computing Homogeneous and Isotropic Correlation'
634
635 if (mmpi_nprocs > 1) then
636 call utl_abort('csg_toolbox: this tool is not yet MPI capable') ! only due to horizCorrelFunction
637 end if
638
639 call spectralFilter2(ensPerts, & ! IN
640 ensPerts_ptr, & ! OUT
641 waveBandIndex_opt=1) ! IN
642 call ens_computeStdDev(ensPerts_ptr)
643 call ens_normalize(ensPerts_ptr)
644 call ens_removeGlobalMean(ensPerts_ptr)
645
646 allocate(corns(nkgdimEns,nkgdimEns,0:ntrunc))
647 allocate(rstddev(nkgdimEns,0:ntrunc))
648
649 call calcCorrelations2(ensPerts_ptr, & ! IN
650 corns, & ! OUT (vertical correlation in spectral space)
651 rstddev) ! OUT ( sqrt(normalized power spectrum) )
652
653 call writeStats(corns,rstddev,waveBandIndex_opt=1) ! IN
654 call calcHorizScale(rstddev,variableType,waveBandIndex_opt=1) ! IN
655 call horizCorrelFunction(rstddev,variableType,waveBandIndex_opt=1) ! IN
656
657 deallocate(rstddev)
658 deallocate(corns)
659
660 case ('HVCORREL_LOCAL')
661 write(*,*)
662 write(*,*) 'Computing Local Correlation'
663
664 call ens_removeGlobalMean(ensPerts)
665 call spectralFilter2(ensPerts, & ! IN
666 ensPerts_ptr, & ! OUT
667 waveBandIndex_opt=1) ! IN
668 call ens_computeStdDev(ensPerts_ptr)
669 call ens_normalize(ensPerts_ptr)
670 call calcLocalCorrelations(ensPerts_ptr) ! IN
671
672 case ('VCORRMATRIX')
673 write(*,*)
674 write(*,*) 'Computing Local Correlation'
675
676 if (doSpectralFilter) then
677 call ens_removeGlobalMean(ensPerts)
678 call spectralFilter2(ensPerts, & ! IN
679 ensPerts_ptr, & ! OUT
680 waveBandIndex_opt=1) ! IN
681 end if
682 call ens_computeStdDev(ensPerts_ptr)
683 call ens_normalize(ensPerts_ptr)
684 call calcLocalVertCorrMatrix(ensPerts_ptr) ! IN
685
686 case ('LOCALIZATIONRADII')
687 write(*,*)
688 write(*,*) 'Estimating the optimal covariance localization radii'
689
690 if (nWaveBand > 1 .and. nVertWaveBand > 1) then
691 call utl_abort('csg_toolbox: cannot do horizontal AND vertical scale-decomposition')
692 end if
693
694 call ens_removeGlobalMean(ensPerts)
695
696 do waveBandIndex = 1, max(nWaveBand,nVertWaveBand)
697
698 if (nVertWaveBand > 1) then
699 if (waveBandIndex == 1) then
700 call vms_computeModesFromFunction(vco_ens, lengthScaleTop, lengthScaleBot, & ! IN
701 vModes) ! OUT
702 end if
703 call vertModesFilter(ensPerts, & ! IN
704 ensPerts_ptr, & ! OUT
705 vModes, waveBandIndex) ! IN
706 else
707 call spectralFilter2(ensPerts, & ! IN
708 ensPerts_ptr, & ! OUT
709 waveBandIndex_opt=waveBandIndex) ! IN
710 end if
711
712 call ens_computeStdDev(ensPerts_ptr)
713
714 if (waveBandIndex == 1) then
715 call ens_copyEnsStdDev(ensPerts_ptr, statevector_template) ! IN
716 call bmd_setup(statevector_template, hco_ens, nEns, pressureProfile_M, & ! IN
717 pressureProfile_T, max(nWaveBand,nVertWaveBand)) ! IN
718 end if
719
720 call bmd_localizationRadii(ensPerts_ptr, waveBandIndex_opt=waveBandIndex) ! IN
721
722 end do
723
724 case ('STDDEV')
725 write(*,*)
726 write(*,*) 'Computing standard deviations using PSI/CHI for winds'
727
728 ! u,v to psi,chi
729 call gvt_setup(hco_ens, hco_ens, vco_ens)
730 call gvt_transform(ensPerts_ptr, 'UVtoPsiChi')
731 if (.not. ens_varExist(ensPerts_ptr,'PP') .and. &
732 .not. ens_varExist(ensPerts_ptr,'CC') ) then
733 call ens_modifyVarName(ensPerts_ptr, 'UU', 'PP')
734 call ens_modifyVarName(ensPerts_ptr, 'VV', 'CC')
735 end if
736
737 ! Compute the grid point std dev
738 call ens_computeStdDev(ensPerts_ptr)
739 call ens_copyEnsStdDev(ensPerts_ptr, statevector_template) ! IN
740 call gio_writeToFile(statevector_template, './stddev.fst', 'STDDEV_GRIDP', &
741 typvar_opt = 'E', numBits_opt = 32)
742
743 ! Compute the zonal std dev
744 call gbi_setup(gbi_zonalMean, 'YrowBand', statevector_template, hco_ens)
745 call gbi_stdDev(gbi_zonalMean, ensPerts_ptr, & ! IN
746 statevector_template) ! OUT
747 call gio_writeToFile(statevector_template, './stddev.fst', 'STDDEV_ZONAL', &
748 typvar_opt = 'E', numBits_opt = 32)
749
750 case ('POWERSPEC')
751 write(*,*)
752 write(*,*) 'Computing power spectra'
753
754 call calcPowerSpec(ensPerts_ptr, & ! IN
755 powerSpec) ! OUT
756 call writePowerSpec(powerSpec, modelSpace) ! IN
757
758 case ('VERTMODES_SPEC')
759 write(*,*)
760 write(*,*) 'Computing vertical modes spectra'
761
762 if (nWaveBand > 1 .or. nVertWaveBand > 1) then
763 call utl_abort('csg_toolbox: waveband decomposition cannot be use when TOOLBOX=VERTMODES_SPEC')
764 end if
765
766 call vms_computeModesFromFunction(vco_ens, lengthScaleTop, lengthScaleBot, & ! IN
767 vModes) ! OUT
768 call vms_writeModes(vModes)
769
770 call calcVertModesSpec(ensPerts_ptr,vModes) ! IN
771
772 case ('VERTMODES_WAVEBAND')
773 write(*,*)
774 write(*,*) 'Computing vertical-scale decomposed ensemble perturbations'
775
776 if (nVertWaveBand == 1) then
777 call utl_abort('csg_toolbox: please specify a vertical waveband decomposition when TOOLBOX=VERTMODES_WAVEBAND')
778 end if
779
780 call vms_computeModesFromFunction(vco_ens, lengthScaleTop, lengthScaleBot, & ! IN
781 vModes) ! OUT
782 call vms_writeModes(vModes)
783
784 do waveBandIndex = 1, nVertWaveBand
785
786 call vertModesFilter(ensPerts, & ! IN
787 ensPerts_ptr, & ! OUT
788 vModes, waveBandIndex) ! IN
789
790 write(wbnum,'(I2.2)') waveBandIndex
791
792 ! Compute the grid point std dev
793 call ens_computeStdDev(ensPerts_ptr)
794 call ens_copyEnsStdDev(ensPerts_ptr, & ! IN
795 statevector_template) ! OUT
796 call gio_writeToFile(statevector_template, './stddev_'//trim(wbnum)//'.fst', 'STD_GRIDP_'//trim(wbnum), &
797 typvar_opt = 'E', numBits_opt = 32)
798
799 ! Compute the global std dev
800 if (waveBandIndex == 1) then
801 call gbi_setup(gbi_globalMean, 'HorizontalMean', statevector_template, hco_ens)
802 end if
803 call gbi_stdDev(gbi_globalMean, ensPerts_ptr, & ! IN
804 statevector_template) ! OUT
805 call gio_writeToFile(statevector_template, './stddev_'//trim(wbnum)//'.fst', 'STD_GLB_'//trim(wbnum), &
806 typvar_opt = 'E', numBits_opt = 32)
807
808 ! Write the scale-decomposed ensemble perturbations for member=1
809 call ens_copyMember(ensPerts_ptr, stateVector_template, 1)
810 call gio_writeToFile(statevector_template, './ensPert_0001_'//trim(wbnum)//'.fst', 'ENSPERT_'//trim(wbnum), &
811 numBits_opt = 32)
812
813 end do
814
815 ! Write the full ensemble perturbations for member=1
816 call ens_copyMember(ensPerts, stateVector_template, 1)
817 call gio_writeToFile(statevector_template, './ensPert_0001.fst', 'ENSPERT', &
818 numBits_opt = 32)
819
820 ! Compute the grid point std dev for the full perturbations
821 call ens_computeStdDev(ensPerts)
822 call ens_copyEnsStdDev(ensPerts, & ! IN
823 statevector_template) ! OUT
824 call gio_writeToFile(statevector_template, './stddev.fst', 'STD_GRIDP', &
825 typvar_opt = 'E', numBits_opt = 32)
826
827 ! Compute the global std dev for the full perturbations
828 call gbi_stdDev(gbi_globalMean, ensPerts, & ! IN
829 statevector_template) ! OUT
830 call gio_writeToFile(statevector_template, './stddev.fst', 'STD_GLB', &
831 typvar_opt = 'E', numBits_opt = 32)
832
833 case default
834 write(*,*)
835 write(*,*) 'Unknown TOOL in csg_toolbox : ', trim(tool)
836 call utl_abort('csg_toolbox')
837 end select
838
839 !
840 !- Write the estimated pressure profiles
841 !
842 if (vco_ens%vgridPresent) then
843 call writePressureProfiles
844 end if
845
846 !
847 !- Ending
848 !
849 call ens_deallocate(ensPerts)
850
851 write(*,*)
852 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
853 write(*,*) 'csl_toolbox: Done!'
854
855 end subroutine csg_toolbox
856
857 !--------------------------------------------------------------------------
858 ! WRITESPSTATS
859 !--------------------------------------------------------------------------
860 subroutine writeSpStats(ptot,theta)
861 !
862 !:Purpose: Write the spectral representation of PtoT and THETA to files
863 !
864 implicit none
865
866 ! Arguments:
867 real(8), intent(in) :: PtoT(:,:,:)
868 real(8), intent(in) :: theta(:,:)
869
870 ! Locals:
871 integer jn,ierr,ipak,latIndex,levIndex1,levIndex2,nlev
872 integer fstouv,fnom,fstfrm,fclos
873 integer ip1,ip3,kni,knj,idatyp,idateo
874 integer :: nulstats
875 real(8) :: bufz(nLevEns_M),bufyz(nj,nLevEns_M),zsp(0:ntrunc,nLevEns_M)
876 real(8) :: bufptot(nj,(nLevEns_T+1)*nLevEns_M),spptot(0:ntrunc,(nLevEns_T+1)*nLevEns_M)
877 real(8) :: zspptot(nLevEns_T+1,nLevEns_M)
878
879 if (mmpi_myid /= 0) return
880
881 nulstats=0
882 ierr = fnom (nulstats,'./stats_sp.fst','RND',0)
883 ierr = fstouv(nulstats,'RND')
884
885 ipak = -32
886 idatyp = 5
887 ip1 = 0
888 ip3 = nens
889 idateo = 0
890
891 ! write out SP_THETA
892
893 do latIndex = 1, nj
894 do levIndex1 = 1, nLevEns_M
895 bufyz(latIndex,levIndex1) = theta(levIndex1,latIndex)
896 end do
897 end do
898
899 call gst_zlegdir(gstID_nkgdimEns,bufyz,zsp,nLevEns_M)
900
901 do jn = 0, ntrunc
902 do levIndex1=1, nLevEns_M
903 bufz(levIndex1) = zsp(jn,levIndex1)
904 end do
905
906 ierr = utl_fstecr(bufz,ipak,nulstats,idateo,0,0,nlevEns_M,1,1, &
907 ip1,jn,ip3,'X','ZZ','SP_THETA','X',0,0,0,0,idatyp,.true.)
908
909 end do
910
911 ! write out SP_PTOT
912
913 do latIndex = 1, nj
914 do levIndex1 = 1, (nLevEns_T+1)
915 do levIndex2 = 1, nLevEns_M
916 bufptot(latIndex,(levIndex2-1)*(nLevEns_T+1)+levIndex1) = PtoT(levIndex1,levIndex2,latIndex)
917 end do
918 end do
919 end do
920
921 nlev=(nLevEns_T+1)*nLevEns_M
922 call gst_zlegdir(gstID_nkgdimEns,bufptot,spptot,nLev)
923
924 do jn = 0, ntrunc
925 do levIndex1 = 1, (nLevEns_T+1)
926 do levIndex2 = 1, nLevEns_M
927 zspptot(levIndex1,levIndex2) = spptot(jn,(levIndex2-1)*(nLevEns_T+1)+levIndex1)
928 end do
929 end do
930
931 kni=nLevEns_T+1
932 knj=nLevEns_M
933 ierr = utl_fstecr(zspptot,ipak,nulstats,idateo,0,0,kni,knj,1, &
934 ip1,jn,ip3,'X','ZZ','SP_PTOT ','X',0,0,0,0,idatyp,.true.)
935 end do
936
937 ierr = fstfrm(nulstats)
938 ierr = fclos (nulstats)
939
940 write(*,*) 'finished writing statistics...'
941
942 end subroutine writeSpStats
943
944 !--------------------------------------------------------------------------
945 ! REMOVEBALANCEDCHI
946 !--------------------------------------------------------------------------
947 subroutine removeBalancedChi(ensPerturbations,theta)
948 !
949 !:Purpose: Subtract the balanced components of velocity potential
950 ! from the full variable
951 !
952 implicit none
953
954 ! Arguments:
955 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
956 real(8), intent(in) :: theta(:,:)
957
958 ! Locals:
959 real(4), pointer :: psi_ptr(:,:,:), chi_ptr(:,:,:)
960 integer :: ensIndex,latIndex,levIndex,lonIndex
961
962 do ensIndex = 1,nens
963 psi_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,1:nlevEns_M,ensIndex)
964 chi_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,(nlevEns_M+1):(2*nlevEns_M),ensIndex)
965
966 do levIndex = 1, nLevEns_M
967 do latIndex = myLatBeg, myLatEnd
968 do lonIndex = myLonBeg, myLonEnd
969 chi_ptr(lonIndex,latIndex,levIndex) = chi_ptr(lonIndex,latIndex,levIndex) + &
970 tan(theta(levIndex,latIndex))*psi_ptr(lonIndex,latIndex,levIndex)
971 end do
972 end do
973 end do
974
975 end do
976
977 write(*,*) 'finished removing balanced chi...'
978
979 end subroutine removeBalancedChi
980
981 !--------------------------------------------------------------------------
982 ! REMOVEBALANCEDT_PS
983 !--------------------------------------------------------------------------
984 subroutine removeBalancedT_Ps(ensPerturbations,ensBalPerturbations,PtoT)
985 !
986 !:Purpose: Subtract the balanced components of temperature and surface
987 ! pressure from the full variables
988 !
989 implicit none
990
991 ! Arguments:
992 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
993 real(4), pointer, intent(inout) :: ensBalPerturbations(:,:,:,:)
994 real(8), intent(in) :: PtoT(:,:,:)
995
996 ! Locals:
997 real(4),pointer :: tt_ptr(:,:,:), ps_ptr(:,:,:), ttb_ptr(:,:,:), psb_ptr(:,:,:)
998 real(8) :: spectralState(nla_mpilocal,2,nLevEns_M), spBalancedP(nla_mpilocal,2,nlevEns_M)
999 real(8) :: balancedP(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nlevEns_M)
1000 real(8) :: psi(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nLevEns_M)
1001 integer :: ensIndex, latIndex, lonIndex, jk1, jk2
1002
1003 do ensIndex=1,nens
1004
1005 write(*,*) 'removing balanced T and Ps for member ', ensIndex
1006
1007 psi(:,:,:) = ensPerturbations(:,:,1:nlevEns_M,ensIndex)
1008 call gst_setID(gstID_nLevEns_M)
1009 call gst_reespe(spectralState,psi)
1010 call calcBalancedP(spectralState,spBalancedP)
1011 call gst_speree(spBalancedP,balancedP)
1012
1013 tt_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,(1+2*nLevEns_M):(2*nLevEns_M+1*nLevEns_T),ensIndex)
1014 ps_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,(1+2*nLevEns_M+2*nLevEns_T):(1+2*nLevEns_M+2*nLevEns_T),ensIndex)
1015 ttb_ptr(myLonBeg:,myLatBeg:,1:) => ensBalPerturbations(:,:,1:nLevEns_T,ensIndex)
1016 psb_ptr(myLonBeg:,myLatBeg:,1:) => ensBalPerturbations(:,:,(1+nLevEns_T):(1+nLevEns_T),ensIndex)
1017
1018 ttb_ptr(:,:,:)=0.0d0
1019 psb_ptr(:,:,:)=0.0d0
1020
1021 !$OMP PARALLEL DO PRIVATE (latIndex,lonIndex,jk1,jk2)
1022 do latIndex = myLatBeg, myLatEnd
1023 do lonIndex = myLonBeg, myLonEnd
1024 do jk1 = 1, nLevEns_T
1025 do jk2 = 1, nlevptot
1026 ttb_ptr(lonIndex,latIndex,jk1) = ttb_ptr(lonIndex,latIndex,jk1) + PtoT(jk1,jk2,latIndex)*balancedP(lonIndex,latIndex,jk2)
1027 end do
1028 tt_ptr(lonIndex,latIndex,jk1) = tt_ptr(lonIndex,latIndex,jk1) - ttb_ptr(lonIndex,latIndex,jk1)
1029 end do
1030 do jk2 = 1, nlevptot
1031 psb_ptr(lonIndex,latIndex,1) = psb_ptr(lonIndex,latIndex,1) + PtoT(nLevEns_T+1,jk2,latIndex)*balancedP(lonIndex,latIndex,jk2)
1032 end do
1033 ps_ptr(lonIndex,latIndex,1) = ps_ptr(lonIndex,latIndex,1) - psb_ptr(lonIndex,latIndex,1)
1034 end do
1035 end do
1036 !$OMP END PARALLEL DO
1037
1038 end do
1039
1040 write(*,*) 'finished removing balanced T and Ps...'
1041
1042 end subroutine removeBalancedT_Ps
1043
1044 !--------------------------------------------------------------------------
1045 ! CALCCORRELATIONS
1046 !--------------------------------------------------------------------------
1047 subroutine calcCorrelations(ensPerturbations,corns,rstddev,latMask_opt)
1048 !
1049 !:Purpose: Calculate the homogeneous and isotropic correlations in spectral space
1050 !
1051 implicit none
1052
1053 ! Arguments:
1054 real(4), pointer, intent(in) :: ensPerturbations(:,:,:,:)
1055 real(8), intent(out) :: corns(nkgdimEns,nkgdimEns,0:ntrunc)
1056 real(8), intent(out) :: rstddev(nkgdimEns,0:ntrunc)
1057 real(8), optional, intent(in) :: latMask_opt(:)
1058
1059 ! Locals:
1060 real(8) :: spectralState(nla_mpilocal,2,nkgdimEns)
1061 real(8) :: corns_mpiglobal(nkgdimEns,nkgdimEns,0:ntrunc)
1062 real(8) :: gridState(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nkgdimEns)
1063 real(8) :: dfact,dfact2,dsummed
1064 integer :: ensIndex,ila_mpilocal,ila_mpiglobal,jn,jm,jk1,jk2,latIndex,nsize,ierr
1065
1066 call utl_tmg_start(120,'--CalcStats_Corr')
1067
1068 corns(:,:,:) = 0.0d0
1069 do ensIndex = 1, nens
1070
1071 write(*,*) 'calcCorrelations: processing member ',ensIndex
1072
1073 gridState(:,:,:) = ensPerturbations(:,:,:,ensIndex)
1074 if(present(latMask_opt)) then
1075 do latIndex = myLatBeg, myLatEnd
1076 gridState(:,latIndex,:) = latMask_opt(latIndex)*gridState(:,latIndex,:)
1077 end do
1078 end if
1079 call gst_setID(gstID_nkgdimEns)
1080 call gst_reespe(spectralState,gridState)
1081
1082 !$OMP PARALLEL DO PRIVATE (jn,jm,dfact,ila_mpilocal,ila_mpiglobal,jk1,jk2)
1083 do jn = mynBeg, mynEnd, mynSkip
1084 do jm = mymBeg, mymEnd, mymSkip
1085 if(jm.le.jn) then
1086 dfact = 2.0d0
1087 if (jm.eq.0) dfact = 1.0d0
1088 ila_mpiglobal = gst_getNind(jm,gstID_nkgdimEns) + jn - jm
1089 ila_mpilocal = ilaList_mpilocal(ila_mpiglobal)
1090 do jk1 = 1, nkgdimEns
1091 do jk2 = 1, nkgdimEns
1092 corns(jk1,jk2,jn) = corns(jk1,jk2,jn) + &
1093 dfact*( spectralState(ila_mpilocal,1,jk1)*spectralState(ila_mpilocal,1,jk2) + &
1094 spectralState(ila_mpilocal,2,jk1)*spectralState(ila_mpilocal,2,jk2) )
1095 end do
1096 end do
1097 end if
1098 end do
1099 end do
1100 !$OMP END PARALLEL DO
1101
1102 end do
1103
1104 ! communicate between all tasks
1105 nsize = nkgdimEns*nkgdimEns*(1+ntrunc)
1106 call rpn_comm_allreduce(corns,corns_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
1107 corns(:,:,:) = corns_mpiglobal(:,:,:)
1108
1109 !$OMP PARALLEL DO PRIVATE (jn,jk1)
1110 do jn = 0, ntrunc
1111 do jk1 = 1, nkgdimEns
1112 if(abs(corns(jk1,jk1,jn)).gt.0.0d0) then
1113 rstddev(jk1,jn) = dsqrt(abs(corns(jk1,jk1,jn)))
1114 else
1115 rstddev(jk1,jn) = 0.0d0
1116 end if
1117 end do
1118 end do
1119 !$OMP END PARALLEL DO
1120
1121 !$OMP PARALLEL DO PRIVATE (jn,jk1,jk2)
1122 do jn = 0, ntrunc
1123 do jk1 = 1, nkgdimEns
1124 do jk2 = 1, nkgdimEns
1125 if(rstddev(jk1,jn).ne.0..and.rstddev(jk2,jn).ne.0.) then
1126 corns(jk1,jk2,jn) = corns(jk1,jk2,jn)/(rstddev(jk1,jn)*rstddev(jk2,jn))
1127 else
1128 corns(jk1,jk2,jn) = 0.0d0
1129 end if
1130 end do
1131 end do
1132 end do
1133 !$OMP END PARALLEL DO
1134
1135 dfact2 = 1.0d0/sqrt(dble(nens-1))
1136 do jn = 0, ntrunc
1137 dfact = 1.0d0/sqrt(2.0d0*dble(jn) + 1.0d0)
1138 do jk1 = 1, nkgdimEns
1139 rstddev(jk1,jn) = rstddev(jk1,jn)*dfact2*dfact
1140 end do
1141 end do
1142
1143 ! Normalize to ensure correlations in horizontal and Multiply by sqrt(0.5) to make valid for m.ne.0
1144 !$OMP PARALLEL DO PRIVATE (jk1,jn,dsummed)
1145 do jk1 = 1, nkgdimEns
1146 dsummed=0.0d0
1147 do jn = 0, ntrunc
1148 dsummed=dsummed + (rstddev(jk1,jn)**2)*((2.0d0*dble(jn))+1.0d0)/2.0d0
1149 end do
1150 do jn = 0, ntrunc
1151 if(dsummed.gt.0.0d0) rstddev(jk1,jn)=rstddev(jk1,jn)*sqrt(0.5d0/dsummed)
1152 end do
1153 end do
1154 !$OMP END PARALLEL DO
1155
1156 call utl_tmg_stop(120)
1157 write(*,*) 'finished computing correlations...'
1158
1159 end subroutine calcCorrelations
1160
1161 !--------------------------------------------------------------------------
1162 ! CALCCORRELATIONS2
1163 !--------------------------------------------------------------------------
1164 subroutine calcCorrelations2(ensPerts,corns,rstddev,latMask_opt)
1165 !
1166 !:Purpose: Calculate the homogeneous and isotropic correlations in spectral space
1167 !
1168 implicit none
1169
1170 ! Arguments:
1171 type(struct_ens), intent(inout) :: ensPerts
1172 real(8), intent(out) :: corns(nkgdimEns,nkgdimEns,0:ntrunc)
1173 real(8), intent(out) :: rstddev(nkgdimEns,0:ntrunc)
1174 real(8), optional, intent(in) :: latMask_opt(:)
1175
1176 ! Locals:
1177 real(4), pointer :: ptr4d_r4(:,:,:,:)
1178 real(8) :: corns_mpiglobal(nkgdimEns,nkgdimEns,0:ntrunc)
1179 real(8) :: spectralState(nla_mpilocal,2,nkgdimEns)
1180 real(8) :: gridState(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nkgdimEns)
1181 real(8) :: dfact, dfact2, dsummed
1182 integer :: ensIndex, ila_mpilocal, ila_mpiglobal, jn, jm, jk1, jk2
1183 integer :: levIndex, latIndex, nsize, ierr
1184
1185 call utl_tmg_start(120,'--CalcStats_Corr')
1186
1187 corns(:,:,:) = 0.0d0
1188 do ensIndex = 1, nens
1189
1190 write(*,*) 'calcCorrelations: processing member ',ensIndex
1191
1192 !- 2.1 Extract fields from ensPerturbations
1193 do levIndex = 1, ens_getNumK(ensPerts)
1194 ptr4d_r4 => ens_getOneLev_r4(ensPerts,levIndex)
1195 gridState(:,:,levIndex) = real(ptr4d_r4(ensIndex,1,:,:),8)
1196 end do
1197 if ( present(latMask_opt) ) then
1198 do latIndex = myLatBeg, myLatEnd
1199 gridState(:,latIndex,:) = latMask_opt(latIndex)*gridState(:,latIndex,:)
1200 end do
1201 end if
1202 call gst_setID(gstID_nkgdimEns)
1203 call gst_reespe(spectralState,gridState)
1204
1205 !$OMP PARALLEL DO PRIVATE (jn,jm,dfact,ila_mpilocal,ila_mpiglobal,jk1,jk2)
1206 do jn = mynBeg, mynEnd, mynSkip
1207 do jm = mymBeg, mymEnd, mymSkip
1208 if(jm.le.jn) then
1209 dfact = 2.0d0
1210 if (jm.eq.0) dfact = 1.0d0
1211 ila_mpiglobal = gst_getNind(jm,gstID_nkgdimEns) + jn - jm
1212 ila_mpilocal = ilaList_mpilocal(ila_mpiglobal)
1213 do jk1 = 1, nkgdimEns
1214 do jk2 = 1, nkgdimEns
1215 corns(jk1,jk2,jn) = corns(jk1,jk2,jn) + &
1216 dfact*( spectralState(ila_mpilocal,1,jk1)*spectralState(ila_mpilocal,1,jk2) + &
1217 spectralState(ila_mpilocal,2,jk1)*spectralState(ila_mpilocal,2,jk2) )
1218 end do
1219 end do
1220 end if
1221 end do
1222 end do
1223 !$OMP END PARALLEL DO
1224
1225 end do
1226
1227 ! communicate between all tasks
1228 nsize = nkgdimEns*nkgdimEns*(1+ntrunc)
1229 call rpn_comm_allreduce(corns,corns_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
1230 corns(:,:,:) = corns_mpiglobal(:,:,:)
1231
1232 !$OMP PARALLEL DO PRIVATE (jn,jk1)
1233 do jn = 0, ntrunc
1234 do jk1 = 1, nkgdimEns
1235 if(abs(corns(jk1,jk1,jn)).gt.0.0d0) then
1236 rstddev(jk1,jn) = dsqrt(abs(corns(jk1,jk1,jn)))
1237 else
1238 rstddev(jk1,jn) = 0.0d0
1239 end if
1240 end do
1241 end do
1242 !$OMP END PARALLEL DO
1243
1244 !$OMP PARALLEL DO PRIVATE (jn,jk1,jk2)
1245 do jn = 0, ntrunc
1246 do jk1 = 1, nkgdimEns
1247 do jk2 = 1, nkgdimEns
1248 if(rstddev(jk1,jn).ne.0..and.rstddev(jk2,jn).ne.0.) then
1249 corns(jk1,jk2,jn) = corns(jk1,jk2,jn)/(rstddev(jk1,jn)*rstddev(jk2,jn))
1250 else
1251 corns(jk1,jk2,jn) = 0.0d0
1252 end if
1253 end do
1254 end do
1255 end do
1256 !$OMP END PARALLEL DO
1257
1258 dfact2 = 1.0d0/sqrt(dble(nens-1))
1259 do jn = 0, ntrunc
1260 dfact = 1.0d0/sqrt(2.0d0*dble(jn) + 1.0d0)
1261 do jk1 = 1, nkgdimEns
1262 rstddev(jk1,jn) = rstddev(jk1,jn)*dfact2*dfact
1263 end do
1264 end do
1265
1266 ! Normalize to ensure correlations in horizontal and Multiply by sqrt(0.5) to make valid for m.ne.0
1267 !$OMP PARALLEL DO PRIVATE (jk1,jn,dsummed)
1268 do jk1 = 1, nkgdimEns
1269 dsummed=0.0d0
1270 do jn = 0, ntrunc
1271 dsummed=dsummed + (rstddev(jk1,jn)**2)*((2.0d0*dble(jn))+1.0d0)/2.0d0
1272 end do
1273 do jn = 0, ntrunc
1274 if(dsummed.gt.0.0d0) rstddev(jk1,jn)=rstddev(jk1,jn)*sqrt(0.5d0/dsummed)
1275 end do
1276 end do
1277 !$OMP END PARALLEL DO
1278
1279 call utl_tmg_stop(120)
1280 write(*,*) 'finished computing correlations...'
1281
1282 end subroutine calcCorrelations2
1283
1284 !--------------------------------------------------------------------------
1285 ! CALCPOWERSPEC
1286 !--------------------------------------------------------------------------
1287 subroutine calcPowerSpec(ensPerts,powerSpec)
1288 !
1289 !:Purpose: Calculate the horizontal power spectrum of the ensemble
1290 ! of error samples
1291 !
1292 implicit none
1293
1294 ! Arguments:
1295 type(struct_ens), intent(inout) :: ensPerts
1296 real(8), allocatable, intent(out) :: powerSpec(:,:)
1297
1298 ! Locals:
1299 real(8), allocatable :: ensPertSP(:,:,:)
1300 real(8), allocatable :: ensPertGD(:,:,:)
1301 real(4), pointer :: ptr4d_r4(:,:,:,:)
1302 real(8) :: dfact, dfact2
1303 integer :: gstPowerSpecID
1304 integer :: memberIndex, levIndex, latIndex, lonIndex
1305 integer :: jn, jm, ila_mpilocal, ila_mpiglobal
1306
1307 allocate(powerSpec (ens_getNumK(ensPerts),0:ntrunc))
1308
1309 !
1310 !- Spectral decomposition and spectral coefficient summation
1311 !
1312 gstPowerSpecID = gst_setup(ni,nj,nTrunc,nEnsOverDimension)
1313
1314 allocate(ensPertSP(nla_mpilocal,2,nEnsOverDimension))
1315 allocate(ensPertGD(nEnsOverDimension,myLonBeg:myLonEnd,myLatBeg:myLatEnd))
1316
1317 powerSpec(:,:)=0.0d0
1318
1319 do levIndex = 1, ens_getNumK(ensPerts) ! Loop on variables and vertical levels
1320
1321 ptr4d_r4 => ens_getOneLev_r4(ensPerts,levIndex)
1322
1323 do latIndex = myLatBeg, myLatEnd
1324 ensPertGD(:,:,latIndex) = 0.0d0
1325 end do
1326
1327 !$OMP PARALLEL DO PRIVATE (memberIndex,latIndex,lonIndex)
1328 do latIndex = myLatBeg, myLatEnd
1329 do lonIndex = myLonBeg, myLonEnd
1330 do memberIndex = 1, nEns
1331 ensPertGD(memberIndex,lonIndex,latIndex) = dble(ptr4d_r4(memberIndex,1,lonIndex,latIndex))
1332 end do
1333 end do
1334 end do
1335 !$OMP END PARALLEL DO
1336
1337 !- GridPoint space -> Spectral Space
1338 call gst_setID(gstPowerSpecID) ! IN
1339 call gst_reespe_kij(ensPertSP, & ! OUT
1340 ensPertGD) ! IN
1341
1342 !$OMP PARALLEL DO PRIVATE (memberIndex,jn,jm,dfact,ila_mpilocal,ila_mpiglobal)
1343 do jn = mynBeg, mynEnd, mynSkip
1344 do jm = mymBeg, mymEnd, mymSkip
1345 if (jm .le. jn) then
1346 dfact = 2.0d0
1347 if (jm.eq.0) dfact = 1.0d0
1348 ila_mpiglobal = gst_getNind(jm,gstPowerSpecID) + jn - jm
1349 ila_mpilocal = ilaList_mpilocal(ila_mpiglobal)
1350 do memberIndex = 1, nEns
1351 powerSpec(levIndex,jn) = powerSpec(levIndex,jn) + &
1352 dfact*( ensPertSP(ila_mpilocal,1,memberIndex)**2 + ensPertSP(ila_mpilocal,2,memberIndex)**2 )
1353 end do
1354 end if
1355 end do
1356 end do
1357 !$OMP END PARALLEL DO
1358
1359 end do
1360
1361 !
1362 !- Communicate between all tasks
1363 !
1364 call mmpi_allreduce_sumR8_2d(powerSpec, "GRID")
1365
1366 !
1367 !- Apply the appropriate scaling
1368 !
1369 dfact2 = 1.0d0/sqrt(dble(nens-1))
1370 do jn = 0, ntrunc
1371 dfact = 1.0d0/sqrt(2.0d0*dble(jn) + 1.0d0)
1372 do levIndex = 1, ens_getNumK(ensPerts)
1373 powerSpec(levIndex,jn) = powerSpec(levIndex,jn)*dfact2*dfact
1374 end do
1375 end do
1376
1377 write(*,*) 'finished computing power spectrum...'
1378
1379 end subroutine calcPowerSpec
1380
1381 !--------------------------------------------------------------------------
1382 ! WRITESTATS
1383 !--------------------------------------------------------------------------
1384 subroutine writeStats(corns, rstddev, ptot_opt, theta_opt, waveBandIndex_opt, latBand_opt)
1385 !
1386 !:Purpose: Write several components of the BHI matrix to a file
1387 !
1388 implicit none
1389
1390 ! Arguments:
1391 real(8), intent(in) :: corns(nkgdimEns,nkgdimEns,0:ntrunc)
1392 real(8), intent(in) :: rstddev(nkgdimEns,0:ntrunc)
1393 real(8), optional, intent(in) :: PtoT_opt(:,:,:)
1394 real(8), optional, intent(in) :: theta_opt(:,:)
1395 integer, optional, intent(in) :: waveBandIndex_opt
1396 integer, optional, intent(in) :: latBand_opt
1397
1398 ! Locals:
1399 real(8) :: prcor(nkgdimEns,nkgdimEns)
1400 integer :: jn,ierr,ipak,jk,jl
1401 integer :: fstouv,fnom,fstfrm,fclos
1402 integer :: ip1,ip2,ip3,idatyp,idateo
1403 integer :: nulstats
1404 character(len=128) :: outfilename
1405 character(len=2) :: wbnum
1406
1407 if (mmpi_myid /= 0) return
1408
1409 nulstats=0
1410 if ( nWaveBand == 1 ) then
1411 outfilename='./bgcov.fst'
1412 else
1413 if (.not. present(waveBandIndex_opt)) then
1414 write(*,*) 'writeStats: No waveBandIndex was supplied!!!'
1415 call utl_abort('calbmatrix_glb')
1416 end if
1417 write(wbnum,'(I2.2)') waveBandIndex_opt
1418 outfilename='./bgcov_'//trim(wbnum)//'.fst'
1419 end if
1420 ierr = fnom (nulstats,trim(outfilename),'RND',0)
1421 ierr = fstouv(nulstats,'RND')
1422
1423 ipak = -32
1424 idatyp = 5
1425 ip1 = 0
1426 ip2 = 0
1427 ip3 = nens
1428 idateo = 0
1429
1430 if(present(latBand_opt)) ip1 = latBand_opt
1431
1432 if (present(ptot_opt)) then
1433 ierr = utl_fstecr(ptot_opt,ipak,nulstats,idateo,0,0,nlevEns_T+1,nlevEns_M,nj, &
1434 ip1,ip2,ip3,'X','ZZ','P_to_T ','X',0,0,0,0,idatyp,.true.)
1435 end if
1436 if (present(theta_opt)) then
1437 ierr = utl_fstecr(theta_opt,ipak,nulstats,idateo,0,0,nlevEns_M,nj,1, &
1438 ip1,ip2,ip3,'X','ZZ','THETA ','X',0,0,0,0,idatyp,.true.)
1439 end if
1440
1441 do jn = 0, ntrunc
1442 ip2 = jn
1443 ierr = utl_fstecr(corns(:,:,jn),ipak,nulstats,idateo,0,0,nkgdimEns,nkgdimEns,1, &
1444 ip1,ip2,ip3,'X','ZZ','CORRNS ','X',0,0,0,0,idatyp,.true.)
1445 end do
1446
1447 do jn = 0, ntrunc
1448 ip2 = jn
1449 ierr = utl_fstecr(rstddev(:,jn),ipak,nulstats,idateo,0,0,nkgdimEns,1,1, &
1450 ip1,ip2,ip3,'X','SS','RSTDDEV ','X',0,0,0,0,idatyp,.true.)
1451 end do
1452
1453
1454 ! Computing the total vertical correlation matrix
1455 do jk = 1, nkgdimEns
1456 do jl = 1, nkgdimEns
1457 prcor(jk,jl) = 0.0d0
1458 do jn = 0, ntrunc
1459 prcor(jk,jl) = prcor(jk,jl) + ((2*jn+1)*rstddev(jk,jn)*rstddev(jl,jn)*corns(jk,jl,jn))
1460 end do
1461 end do
1462 end do
1463
1464 do jk = 1, nkgdimEns
1465 do jl = 1, nkgdimEns
1466 if(prcor(jk,jk)*prcor(jl,jl) .gt. 0.0d0) then
1467 prcor(jk,jl) = prcor(jk,jl) / (sqrt(prcor(jk,jk)*prcor(jl,jl)))
1468 else
1469 prcor(jk,jl) = 0.0d0
1470 end if
1471 end do
1472 end do
1473 ip2 =0
1474 ierr = utl_fstecr(prcor(:,:),ipak,nulstats,idateo,0,0,nkgdimEns,nkgdimEns,1, &
1475 ip1,ip2,ip3,'X','ZV','CORVERT ','X',0,0,0,0,idatyp,.true.)
1476
1477 ierr = fstfrm(nulstats)
1478 ierr = fclos (nulstats)
1479
1480 write(*,*) 'finished writing statistics...'
1481
1482 end subroutine writeStats
1483
1484 !--------------------------------------------------------------------------
1485 ! WRITEPRESSUREPROFILES
1486 !--------------------------------------------------------------------------
1487 subroutine writePressureProfiles
1488 !
1489 !:Purpose: Write the profiles of pressure to ascii files
1490 !
1491 implicit none
1492
1493 ! Locals:
1494 character(len=128) :: outfilename
1495 integer :: jk
1496
1497 if (mmpi_myid /= 0) return
1498
1499 outfilename = "./pressureProfile_M.txt"
1500 open (unit=99,file=outfilename,action="write",status="new")
1501 do jk = 1, nLevEns_M
1502 write(99,'(I3,2X,F7.2)') jk, pressureProfile_M(jk)/100.d0
1503 end do
1504 close(unit=99)
1505
1506 outfilename = "./pressureProfile_T.txt"
1507 open (unit=99,file=outfilename,action="write",status="new")
1508 do jk = 1, nLevEns_T
1509 write(99,'(I3,2X,F7.2)') jk, pressureProfile_T(jk)/100.d0
1510 end do
1511 close(unit=99)
1512
1513 write(*,*) 'finished writing pressure profiles...'
1514
1515 end subroutine writePressureProfiles
1516
1517 !--------------------------------------------------------------------------
1518 ! WRITESTDDEV
1519 !--------------------------------------------------------------------------
1520 subroutine writeStddev(stddevZonAvg,stddev3d,stddevZonAvgUnbal_opt,stddev3dUnbal_opt)
1521 !
1522 !:Purpose: Write the stddev to a file
1523 !
1524 implicit none
1525
1526 ! Arguments:
1527 real(8), pointer, intent(in) :: stddevZonAvg(:,:)
1528 real(8), pointer, intent(in) :: stddev3d(:,:,:)
1529 real(8), pointer, optional, intent(in) :: stddevZonAvgUnbal_opt(:,:)
1530 real(8), pointer, optional, intent(in) :: stddev3dUnbal_opt(:,:,:)
1531
1532 ! Locals:
1533 type(struct_gsv) :: stateVector
1534 real(8) :: dfact, zbufyz(nj,max(nLevEns_M,nLevens_T)), zbufy(nj)
1535 integer :: latIndex, levIndex, ierr, varIndex, varIndexStddev, nLevEns, numVarToWrite
1536 integer :: ip1, ip2, ip3, idatyp, idateo, numBits
1537 integer :: nulstats
1538 real(8), pointer :: field(:,:,:)
1539 integer, allocatable :: dateStampList(:)
1540 character(len=4) :: nomVarToWrite(1:20)
1541 integer :: fstouv, fnom, fstfrm, fclos
1542
1543 numBits = 32
1544 idatyp = 5
1545 ip1 = 0
1546 ip2 = 0
1547 ip3 = nens
1548 idateo = 0
1549
1550 ! figure out full list of variables to write
1551 numVarToWrite = size(nomvar,1)
1552 nomVarToWrite(1:numVarToWrite) = nomvar(:,cvSpace)
1553 if (present(stddevZonAvgUnbal_opt) .and. present(stddev3dUnbal_opt)) then
1554 do varIndex = 1, nvar
1555 if( all(nomvar(:,cvSpace) /= nomvar(varIndex,cvUnbalSpace)) ) then
1556 numVarToWrite = numVarToWrite + 1
1557 nomVarToWrite(numVarToWrite) = nomvar(varIndex,cvUnbalSpace)
1558 end if
1559 end do
1560 end if
1561
1562 ! first, write stddev3d
1563
1564 ! allocate stateVector used for writing stddev3d
1565 allocate(dateStampList(1))
1566 dateStampList(:) = 0
1567 call gsv_allocate( stateVector, 1, hco_ens, vco_ens, &
1568 mpi_local_opt=.true., dateStampList_opt=dateStampList, &
1569 varNames_opt=nomVarToWrite(1:numVarToWrite) )
1570 do varIndex = 1, numVarToWrite
1571 nLevEns = gsv_getNumLevFromVarName(stateVector,nomVarToWrite(varIndex))
1572 call gsv_getField(stateVector,field,nomVarToWrite(varIndex))
1573 if ( any(nomVarToWrite(varIndex) == nomvar(:,cvSpace)) ) then
1574 field(:,:,:) = stddev3d(:, :, (varLevOffset(varIndex)+1):(varLevOffset(varIndex)+nLevEns) )
1575 else if(present(stddevZonAvgUnbal_opt) .and. present(stddev3dUnbal_opt)) then
1576 varIndexStddev = findloc(nomvar(:,cvUnbalSpace), value=nomVarToWrite(varIndex), dim=1)
1577 field(:,:,:) = stddev3dUnbal_opt(:, :, (varLevOffset(varIndexStddev)+1):(varLevOffset(varIndexStddev)+nLevEns) )
1578 end if
1579 end do
1580 call gio_writeToFile(stateVector, './stddev.fst', etiket_in='STDDEV3D', ip3_opt=ip3, &
1581 typvar_opt='E', numBits_opt=numBits, containsFullField_opt=.false.)
1582
1583 ! second, write stddev (zonal average)
1584
1585 if (mmpi_myid == 0) then
1586 nulstats=0
1587 ierr = fnom (nulstats,'./stddev.fst','RND',0)
1588 ierr = fstouv(nulstats,'RND')
1589
1590 ! do 3d variables
1591 do varIndex=1,nvar3d
1592 nLevEns = gsv_getNumLevFromVarName(stateVector,nomvar(varIndex,cvSpace))
1593
1594 dfact=1.0d0
1595 do levIndex = 1, nlevEns
1596 do latIndex = 1, nj
1597 zbufyz(latIndex,levIndex)=dfact*stddevZonAvg(varLevOffset(varIndex)+levIndex,latIndex)
1598 end do
1599 end do
1600 ierr = utl_fstecr(zbufyz(:,1:nLevEns),-numBits,nulstats,idateo,0,0,1,nj,nlevEns,ip1,ip2,ip3, &
1601 'E',nomvar3d(varIndex,cvSpace),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1602
1603 if (present(stddevZonAvgUnbal_opt) .and. present(stddev3dUnbal_opt)) then
1604 if(nomvar3d(varIndex,cvSpace).ne.nomvar3d(varIndex,cvUnbalSpace)) then
1605 dfact=1.0d0
1606 do levIndex = 1, nlevEns
1607 do latIndex = 1, nj
1608 zbufyz(latIndex,levIndex)=dfact*stddevZonAvgUnbal_opt(varLevOffset(varIndex)+levIndex,latIndex)
1609 end do
1610 end do
1611 ierr = utl_fstecr(zbufyz(:,1:nLevEns),-numBits,nulstats,idateo,0,0,1,nj,nlevEns,ip1,ip2,ip3, &
1612 'E',nomvar3d(varIndex,cvUnbalSpace),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1613 end if
1614 end if
1615
1616 end do
1617
1618 ! now do 2D variables
1619 do varIndex=1,nvar2d
1620 if(nomvar2d(varIndex,cvSpace).eq.'P0') then
1621 dfact=1.0d0/1.0d2
1622 else
1623 dfact=1.0d0
1624 end if
1625
1626 do latIndex = 1, nj
1627 zbufy(latIndex)=dfact*stddevZonAvg(varLevOffset(nvar3d+1)+varIndex,latIndex)
1628 end do
1629 ierr = utl_fstecr(zbufy,-numBits,nulstats,idateo,0,0,1,nj,1,ip1,ip2,ip3, &
1630 'E',nomvar2d(varIndex,cvSpace),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1631
1632 if (present(stddevZonAvgUnbal_opt) .and. present(stddev3dUnbal_opt)) then
1633 if(nomvar2d(varIndex,cvSpace).ne.nomvar2d(varIndex,cvUnbalSpace)) then
1634 if(nomvar2d(varIndex,cvUnbalSpace).eq.'UP') then
1635 dfact=1.0d0/1.0d2
1636 else
1637 dfact=1.0d0
1638 end if
1639
1640 do latIndex = 1, nj
1641 zbufy(latIndex)=dfact*stddevZonAvgUnbal_opt(varLevOffset(nvar3d+1)+varIndex,latIndex)
1642 end do
1643 ierr = utl_fstecr(zbufy,-numBits,nulstats,idateo,0,0,1,nj,1,ip1,ip2,ip3, &
1644 'E',nomvar2d(varIndex,cvUnbalSpace),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1645 end if
1646 end if
1647
1648 end do
1649
1650 ierr = fstfrm(nulstats)
1651 ierr = fclos (nulstats)
1652 end if
1653
1654 call gsv_deallocate(stateVector)
1655
1656 write(*,*) 'finished writing stddev...'
1657
1658 end subroutine writeStddev
1659
1660 !--------------------------------------------------------------------------
1661 ! WRITESTDDEVBAL
1662 !--------------------------------------------------------------------------
1663 subroutine writeStddevBal(stddevZonAvgBal,stddev3dBal)
1664 !
1665 !:Purpose: Write the stddev of the balanced variables to a file
1666 !
1667 implicit none
1668
1669 ! Arguments:
1670 real(8), intent(in) :: stddevZonAvgBal(:,:)
1671 real(8), intent(in) :: stddev3dBal(:,:,:)
1672
1673 ! Locals:
1674 type(struct_gsv) :: stateVector
1675 real(8) :: dfact, zbufyz(nj,max(nLevEns_M,nLevens_T)), zbufy(nj)
1676 integer :: latIndex, levIndex, ierr, varIndex, nLevEns
1677 integer :: fstouv, fnom, fstfrm, fclos
1678 integer :: ip1, ip2, ip3, idatyp, idateo, numBits
1679 integer :: nulstats
1680 real(8), pointer :: field(:,:,:)
1681 integer, allocatable :: dateStampList(:)
1682 integer, parameter :: nvar3d=1, nvar2d=1, nvar=nvar3d+nvar2d
1683 character(len=4) :: nomVarToWrite(nvar)
1684 character(len=4) :: nomvar3dBal(nvar3d), nomvar2dBal(nvar2d)
1685 integer :: varLevOffsetBal(nvar)
1686
1687 nomvar3dBal(1)='TB'
1688 nomvar2dBal(1)='PB'
1689 varLevOffsetBal(1) = 0
1690 varLevOffsetBal(2) = 1*nLevEns_T
1691
1692 numBits = 32
1693 idatyp = 5
1694 ip1 = 0
1695 ip2 = 0
1696 ip3 = nens
1697 idateo = 0
1698
1699 nomVarToWrite(1:nvar3d) = nomvar3dBal(:)
1700 nomVarToWrite((1+nvar3d):nvar) = nomvar2dBal(:)
1701
1702 ! first, write stddev3d
1703
1704 ! allocate stateVector used for writing stddev3d
1705 allocate(dateStampList(1))
1706 dateStampList(:) = 0
1707 call gsv_allocate( stateVector, 1, hco_ens, vco_ens, &
1708 mpi_local_opt=.true., dateStampList_opt=dateStampList, &
1709 varNames_opt=nomVarToWrite(1:nvar) )
1710 do varIndex = 1, nvar
1711 nLevEns = gsv_getNumLevFromVarName(stateVector,nomVarToWrite(varIndex))
1712 call gsv_getField(stateVector,field,nomVarToWrite(varIndex))
1713 field(:,:,:) = stddev3dBal(:, :, (varLevOffsetBal(varIndex)+1):(varLevOffsetBal(varIndex)+nLevEns) )
1714 end do
1715 call gio_writeToFile(stateVector, './stddev_balanced.fst', etiket_in='STDDEV3D', ip3_opt=ip3, &
1716 typvar_opt='E', numBits_opt=numBits, containsFullField_opt=.false.)
1717
1718 ! second, write stddev (zonal average)
1719
1720 if (mmpi_myid == 0) then
1721 nulstats=0
1722 ierr = fnom (nulstats,'./stddev_balanced.fst','RND',0)
1723 ierr = fstouv(nulstats,'RND')
1724
1725 ! do 3d variables
1726 do varIndex=1,nvar3d
1727 nLevEns = gsv_getNumLevFromVarName(stateVector,nomVar3dBal(varIndex))
1728 !nip1_l(1:nLevEns_T)=nip1_T(1:nLevEns_T)
1729 dfact=1.0d0
1730 do levIndex = 1, nlevEns
1731 do latIndex = 1, nj
1732 zbufyz(latIndex,levIndex)=dfact*stddevZonAvgBal(varLevOffsetBal(varIndex)+levIndex,latIndex)
1733 end do
1734 end do
1735 ierr = utl_fstecr(zbufyz(:,1:nLevEns),-numBits,nulstats,idateo,0,0,1,nj,nlevEns,ip1,ip2,ip3, &
1736 'E',nomvar3dBal(varIndex),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1737 end do
1738
1739 ! now do 2D variables
1740 do varIndex=1,nvar2d
1741 dfact=1.0d0/1.0d2
1742 do latIndex = 1, nj
1743 zbufy(latIndex)=dfact*stddevZonAvgBal(varLevOffsetBal(nvar3d+1)+varIndex,latIndex)
1744 end do
1745 ierr = utl_fstecr(zbufy,-numBits,nulstats,idateo,0,0,1,nj,1,ip1,ip2,ip3, &
1746 'E',nomvar2dBal(varIndex),'STDDEV ','X',0,0,0,0,idatyp,.true.)
1747 end do
1748
1749 ierr = fstfrm(nulstats)
1750 ierr = fclos (nulstats)
1751 end if
1752
1753 call gsv_deallocate(stateVector)
1754
1755 write(*,*) 'finished writing stddev...'
1756
1757 end subroutine writeStddevBal
1758
1759 !--------------------------------------------------------------------------
1760 ! SPECTRALFILTER
1761 !--------------------------------------------------------------------------
1762 subroutine spectralFilter(ensPerturbations,nlev,waveBandIndex_opt)
1763 !
1764 !:Purpose: Apply a spectral filter
1765 !
1766 implicit none
1767
1768 ! Arguments:
1769 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
1770 integer, intent(in) :: nlev
1771 integer, optional, intent(in) :: waveBandIndex_opt
1772
1773 ! Locals:
1774 real(8) :: spectralState(nla_mpilocal,2,nlev)
1775 real(8) :: member(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nlev)
1776 integer :: ensIndex, jk, jn, jm, ila_mpilocal, ila_mpiglobal
1777 real(8), allocatable :: ResponseFunction(:)
1778 real(8) :: waveLength
1779 character(len=128) :: outfilename
1780 character(len=2) :: wbnum
1781
1782 if ( nWaveBand /= 1 ) then
1783 write(*,*) 'Bandpass filtering step'
1784 if (.not. present(waveBandIndex_opt)) then
1785 write(*,*) 'Error: No waveBandIndex was supplied!!!'
1786 call utl_abort('calbmatrix_glb')
1787 end if
1788 allocate(ResponseFunction(0:ntrunc))
1789 write(wbnum,'(I2.2)') waveBandIndex_opt
1790 outfilename = "./ResponseFunction_"//wbnum//".txt"
1791 open (unit=99,file=outfilename,action="write",status="new")
1792 do jn = 0, ntrunc
1793 ResponseFunction(jn) = spf_filterResponseFunction(dble(jn),waveBandIndex_opt, waveBandPeaks, nWaveBand)
1794 if ( jn /= 0) then
1795 waveLength=2*MPC_PI_R8*ec_ra/dble(jn)
1796 else
1797 waveLength=0.d0
1798 end if
1799 write(* ,'(I4,2X,F7.1,2X,F5.3)') jn, waveLength/1000.d0, ResponseFunction(jn)
1800 write(99,'(I4,2X,F7.1,2X,F5.3)') jn, waveLength/1000.d0, ResponseFunction(jn)
1801 end do
1802 close(unit=99)
1803 end if
1804
1805 do ensIndex=1,nens
1806 member(:,:,:)=dble(ensPerturbations(:,:,:,ensIndex))
1807 if(nlev.eq.nkgdimEns) then
1808 call gst_setID(gstID_nkgdimEns)
1809 else if(nlev.eq.nLevEns_T+1) then
1810 call gst_setID(gstID_nLevEns_T_P1)
1811 else
1812 write(*,*) 'spectralFilter: nlev = ',nlev
1813 call utl_abort('spectralFilter: spectral transform not initialized for this number of levels')
1814 end if
1815 call gst_reespe(spectralState,member)
1816 if ( nWaveBand /= 1 ) then
1817 !$OMP PARALLEL DO PRIVATE (jk,jn,jm,ila_mpilocal,ila_mpiglobal)
1818 do jk = 1, nlev
1819 do jn = mynBeg, mynEnd, mynSkip
1820 do jm = mymBeg, mymEnd, mymSkip
1821 if(jm.le.jn) then
1822 ila_mpiglobal = gst_getNind(jm,gstID_nkgdimEns) + jn - jm
1823 ila_mpilocal = ilaList_mpilocal(ila_mpiglobal)
1824 spectralState(ila_mpilocal,1,jk) = spectralState(ila_mpilocal,1,jk) * ResponseFunction(jn)
1825 spectralState(ila_mpilocal,2,jk) = spectralState(ila_mpilocal,2,jk) * ResponseFunction(jn)
1826 end if
1827 end do
1828 end do
1829 end do
1830 !$OMP END PARALLEL DO
1831 end if
1832 call gst_speree(spectralState,member)
1833 ensPerturbations(:,:,:,ensIndex)=sngl(member(:,:,:))
1834 end do
1835
1836 if ( nWaveBand /= 1 ) then
1837 deallocate(ResponseFunction)
1838 end if
1839
1840 write(*,*) 'finished applying spectral filter...'
1841
1842 end subroutine spectralFilter
1843
1844 !--------------------------------------------------------------------------
1845 ! SPECTRALFILTER2
1846 !--------------------------------------------------------------------------
1847 subroutine spectralFilter2(ensPerts_in,ensPerts_out,waveBandIndex_opt)
1848 !
1849 !:Purpose: Apply a spectral filter
1850 !
1851 implicit none
1852
1853 ! Arguments:
1854 type(struct_ens), intent(inout) :: ensPerts_in
1855 type(struct_ens), intent(inout) :: ensPerts_out
1856 integer, optional, intent(in) :: waveBandIndex_opt
1857
1858 ! Locals:
1859 real(8), allocatable :: ensPertSP(:,:,:)
1860 real(8), allocatable :: ensPertGD(:,:,:)
1861 real(4), pointer :: ptr4d_r4(:,:,:,:)
1862 integer :: memberIndex, levIndex, latIndex, lonIndex
1863 integer :: jn, jm, ila_mpilocal, ila_mpiglobal
1864 integer :: gstFilterID
1865 real(8), allocatable :: ResponseFunction(:)
1866 real(8) :: waveLength
1867 character(len=128) :: outfilename
1868 character(len=2) :: wbnum
1869
1870 !
1871 !- 1. Pre-compute the response function (if needed)
1872 !
1873 if ( nWaveBand /= 1 ) then
1874 write(*,*) 'Bandpass filtering step'
1875 if (.not. present(waveBandIndex_opt)) then
1876 write(*,*) 'Error: No waveBandIndex was supplied!!!'
1877 call utl_abort('calbmatrix_glb: spectralFilter2')
1878 end if
1879 allocate(ResponseFunction(0:ntrunc))
1880 write(wbnum,'(I2.2)') waveBandIndex_opt
1881 outfilename = "./ResponseFunction_"//wbnum//".txt"
1882 if (mmpi_myid == 0) then
1883 open (unit=99,file=outfilename,action="write",status="new")
1884 end if
1885 do jn = 0, ntrunc
1886 ResponseFunction(jn) = spf_filterResponseFunction(dble(jn),waveBandIndex_opt, waveBandPeaks, nWaveBand)
1887 if ( jn /= 0) then
1888 waveLength=2*MPC_PI_R8*ec_ra/dble(jn)
1889 else
1890 waveLength=0.d0
1891 end if
1892 write(* ,'(I4,2X,F7.1,2X,F5.3)') jn, waveLength/1000.d0, ResponseFunction(jn)
1893 if (mmpi_myid == 0) then
1894 write(99,'(I4,2X,F7.1,2X,F5.3)') jn, waveLength/1000.d0, ResponseFunction(jn)
1895 end if
1896 end do
1897 if (mmpi_myid == 0) then
1898 close(unit=99)
1899 end if
1900 end if
1901
1902 !
1903 !- 2. Apply Filter
1904 !
1905 gstFilterID = gst_setup(ni,nj,nTrunc,nEnsOverDimension)
1906
1907 allocate(ensPertSP(nla_mpilocal,2,nEnsOverDimension))
1908 allocate(ensPertGD(nEnsOverDimension,myLonBeg:myLonEnd,myLatBeg:myLatEnd))
1909 ensPertSP(:,:,:) = 0.0d0
1910
1911 do levIndex = 1, ens_getNumK(ensPerts_in) ! Loop on variables and vertical levels
1912
1913 ptr4d_r4 => ens_getOneLev_r4(ensPerts_in,levIndex)
1914
1915 do latIndex = myLatBeg, myLatEnd
1916 ensPertGD(:,:,latIndex) = 0.0d0
1917 end do
1918
1919 !$OMP PARALLEL DO PRIVATE (memberIndex,latIndex,lonIndex)
1920 do latIndex = myLatBeg, myLatEnd
1921 do lonIndex = myLonBeg, myLonEnd
1922 do memberIndex = 1, nEns
1923 ensPertGD(memberIndex,lonIndex,latIndex) = dble(ptr4d_r4(memberIndex,1,lonIndex,latIndex))
1924 end do
1925 end do
1926 end do
1927 !$OMP END PARALLEL DO
1928
1929 !- GridPoint space -> Spectral Space
1930 call gst_setID(gstFilterID) ! IN
1931 call gst_reespe_kij(ensPertSP, & ! OUT
1932 ensPertGD) ! IN
1933
1934 !- Filtering
1935 if ( nWaveBand /= 1 ) then
1936 !$OMP PARALLEL DO PRIVATE (memberIndex,jn,jm,ila_mpilocal,ila_mpiglobal)
1937 do memberIndex = 1, nEns
1938 do jn = mynBeg, mynEnd, mynSkip
1939 do jm = mymBeg, mymEnd, mymSkip
1940 if (jm <= jn) then
1941 ila_mpiglobal = gst_getNind(jm,gstFilterID) + jn - jm
1942 ila_mpilocal = ilaList_mpilocal(ila_mpiglobal)
1943 ensPertSP(ila_mpilocal,1,memberIndex) = ensPertSP(ila_mpilocal,1,memberIndex) * ResponseFunction(jn)
1944 ensPertSP(ila_mpilocal,2,memberIndex) = ensPertSP(ila_mpilocal,2,memberIndex) * ResponseFunction(jn)
1945 end if
1946 end do
1947 end do
1948 end do
1949 !$OMP END PARALLEL DO
1950 end if
1951
1952 ! Spectral Space -> GridPoint space
1953 call gst_setID(gstFilterID) ! IN
1954 call gst_speree_kij(ensPertSP, & ! IN
1955 ensPertGD) ! OUT
1956
1957 ptr4d_r4 => ens_getOneLev_r4(ensPerts_out,levIndex)
1958
1959 !$OMP PARALLEL DO PRIVATE (memberIndex,latIndex,lonIndex)
1960 do latIndex = myLatBeg, myLatEnd
1961 do lonIndex = myLonBeg, myLonEnd
1962 do memberIndex = 1, nEns
1963 ptr4d_r4(memberIndex,1,lonIndex,latIndex) = sngl(ensPertGD(memberIndex,lonIndex,latIndex))
1964 end do
1965 end do
1966 end do
1967 !$OMP END PARALLEL DO
1968
1969 end do
1970
1971 if ( nWaveBand /= 1 ) then
1972 deallocate(ResponseFunction)
1973 end if
1974 deallocate(ensPertGD)
1975 deallocate(ensPertSP)
1976
1977 write(*,*) 'finished applying spectral filter...'
1978
1979 end subroutine spectralFilter2
1980
1981 !--------------------------------------------------------------------------
1982 ! CALCTHETA
1983 !--------------------------------------------------------------------------
1984 subroutine calcTheta(ensPerturbations,theta)
1985 !
1986 !:Purpose: Calculate the Theta turning angle according to Ekman balance
1987 !
1988 implicit none
1989
1990 ! Arguments:
1991 real(4), pointer, intent(in) :: ensPerturbations(:,:,:,:)
1992 real(8), intent(out) :: theta(:,:)
1993
1994 ! Locals:
1995 real(8) :: zchipsi(nLevEns_M,nj), zpsipsi(nLevEns_M,nj)
1996 real(8) :: zchipsi_mpiglobal(nLevEns_M,nj), zpsipsi_mpiglobal(nLevEns_M,nj)
1997 real(4), pointer :: psi_ptr(:,:,:), chi_ptr(:,:,:)
1998 integer :: latIndex,lonIndex,levIndex,ensIndex, ierr, nsize
1999
2000 theta(:,:) = 0.0d0
2001 zchipsi(:,:) = 0.0d0
2002 zpsipsi(:,:) = 0.0d0
2003 zchipsi_mpiglobal(:,:) = 0.0d0
2004 zpsipsi_mpiglobal(:,:) = 0.0d0
2005
2006 do ensIndex = 1,nens
2007 psi_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,1:nlevEns_M,ensIndex)
2008 chi_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,(nlevEns_M+1):(2*nlevEns_M),ensIndex)
2009
2010 ! update zchipsi and zpsipsi covariances
2011 do latIndex = myLatBeg, myLatEnd
2012 do lonIndex = myLonBeg, myLonEnd
2013 do levIndex = 1, nLevEns_M
2014 zpsipsi(levIndex,latIndex) = zpsipsi(levIndex,latIndex) + &
2015 psi_ptr(lonIndex,latIndex,levIndex) * psi_ptr(lonIndex,latIndex,levIndex)
2016 zchipsi(levIndex,latIndex) = zchipsi(levIndex,latIndex) + &
2017 chi_ptr(lonIndex,latIndex,levIndex) * psi_ptr(lonIndex,latIndex,levIndex)
2018 end do
2019 end do
2020 end do
2021 end do
2022
2023 nsize = nLevEns_M*nj
2024 call rpn_comm_allreduce(zchipsi,zchipsi_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2025 call rpn_comm_allreduce(zpsipsi,zpsipsi_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2026
2027 ! calculate THETA
2028 do latIndex = 1, nj
2029 do levIndex = 1, nLevEns_M
2030 theta(levIndex,latIndex) = atan(-zchipsi_mpiglobal(levIndex,latIndex) / &
2031 zpsipsi_mpiglobal(levIndex,latIndex))
2032 end do
2033 end do
2034
2035 write(*,*) 'finished computing theta...'
2036
2037 end subroutine calcTheta
2038
2039 !--------------------------------------------------------------------------
2040 ! CALCPTOT
2041 !--------------------------------------------------------------------------
2042 subroutine calcPtoT(ensPerturbations,PtoT)
2043 !
2044 !:Purpose: Calculate the "P" to Temperature transform matrix using
2045 ! a regression analysis of the "P" and temperature samples
2046 !
2047 implicit none
2048
2049 ! Arguments:
2050 real(4), pointer, intent(in) :: ensPerturbations(:,:,:,:)
2051 real(8), intent(out) :: PtoT(:,:,:)
2052
2053 ! Locals:
2054 real(8) :: spectralState(nla_mpilocal,2,nLevEns_M), spBalancedP(nla_mpilocal,2,nlevEns_M)
2055 real(8) :: balancedP(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nlevEns_M)
2056 real(8) :: psi(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nLevEns_M)
2057 real(4), pointer :: tt_ptr(:,:,:),ps_ptr(:,:)
2058 INTEGER :: ensIndex, JK1, JK2, nsize
2059 INTEGER :: IERR, JK, latIndex, lonIndex, JB, JPNLATBND
2060 PARAMETER (JPNLATBND = 3)
2061 REAL(8) :: ZFACT,zlat(nj)
2062 REAL(8) :: ZFACTTOT
2063 REAL(8) :: ZM1(NLEVENS_T+1,NLEVENS_M,JPNLATBND), ZM2(NLEVPTOT,NLEVPTOT,JPNLATBND)
2064 REAL(8) :: ZM1_mpiglobal(NLEVENS_T+1,NLEVENS_M,JPNLATBND), ZM2_mpiglobal(NLEVPTOT,NLEVPTOT,JPNLATBND)
2065 REAL(8) :: ZPTOTBND(NLEVENS_T+1,NLEVENS_M)
2066 REAL(8) :: ZM2INV(NLEVPTOT,NLEVPTOT,JPNLATBND)
2067 REAL(8) :: DLA2, DL1SA2
2068 REAL(8) :: DLLATMIN(JPNLATBND), DLLATMAX(JPNLATBND)
2069 real(8) :: zeigwrk(4*nlevPtoT),zeigen(nlevPtoT,nlevPtoT),zeigenv(nlevPtoT)
2070 real(8) :: zeigenvi(nlevPtoT)
2071 integer :: iwork,info
2072
2073 DATA DLLATMIN / -60.0D0, -30.0D0, 30.0D0 /
2074 DATA DLLATMAX / -30.0D0, 30.0D0, 60.0D0 /
2075
2076 DLA2 = ec_ra * ec_ra
2077 DL1SA2 = 1.D0/DLA2
2078
2079 ! 1. Initialize P_to_T, ZM1, ZM2
2080
2081 ZFACTTOT = 0.0D0
2082 DO latIndex = 1, nj
2083 ZFACTTOT = ZFACTTOT + cos(GST_GETRLATI(latIndex))
2084 END DO
2085 ZFACTTOT = NJ/ZFACTTOT
2086
2087 PtoT(:,:,:) = 0.0d0
2088 ZM1(:,:,:) = 0.0d0
2089 ZM2(:,:,:) = 0.0d0
2090 ZPTOTBND(:,:) = 0.0d0
2091 ZM1_mpiglobal(:,:,:) = 0.0d0
2092 ZM2_mpiglobal(:,:,:) = 0.0d0
2093
2094 do ensIndex = 1, nens
2095
2096 write(*,*) 'calcPtoT: processing member ',ensIndex
2097
2098 psi(:,:,:) = ensPerturbations(:,:,1:nlevEns_M,ensIndex)
2099 call gst_setID(gstID_nLevEns_M)
2100 call gst_reespe(spectralState,psi)
2101 call calcBalancedP(spectralState,spBalancedP)
2102 call gst_speree(spBalancedP,balancedP)
2103
2104 tt_ptr(myLonBeg:,myLatBeg:,1:) => ensPerturbations(:,:,(2*nLevEns_M+1):(2*nLevEns_M+nLevEns_T),ensIndex)
2105 ps_ptr(myLonBeg:,myLatBeg:) => ensPerturbations(:,:,2*nLevEns_M+2*nLevEns_T+1,ensIndex)
2106
2107 do latIndex = myLatBeg, myLatEnd
2108 zlat(latIndex)=GST_GETRLATI(latIndex)
2109 end do
2110
2111 !$OMP PARALLEL DO PRIVATE (JK1,JB,latIndex,ZFACT,lonIndex,JK2)
2112 DO JK1 = 1, (nLevEns_T+1)
2113 DO JB=1,JPNLATBND
2114 DO latIndex = myLatBeg, myLatEnd
2115 if ((ZLAT(latIndex) .gt. 2.D0*MPC_PI_R8*DLLATMIN(JB)/360.D0) .and. &
2116 (ZLAT(latIndex) .le. 2.D0*MPC_PI_R8*DLLATMAX(JB)/360.D0)) then
2117 ZFACT = cos(ZLAT(latIndex))*ZFACTTOT
2118 DO lonIndex = myLonBeg, myLonEnd
2119
2120 ! update ZM1 = sum_over_t_x_y[vec(T lnPs) vec(P_b)^T]
2121 DO JK2 = 1, nLevEns_M
2122 IF(JK1.LE.nLevEns_T) THEN
2123 zm1(jk1,jk2,jb) = zm1(jk1,jk2,jb) + zfact * tt_ptr(lonIndex,latIndex,jk1) * balancedP(lonIndex,latIndex,jk2)
2124 ELSE
2125 zm1(jk1,jk2,jb) = zm1(jk1,jk2,jb) + zfact * ps_ptr(lonIndex,latIndex) * balancedP(lonIndex,latIndex,jk2)
2126 END IF
2127 END DO
2128
2129 ! update ZM2 = sum_over_t_x_y[vec(P_b) vec(P_b)^T]
2130 IF(JK1.LE.NLEVPTOT) THEN
2131 DO JK2 = 1, NLEVPTOT
2132 zm2(jk1,jk2,jb) = zm2(jk1,jk2,jb) + zfact * balancedP(lonIndex,latIndex,jk1) * balancedP(lonIndex,latIndex,jk2)
2133 END DO
2134 END IF
2135
2136 END DO
2137 end if
2138 END DO
2139 END DO ! Loop on JPNLATBND
2140 END DO ! Loop on JK1
2141 !$OMP END PARALLEL DO
2142
2143 END DO
2144
2145 ! communicate matrices to have global result on all tasks
2146 nsize = (NLEVENS_T+1)*NLEVENS_M*JPNLATBND
2147 call rpn_comm_allreduce(zm1,zm1_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2148 nsize = NLEVPTOT*NLEVPTOT*JPNLATBND
2149 call rpn_comm_allreduce(zm2,zm2_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2150
2151 ! SET ZM1_MPIGLOBAL, ZM2_MPIGLOBAL EQUAL FOR ALL THREE REGIONS
2152 DO JK1 = 1, NLEVPTOT
2153 DO JK2 = 1, NLEVPTOT
2154 ZM2_MPIGLOBAL(JK1,JK2,1)=ZM2_MPIGLOBAL(JK1,JK2,1)+ZM2_MPIGLOBAL(JK1,JK2,3)
2155 ZM2_MPIGLOBAL(JK1,JK2,2)=ZM2_MPIGLOBAL(JK1,JK2,1)
2156 ZM2_MPIGLOBAL(JK1,JK2,3)=ZM2_MPIGLOBAL(JK1,JK2,1)
2157 END DO
2158 END DO
2159 DO JK1 = 1, (nLevEns_T+1)
2160 DO JK2 = 1, NLEVPTOT
2161 ZM1_MPIGLOBAL(JK1,JK2,1)=ZM1_MPIGLOBAL(JK1,JK2,1)+ZM1_MPIGLOBAL(JK1,JK2,3)
2162 ZM1_MPIGLOBAL(JK1,JK2,2)=ZM1_MPIGLOBAL(JK1,JK2,1)
2163 ZM1_MPIGLOBAL(JK1,JK2,3)=ZM1_MPIGLOBAL(JK1,JK2,1)
2164 END DO
2165 END DO
2166
2167 DO JK1=1,NLEVPTOT
2168 DO JK2=1,NLEVPTOT
2169 ZEIGEN(JK1,JK2)=ZM2_MPIGLOBAL(JK1,JK2,1)
2170 END DO
2171 END DO
2172 IWORK=4*NLEVPTOT
2173 CALL DSYEV('V','U',NLEVPTOT,ZEIGEN,NLEVPTOT,ZEIGENV,ZEIGWRK,IWORK,INFO)
2174
2175 write(*,*) 'calcPtot: info=',info
2176 write(*,*) 'calcPtot: eigen values=',zeigenv(:)
2177
2178 do JK1=1,NLEVPTOT
2179 if (ZEIGENV(JK1).gt.0.0d0) then
2180 ZEIGENVI(JK1)=1.0d0/ZEIGENV(JK1)
2181 else
2182 ZEIGENVI(JK1)=0.0d0
2183 end if
2184 end do
2185
2186 DO JK1=1,NLEVPTOT
2187 DO JK2=1,NLEVPTOT
2188 ZM2INV(JK1,JK2,1)=0.0d0
2189 DO JK=1,NLEVPTOT
2190 ZM2INV(JK1,JK2,1)=ZM2INV(JK1,JK2,1)+ZEIGEN(JK1,JK)*ZEIGENVI(JK)*ZEIGEN(JK2,JK)
2191 END DO
2192 END DO
2193 END DO
2194
2195
2196 ! Calculate A = ZM1_MPIGLOBAL*inv(ZM2)
2197 DO JK1 = 1, (nLevEns_T+1)
2198 DO JK2 = 1, NLEVPTOT
2199 DO JK = 1, NLEVPTOT
2200 ZPTOTBND(JK1,JK2) = ZPTOTBND(JK1,JK2) + ZM1_MPIGLOBAL(JK1,JK,1) * ZM2INV(JK,JK2,1)
2201 END DO
2202 END DO
2203 END DO
2204
2205 DO JK1 = 1, nLevEns_T+1
2206 DO JK2 = 1, NLEVPTOT
2207 DO latIndex = 1, nj
2208 PTOT(JK1,JK2,latIndex) = ZPTOTBND(JK1,JK2)
2209 END DO
2210 END DO
2211 END DO
2212
2213 write(*,*) 'finished computing PtoT...'
2214
2215 end subroutine calcPtoT
2216
2217 !--------------------------------------------------------------------------
2218 ! REMOVEGLOBALMEAN
2219 !--------------------------------------------------------------------------
2220 subroutine removeGlobalMean(ensPerturbations)
2221 !
2222 !:Purpose: Calculate and subtract the horizontal mean value from the ensemble
2223 ! of samples for each member, vertical level and variable
2224 !
2225 implicit none
2226
2227 ! Arguments:
2228 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
2229
2230 ! Locals:
2231 integer :: lonIndex,latIndex,levIndex,ensIndex,ierr
2232 real(8) :: dmean, dmean_mpiglobal
2233
2234 do ensIndex = 1, nens
2235 do levIndex = 1, nkgdimEns
2236 dmean = 0.0d0
2237 do latIndex = myLatBeg, myLatEnd
2238 do lonIndex = myLonBeg, myLonEnd
2239 dmean = dmean + ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)
2240 end do
2241 end do
2242 call rpn_comm_allreduce(dmean, dmean_mpiglobal,1,"mpi_double_precision","mpi_sum","GRID",ierr)
2243 dmean_mpiglobal = dmean_mpiglobal/(dble(ni)*dble(nj))
2244 do latIndex = myLatBeg, myLatEnd
2245 do lonIndex = myLonBeg, myLonEnd
2246 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex) = &
2247 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex) - dmean_mpiglobal
2248 end do
2249 end do
2250 end do
2251 end do
2252
2253 write(*,*) 'finished removing global mean...'
2254
2255 end subroutine removeGlobalMean
2256
2257 !--------------------------------------------------------------------------
2258 ! CALCZONAVG
2259 !--------------------------------------------------------------------------
2260 subroutine calcZonAvg(fieldsZonAvg_mpiglobal,fields3D,nlev)
2261 !
2262 !:Purpose: Calculate the zonal average of the supplied 3D fields
2263 !
2264 implicit none
2265
2266 ! Arguments:
2267 real(8), pointer, intent(inout) :: fieldsZonAvg_mpiglobal(:,:)
2268 real(8), pointer, intent(in) :: fields3D(:,:,:)
2269 integer, intent(in) :: nlev
2270
2271 ! Locals:
2272 integer :: lonIndex, latIndex, levIndex, ierr, nsize
2273 real(8) :: dfact
2274 real(8), allocatable :: fieldsZonAvg(:,:)
2275
2276 allocate(fieldsZonAvg(nlev,nj))
2277 fieldsZonAvg(:,:)=0.0d0
2278
2279 dfact=1.0d0/dble(ni)
2280 !$OMP PARALLEL DO PRIVATE (levIndex,latIndex,lonIndex)
2281 do levIndex = 1, nlev
2282 do latIndex = myLatBeg, myLatEnd
2283 do lonIndex = myLonBeg, myLonEnd
2284 fieldsZonAvg(levIndex,latIndex) = fieldsZonAvg(levIndex,latIndex) + &
2285 dfact*fields3D(lonIndex,latIndex,levIndex)
2286 end do
2287 end do
2288 end do
2289 !$OMP END PARALLEL DO
2290
2291 ! combine info from all mpi tasks
2292 nsize = nlev*nj
2293 call rpn_comm_allreduce(fieldsZonAvg,fieldsZonAvg_mpiglobal,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2294
2295 deallocate(fieldsZonAvg)
2296
2297 write(*,*) 'finished computing the zonal average...'
2298
2299 end subroutine calcZonAvg
2300
2301 !--------------------------------------------------------------------------
2302 ! CALCSTDDEV3D
2303 !--------------------------------------------------------------------------
2304 subroutine calcStddev3d(ensPerturbations,stddev3d,nlev)
2305 !
2306 !:Purpose: Calculate the 3d stddev field
2307 !
2308 implicit none
2309
2310 ! Arguments:
2311 real(8), pointer, intent(inout) :: stddev3d(:,:,:)
2312 real(4), pointer, intent(in) :: ensPerturbations(:,:,:,:)
2313 integer, intent(in) :: nlev
2314
2315 ! Locals:
2316 integer :: lonIndex,latIndex,levIndex,ensIndex
2317 real(8) :: dnens
2318
2319 write(*,*) 'started computing the stddev...'
2320 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
2321
2322 stddev3d(:,:,:) = 0.0d0
2323 dnens = 1.0d0/dble(nens-1)
2324 !$OMP PARALLEL DO PRIVATE (levIndex,ensIndex,latIndex,lonIndex)
2325 do levIndex = 1, nlev
2326 do ensIndex = 1, nens
2327 do latIndex = myLatBeg, myLatEnd
2328 do lonIndex = myLonBeg, myLonEnd
2329 stddev3d(lonIndex,latIndex,levIndex)=stddev3d(lonIndex,latIndex,levIndex)+ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)**2
2330 end do
2331 end do
2332 end do
2333 do latIndex = myLatBeg, myLatEnd
2334 do lonIndex = myLonBeg, myLonEnd
2335 if(stddev3d(lonIndex,latIndex,levIndex).gt.0.0d0) then
2336 stddev3d(lonIndex,latIndex,levIndex)=sqrt(stddev3d(lonIndex,latIndex,levIndex)*dnens)
2337 else
2338 stddev3d(lonIndex,latIndex,levIndex)=0.0d0
2339 end if
2340 end do
2341 end do
2342 end do
2343 !$OMP END PARALLEL DO
2344
2345 write(*,*) 'finished computing the stddev...'
2346
2347 end subroutine calcStddev3d
2348
2349 !--------------------------------------------------------------------------
2350 ! CALCBALANCEDP
2351 !--------------------------------------------------------------------------
2352 subroutine calcBalancedP(sppsi,spgz)
2353 !
2354 !:Purpose: Calculate the balanced "P" variable using geostrophy
2355 !
2356 implicit none
2357
2358 ! Arguments:
2359 real(8), intent(in) :: sppsi(:,:,:)
2360 real(8), intent(out) :: spgz(:,:,:)
2361
2362 ! Locals:
2363 real(8) :: spvor_mpiglobal(nla,2,nlevEns_M)
2364 real(8) :: spvor_mpiglobal2(nla,2,nlevEns_M)
2365 real(8) :: spgz_mpiglobal(nla,2,nlevEns_M)
2366 integer :: ia, ib, ji, jm, levIndex, jla_mpilocal, ila_mpiglobal
2367 integer :: ierr, nsize
2368 real(8) :: zn, zm, zenm, zenmp1, zcon, dl1sa2
2369
2370 ! convert PSI to vorticity
2371 dl1sa2 = 1.0d0/(ec_ra*ec_ra)
2372 spvor_mpiglobal(:,:,:) = 0.0d0
2373 spvor_mpiglobal2(:,:,:) = 0.0d0
2374 do levIndex = 1, nlevEns_M
2375 do jla_mpilocal = 1, nla_mpilocal
2376 ila_mpiglobal = ilaList_mpiglobal(jla_mpilocal)
2377 spvor_mpiglobal(ila_mpiglobal,1,levIndex) = sppsi(jla_mpilocal,1,levIndex)*dl1sa2*gst_getRnnp1(ila_mpiglobal)
2378 spvor_mpiglobal(ila_mpiglobal,2,levIndex) = sppsi(jla_mpilocal,2,levIndex)*dl1sa2*gst_getRnnp1(ila_mpiglobal)
2379 end do
2380 ! ensure input field is zero for spectral component (0,0)
2381 spvor_mpiglobal(1,1,levIndex) = 0.0D0
2382 spvor_mpiglobal(1,2,levIndex) = 0.0D0
2383 end do
2384
2385 nsize = nla*2*nlevEns_M
2386 call rpn_comm_allreduce(spvor_mpiglobal,spvor_mpiglobal2,nsize,"mpi_double_precision","mpi_sum","GRID",ierr)
2387
2388 ! initialize output field to zero
2389 spgz_mpiglobal(:,:,:)=0.0d0
2390
2391 ! loop over levels and zonal wavenumbers
2392 ! n.b.: at the tip of the triangle, no contributions
2393
2394 zcon = -2.D0*ec_ROmega*ec_ra**2
2395 do levIndex = 1, nlevEns_M
2396
2397 ! the base address ia will point to the spherical harmonic
2398 ! coefficient (m,m), in the input field
2399 ia = 1
2400 do jm = 0, ntrunc-1
2401 ib = ia + ntrunc - jm
2402 zm = dble(jm)
2403
2404 ! at the base, contributions from n+1 coeff only
2405 zn = zm
2406 zenmp1 = sqrt ( ((zn+1)**2-zm**2)/(4.D0*(zn+1)**2-1.D0) )
2407 spgz_mpiglobal(ia,1,levIndex)=zcon*spvor_mpiglobal2(ia+1,1,levIndex)*zenmp1/((zn+1.0D0)**2)
2408 spgz_mpiglobal(ia,2,levIndex)=zcon*spvor_mpiglobal2(ia+1,2,levIndex)*zenmp1/((zn+1.0D0)**2)
2409
2410 zn = zn+1
2411 do ji = ia+1, ib-1
2412 zenm = sqrt ( (zn**2-zm**2)/(4.D0*zn**2-1.D0) )
2413 zenmp1 = sqrt ( ((zn+1)**2-zm**2)/(4.D0*(zn+1)**2-1.D0) )
2414 spgz_mpiglobal(ji,1,levIndex)=spvor_mpiglobal2(ji-1,1,levIndex)*zenm/(zn**2)
2415 spgz_mpiglobal(ji,2,levIndex)=spvor_mpiglobal2(ji-1,2,levIndex)*zenm/(zn**2)
2416 spgz_mpiglobal(ji,1,levIndex)=zcon*(spgz_mpiglobal(ji,1,levIndex)+spvor_mpiglobal2(ji+1,1,levIndex)*zenmp1/((zn+1.0D0)**2))
2417 spgz_mpiglobal(ji,2,levIndex)=zcon*(spgz_mpiglobal(ji,2,levIndex)+spvor_mpiglobal2(ji+1,2,levIndex)*zenmp1/((zn+1.0D0)**2))
2418 zn = zn + 1.0D0
2419 end do
2420
2421 ! at the top, contributions from n-1 coeff only
2422 zenm = sqrt ( (zn**2-zm**2)/(4.D0*zn**2-1.D0) )
2423 spgz_mpiglobal(ib,1,levIndex) = zcon*spvor_mpiglobal2(ib-1,1,levIndex)*zenm/(zn**2)
2424 spgz_mpiglobal(ib,2,levIndex) = zcon*spvor_mpiglobal2(ib-1,2,levIndex)*zenm/(zn**2)
2425 ia = ib + 1
2426 end do
2427 end do
2428
2429 ! ensure correct value for mass spectral-coefficient for m=n=0
2430 do levIndex = 1, nlevens_M
2431 spgz_mpiglobal(1,1,levIndex) = 0.0D0
2432 spgz_mpiglobal(1,2,levIndex) = 0.0D0
2433 end do
2434
2435 ! copy to mpilocal output array
2436 do levIndex = 1, nlevEns_M
2437 do jla_mpilocal = 1, nla_mpilocal
2438 ila_mpiglobal = ilaList_mpiglobal(jla_mpilocal)
2439 spgz(jla_mpilocal,1,levIndex) = spgz_mpiglobal(ila_mpiglobal,1,levIndex)
2440 spgz(jla_mpilocal,2,levIndex) = spgz_mpiglobal(ila_mpiglobal,2,levIndex)
2441 end do
2442 end do
2443
2444 end subroutine calcBalancedP
2445
2446 !--------------------------------------------------------------------------
2447 ! NORMALIZED3D
2448 !--------------------------------------------------------------------------
2449 subroutine normalize3d(ensPerturbations,stddev3d)
2450 !
2451 !:Purpose: Divide the ensemble perturbations by the supplied 3d stddev field
2452 !
2453 implicit none
2454
2455 ! Arguments:
2456 real(8), pointer, intent(in) :: stddev3d(:,:,:)
2457 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
2458
2459 ! Locals:
2460 integer :: lonIndex,latIndex,levIndex,ensIndex
2461 real(8) :: dfact
2462
2463 !$OMP PARALLEL DO PRIVATE (levIndex,ensIndex,latIndex,lonIndex,DFACT)
2464 do levIndex = 1, nkgdimEns
2465 do latIndex = myLatBeg, myLatEnd
2466 do lonIndex = myLonBeg, myLonEnd
2467 if(stddev3d(lonIndex,latIndex,levIndex).gt.0.0d0) then
2468 dfact=1.0d0/stddev3d(lonIndex,latIndex,levIndex)
2469 else
2470 dfact=0.0d0
2471 end if
2472 do ensIndex = 1, nens
2473 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)=ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)*dfact
2474 end do
2475 end do
2476 end do
2477 end do
2478 !$OMP END PARALLEL DO
2479
2480 write(*,*) 'finished normalizing by stddev3D...'
2481
2482 end subroutine normalize3d
2483
2484 !--------------------------------------------------------------------------
2485 ! MULTIPLY3D
2486 !--------------------------------------------------------------------------
2487 subroutine multiply3d(ensPerturbations,stddev3d,nlev)
2488 !
2489 !:Purpose: Multiply the ensemble perturbations by the supplied 3d stddev field
2490 !
2491 implicit none
2492
2493 ! Arguments:
2494 real(8), pointer, intent(in) :: stddev3d(:,:,:)
2495 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
2496 integer, intent(in) :: nlev
2497
2498 ! Locals:
2499 integer :: lonIndex,latIndex,levIndex,ensIndex
2500
2501 !$OMP PARALLEL DO PRIVATE (levIndex,ensIndex,latIndex,lonIndex)
2502 do ensIndex = 1, nens
2503 do levIndex = 1, nlev
2504 do latIndex = myLatBeg, myLatEnd
2505 do lonIndex = myLonBeg, myLonEnd
2506 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)=ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)*stddev3d(lonIndex,latIndex,levIndex)
2507 end do
2508 end do
2509 end do
2510 end do
2511 !$OMP END PARALLEL DO
2512
2513 write(*,*) 'finished multiplying by stddev3D...'
2514
2515 end subroutine multiply3d
2516
2517 !--------------------------------------------------------------------------
2518 ! READENSEMBLE
2519 !--------------------------------------------------------------------------
2520 subroutine readEnsemble(ensPerturbations)
2521 !
2522 !:Purpose: Read the ensemble of error samples from files
2523 !
2524 implicit none
2525
2526 ! Arguments:
2527 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
2528
2529 ! Locals:
2530 integer :: lonIndex, latIndex, levIndex, ensIndex, numStep
2531 integer, allocatable :: dateStampList(:)
2532 real(4), pointer :: field_r4(:,:,:)
2533 logical :: makeBiPeriodic
2534 type(struct_ens) :: ensPerts
2535 type(struct_gsv) :: stateVector
2536
2537 write(*,*) 'Before reading the ensemble:'
2538 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
2539
2540 numStep = 1
2541 allocate(dateStampList(numStep))
2542 dateStampList(:) = -1
2543 call ens_allocate(ensPerts, nEns, numStep, hco_ens, vco_ens, dateStampList)
2544
2545 makeBiPeriodic = .false.
2546 call ens_readEnsemble(ensPerts, './ensemble', makeBiPeriodic, &
2547 containsFullField_opt=.false.)
2548
2549 call gsv_allocate(stateVector, 1, hco_ens, vco_ens, dateStampList_opt=dateStampList, &
2550 mpi_local_opt=.true., dataKind_opt=4)
2551
2552 do ensIndex = 1, nEns
2553 write(*,*) 'readEnsemble: copying over member ', ensIndex
2554 call ens_copyMember(ensPerts, stateVector, ensIndex)
2555
2556 call gsv_getField(stateVector,field_r4,'UU')
2557 do levIndex = 1, nLevEns_M
2558 do latIndex = myLatBeg, myLatEnd
2559 do lonIndex = myLonBeg, myLonEnd
2560 ensPerturbations(lonIndex,latIndex,levIndex+varLevOffset(1),ensIndex)= field_r4(lonIndex,latIndex,levIndex)
2561 end do
2562 end do
2563 end do
2564
2565 call gsv_getField(stateVector,field_r4,'VV')
2566 do levIndex = 1, nLevEns_M
2567 do latIndex = myLatBeg, myLatEnd
2568 do lonIndex = myLonBeg, myLonEnd
2569 ensPerturbations(lonIndex,latIndex,levIndex+varLevOffset(2),ensIndex)= field_r4(lonIndex,latIndex,levIndex)
2570 end do
2571 end do
2572 end do
2573
2574 call gsv_getField(stateVector,field_r4,'TT')
2575 do levIndex = 1, nLevEns_T
2576 do latIndex = myLatBeg, myLatEnd
2577 do lonIndex = myLonBeg, myLonEnd
2578 ensPerturbations(lonIndex,latIndex,levIndex+varLevOffset(3),ensIndex)= field_r4(lonIndex,latIndex,levIndex)
2579 end do
2580 end do
2581 end do
2582
2583 call gsv_getField(stateVector,field_r4,'LQ')
2584 do levIndex=1,nLevEns_T
2585 do latIndex = myLatBeg, myLatEnd
2586 do lonIndex = myLonBeg, myLonEnd
2587 ensPerturbations(lonIndex,latIndex,levIndex+varLevOffset(4),ensIndex) = field_r4(lonIndex,latIndex,levIndex)
2588 end do
2589 end do
2590 end do
2591
2592 call gsv_getField(stateVector,field_r4,'P0')
2593 do latIndex = myLatBeg, myLatEnd
2594 do lonIndex = myLonBeg, myLonEnd
2595 ensPerturbations(lonIndex,latIndex,1+varLevOffset(5),ensIndex)= field_r4(lonIndex,latIndex,1)
2596 end do
2597 end do
2598
2599 end do
2600
2601 call gsv_deallocate(stateVector)
2602 call ens_deallocate(ensPerts)
2603
2604 write(*,*) 'After reading the ensemble:'
2605 write(*,*) 'Memory Used: ',get_max_rss()/1024,'Mb'
2606
2607 write(*,*) 'finished reading ensemble members...'
2608
2609 end subroutine readEnsemble
2610
2611 !--------------------------------------------------------------------------
2612 ! UV_TO_PSICHI
2613 !--------------------------------------------------------------------------
2614 subroutine uv_to_psichi(ensPerturbations)
2615 !
2616 !:Purpose: Transform wind components to Psi and Chi
2617 !
2618 implicit none
2619
2620 ! Arguments:
2621 real(4), intent(inout) :: ensPerturbations(:,:,:,:)
2622
2623 ! Locals:
2624 integer :: ensIndex, levIndex, jla_mpilocal, ila_mpiglobal
2625 real(8) :: dla2
2626 real(8) :: spectralState(nla_mpilocal,2,nkgdimEns)
2627 real(8) :: member(myLonBeg:myLonEnd,myLatBeg:myLatend,nkgdimens)
2628
2629 ! Convert from U/V to PSI/CHI and spectrally filter all fields
2630 call utl_tmg_start(121,'--CalcStats_UVtoPsiChi')
2631 dla2 = ec_ra * ec_ra
2632 do ensIndex=1,nens
2633 write(*,*) ' doing u/v -> psi/chi and spectral filter for member ', ensIndex
2634 member(:,:,:)=dble(ensPerturbations(:,:,:,ensIndex))
2635 call gst_setID(gstID_nkgdimEns)
2636 call gst_gdsp(spectralState,member,nlevEns_M)
2637 do levIndex = 1, nlevEns_M
2638 do jla_mpilocal = 1, nla_mpilocal
2639 ila_mpiglobal = ilaList_mpiglobal(jla_mpilocal)
2640 spectralState(jla_mpilocal,1,levIndex) = spectralState(jla_mpilocal,1,levIndex) * dla2*gst_getR1snp1(ila_mpiglobal)
2641 spectralState(jla_mpilocal,2,levIndex) = spectralState(jla_mpilocal,2,levIndex) * dla2*gst_getR1snp1(ila_mpiglobal)
2642 spectralState(jla_mpilocal,1,levIndex+nlevEns_M) = spectralState(jla_mpilocal,1,levIndex+nlevEns_M) * dla2*gst_getR1snp1(ila_mpiglobal)
2643 spectralState(jla_mpilocal,2,levIndex+nlevEns_M) = spectralState(jla_mpilocal,2,levIndex+nlevEns_M) * dla2*gst_getR1snp1(ila_mpiglobal)
2644 end do
2645 end do
2646 call gst_speree(spectralState,member)
2647 ensPerturbations(:,:,:,ensIndex)=sngl(member(:,:,:))
2648 end do
2649
2650 call utl_tmg_stop(121)
2651 write(*,*) 'finished doing u/v -> psi/chi and spectral filter...'
2652
2653 end subroutine uv_to_psichi
2654
2655 !--------------------------------------------------------------------------
2656 ! REMOVEMEAN
2657 !--------------------------------------------------------------------------
2658 subroutine removeMean(ensPerturbations)
2659 !
2660 !:Purpose: Compute and subtract the ensemble mean
2661 !
2662 implicit none
2663
2664 ! Arguments:
2665 real(4), pointer, intent(inout) :: ensPerturbations(:,:,:,:)
2666
2667 ! Locals:
2668 integer :: ensIndex, levIndex, latIndex, lonIndex
2669 real(8) :: dnens, gd2d(myLonBeg:myLonEnd,myLatBeg:myLatEnd)
2670
2671 ! remove mean and divide by sqrt(2*(NENS-1)) - extra 2 is needed?
2672 dnens=1.0d0/dble(nens)
2673 !$OMP PARALLEL DO PRIVATE (levIndex,gd2d,ensIndex,latIndex,lonIndex)
2674 do levIndex = 1, nkgdimEns
2675 gd2d(:,:)=0.0d0
2676 do ensIndex = 1, nens
2677 do latIndex = myLatBeg, myLatEnd
2678 do lonIndex = myLonBeg, myLonEnd
2679 gd2d(lonIndex,latIndex)=gd2d(lonIndex,latIndex)+ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)
2680 end do
2681 end do
2682 end do
2683 do latIndex = myLatBeg, myLatEnd
2684 do lonIndex = myLonBeg, myLonEnd
2685 gd2d(lonIndex,latIndex)=gd2d(lonIndex,latIndex)*dnens
2686 end do
2687 end do
2688 do ensIndex = 1, nens
2689 do latIndex = myLatBeg, myLatEnd
2690 do lonIndex = myLonBeg, myLonEnd
2691 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)= &
2692 ensPerturbations(lonIndex,latIndex,levIndex,ensIndex)-gd2d(lonIndex,latIndex)
2693 end do
2694 end do
2695 end do
2696 end do
2697 !$OMP END PARALLEL DO
2698
2699 write(*,*) 'finished removing the ensemble mean...'
2700
2701 end subroutine removeMean
2702
2703 !--------------------------------------------------------------------------
2704 ! HORIZCORRELFUNCTION
2705 !--------------------------------------------------------------------------
2706 subroutine horizCorrelFunction(rstddev,variableType, waveBandIndex_opt)
2707 !
2708 !:Purpose: Compute homogeneous-isotropic horizontal correlation
2709 ! function from spectral variances
2710 !
2711 implicit none
2712
2713 ! Arguments:
2714 real(8), intent(in) :: rstddev(nkgdimEns,0:ntrunc)
2715 integer, intent(in) :: variableType
2716 integer,optional, intent(in) :: waveBandIndex_opt
2717
2718 ! Locals:
2719 real(8) :: spectralState(nla,2,nkgdimEns)
2720 real(8) :: gridState(ni,nj,nkgdimEns)
2721 integer :: ji, jk, jn, jm, ila, iref, jref
2722 integer :: nLevEns, nLevStart, nLevEnd, varIndex, iStart, iEnd
2723 character(len=128) :: outfilename
2724 character(len=2) :: wbnum
2725
2726 write(*,*)
2727 write(*,*) 'Computing horizontal correlation functions'
2728
2729 !
2730 !- 1. Spectral transform of a delta function (at the center of the domain)
2731 !
2732
2733 !- 1.1 Create the delta function
2734 iref = ni/2
2735 jref = nj/2
2736
2737 GridState(:,:,:) = 0.d0
2738 GridState(iref,jref,:) = 1.d0
2739
2740 !- 1.2 Adjoint of the identity (change of norm)
2741 GridState(iref,jref,:) = GridState(iref,jref,:) * real(ni,8) / gst_getRWT(jref)
2742
2743 !- 1.3 Move to spectral space
2744 call gst_setID(gstID_nkgdimEns)
2745 call gst_reespe(spectralState, & ! OUT
2746 GridState) ! IN
2747
2748 !
2749 !- 2. Apply the horizontal correlation function
2750 !
2751 !$OMP PARALLEL DO PRIVATE (jk,jn,jm,ila)
2752 do jk = 1, nkgdimEns
2753 do jn = 0, ntrunc
2754 do jm = 0, jn
2755 ila = gst_getNind(jm) + jn - jm
2756 if (jm.eq.0) then
2757 spectralState(ila,1,jk) = spectralState(ila,1,jk) * rstddev(jk,jn)**2 * 2.0d0
2758 spectralState(ila,2,jk) = 0.d0
2759 else
2760 spectralState(ila,1,jk) = spectralState(ila,1,jk) * rstddev(jk,jn)**2 * 2.0d0
2761 spectralState(ila,2,jk) = spectralState(ila,2,jk) * rstddev(jk,jn)**2 * 2.0d0
2762 end if
2763 end do
2764 end do
2765 end do
2766 !$OMP END PARALLEL DO
2767
2768 !
2769 !- 3. Move back to grid point space
2770 !
2771 call gst_setID(gstID_nkgdimEns)
2772 call gst_speree(spectralState, & ! IN
2773 GridState) ! OUT
2774
2775 !
2776 !- 4. Write to file
2777 !
2778 if (mmpi_myid == 0) then
2779 if ( nWaveBand /= 1 ) then
2780 if (.not. present(waveBandIndex_opt)) then
2781 write(*,*) 'horizCorrelFunction: No waveBandIndex was supplied!!!'
2782 call utl_abort('calbmatrix_glb')
2783 end if
2784 write(wbnum,'(I2.2)') waveBandIndex_opt
2785 end if
2786
2787 !- 4.1 2D correlation function in fst format
2788 if ( nWaveBand == 1 ) then
2789 outfilename = "./horizCorrel.fst"
2790 else
2791 outfilename = "./horizCorrel_"//wbnum//".fst"
2792 end if
2793 call write3d(GridState,outfilename,'HORIZCORFUNC',variableType)
2794
2795 !- 4.2 1D correlation function in txt format (for plotting purposes)
2796 iStart=iref
2797 iEnd=3*ni/4 ! About 10 000 km away from the center of the domain
2798
2799 do varIndex = 1, nvar3d
2800 if ( nWaveBand == 1 ) then
2801 outfilename = "./horizCorrel_"//trim(nomvar3d(varIndex,variableType))//".txt"
2802 else
2803 outfilename = "./horizCorrel_"//trim(nomvar3d(varIndex,variableType))//"_"//wbnum//".txt"
2804 end if
2805 open (unit=99,file=outfilename,action="write",status="new")
2806
2807 if(vnl_varLevelFromVarName(nomvar3d(varIndex,variableType)).eq.'MM') then
2808 nLevEns = nLevEns_M
2809 else
2810 nLevEns = nLevEns_T
2811 end if
2812 nLevStart = varLevOffset(varIndex)+ 1
2813 nLevEnd = varLevOffset(varIndex)+ nLevEns
2814
2815 do ji=iStart,iEnd
2816 do jk = nLevStart,nLevEnd
2817 if ( jk == nLevStart ) then
2818 write(99,'(I7,2X,F7.1,2X,F6.4,$)') ji-iStart, (ji-iStart)*gridSpacingInKm, GridState(ji,jref,jk)
2819 else if ( jk == nLevEnd ) then
2820 write(99,'(2X,F6.4)') GridState(ji,jref,jk) ! Saut de ligne
2821 else
2822 write(99,'(2X,F6.4,$)') GridState(ji,jref,jk)
2823 end if
2824 end do
2825 end do
2826 close(unit=99)
2827 end do
2828
2829 do varIndex = 1, nvar2d
2830 if ( nWaveBand == 1 ) then
2831 outfilename = "./horizCorrel_"//trim(nomvar2d(varIndex,variableType))//".txt"
2832 else
2833 outfilename = "./horizCorrel_"//trim(nomvar2d(varIndex,variableType))//"_"//wbnum//".txt"
2834 end if
2835 open (unit=99,file=outfilename,action="write",status="new")
2836 do ji=iStart,iEnd
2837 write(99,'(I7,2X,F7.1,2X,F6.4)') ji-iStart, (ji-iStart)*gridSpacingInKm, GridState(ji,jref,varLevOffset(nvar3d+1)+varIndex)
2838 end do
2839 close(unit=99)
2840 end do
2841
2842 end if
2843
2844 end subroutine horizCorrelFunction
2845
2846 !--------------------------------------------------------------------------
2847 ! WRITE3D
2848 !--------------------------------------------------------------------------
2849 subroutine write3d(gridpoint3d,filename,etiket_in,variableType)
2850 !
2851 !:Purpose: Write the 3D stddev fields for all variables
2852 !
2853 implicit none
2854
2855 ! Arguments:
2856 real(8), intent(in) :: gridpoint3d(:,:,:)
2857 character(len=*), intent(in) :: filename
2858 character(len=*), intent(in) :: etiket_in
2859 integer, intent(in) :: variableType
2860
2861 ! Locals:
2862 real(8) :: dfact,zbuf(ni,nj)
2863 integer latIndex,lonIndex,levIndex,ierr,varIndex,nLevEns
2864 integer fstouv,fnom,fstfrm,fclos
2865 integer ip1,ip2,ip3,idatyp,idateo,ipak,nip1_l(max(nLevEns_M,nLevens_T))
2866 integer :: nulstats
2867 character(len=12) :: etiket
2868
2869 etiket=trim(etiket_in)
2870
2871 nulstats=0
2872 ierr = fnom (nulstats,filename,'RND',0)
2873 ierr = fstouv(nulstats,'RND')
2874
2875 ipak = -32
2876 idatyp = 5
2877 ip1 = 0
2878 ip2 = 0
2879 ip3 = nens
2880 idateo = 0
2881
2882 ! do 3d variables
2883 do varIndex = 1, nvar3d
2884
2885 if(vnl_varLevelFromVarName(nomvar3d(varIndex,variableType)).eq.'MM') then
2886 nLevEns = nLevEns_M
2887 nip1_l(1:nLevEns_M)=nip1_M(1:nLevEns_M)
2888 else
2889 nLevEns = nLevEns_T
2890 nip1_l(1:nLevEns_T)=nip1_T(1:nLevEns_T)
2891 end if
2892 dfact=1.0d0
2893
2894 do levIndex = 1, nlevEns
2895 do latIndex = myLatBeg, myLatEnd
2896 do lonIndex = myLonBeg, myLonEnd
2897 zbuf(lonIndex,latIndex) = dfact*gridpoint3d(lonIndex,latIndex,varLevOffset(varIndex)+levIndex)
2898 end do
2899 end do
2900 ierr = utl_fstecr(zbuf,ipak,nulstats,idateo,0,0,ni,nj,1,nip1_l(levIndex),ip2,ip3, &
2901 'E',nomvar3d(varIndex,variableType),etiket,'G',0,0,0,0,idatyp,.true.)
2902 end do
2903
2904 end do
2905
2906 ! now do 2D variables
2907 do varIndex = 1, nvar2d
2908 !if(nomvar2d(varIndex,variableType).eq.'P0') then
2909 ! dfact=1.0d0/1.0d2
2910 !else
2911 dfact = 1.0d0
2912 !end if
2913
2914 do latIndex = myLatBeg, myLatEnd
2915 do lonIndex = myLonBeg, myLonEnd
2916 zbuf(lonIndex,latIndex) = dfact*gridpoint3d(lonIndex,latIndex,varLevOffset(nvar3d+1)+varIndex)
2917 end do
2918 end do
2919 ierr = utl_fstecr(zbuf,ipak,nulstats,idateo,0,0,ni,nj,1,0,ip2,ip3, &
2920 'E',nomvar2d(varIndex,variableType),etiket,'G',0,0,0,0,idatyp,.true.)
2921
2922 end do
2923
2924 ierr = fstfrm(nulstats)
2925 ierr = fclos (nulstats)
2926
2927 write(*,*) 'finished writing 3d array...'
2928
2929 end subroutine write3d
2930
2931 !--------------------------------------------------------------------------
2932 ! CALCHORIZSCALE
2933 !--------------------------------------------------------------------------
2934 subroutine CalcHorizScale(rstddev,variableType,waveBandIndex_opt)
2935 !
2936 !:Purpose: Calculate the horizontal correlation length scale
2937 !
2938 implicit none
2939
2940 ! Based on subroutine corrlength.ftn in the "old" var code
2941
2942 ! Arguments:
2943 real(8), intent(in) :: rstddev(nkgdimEns,0:ntrunc)
2944 integer, intent(in) :: variableType
2945 integer, optional, intent(in) :: waveBandIndex_opt
2946
2947 ! Locals:
2948 real(8) :: HorizScale(nkgdimEns)
2949 real(8), pointer :: PressureProfile(:)
2950 integer :: jk, jn, nLevEns, varIndex
2951 real(8) :: rjn, fact, temp, a, b
2952 character(len=128) :: outfilename
2953 character(len=2) :: wbnum
2954
2955 if (mmpi_myid /= 0) return
2956
2957 write(*,*)
2958 write(*,*) 'Computing horizontal correlation lengthscales'
2959
2960 !
2961 !- 1. Compute the horizontal correlation lengthscales
2962 !
2963 do jk = 1, nkgdimEns
2964 a = 0.d0
2965 b = 0.d0
2966 do jn = 0, ntrunc
2967 rjn = dble(jn)
2968 fact = (2.0d0*rjn + 1.0d0)/2.0d0
2969 temp = (rstddev(jk,jn)**2) * fact
2970 a = a + temp
2971 if (jn /= 0) then
2972 b = b - temp*rjn*(rjn+1.d0)
2973 end if
2974 end do
2975 if ( a > 0.d0 .and. b /= 0.d0 ) then
2976 HorizScale(jk) = ec_ra * sqrt(-2.0d0*a/b)
2977 else
2978 HorizScale(jk) = 0.d0
2979 end if
2980 end do
2981
2982 !
2983 !- 2. Write the results
2984 !
2985 if ( nWaveBand /= 1 ) then
2986 if (.not. present(waveBandIndex_opt)) then
2987 write(*,*) 'CalcHorizScale: No waveBandIndex was supplied!!!'
2988 call utl_abort('calbmatrix_glb')
2989 end if
2990 write(wbnum,'(I2.2)') waveBandIndex_opt
2991 end if
2992
2993 do varIndex = 1, nvar3d
2994 write(*,*)
2995 write(*,*) nomvar3d(varIndex,variableType)
2996
2997 if ( nWaveBand == 1 ) then
2998 outfilename = "./horizScale_"//trim(nomvar3d(varIndex,variableType))//".txt"
2999 else
3000 outfilename = "./horizScale_"//trim(nomvar3d(varIndex,variableType))//"_"//wbnum//".txt"
3001 end if
3002 open (unit=99,file=outfilename,action="write",status="new")
3003
3004 if(vnl_varLevelFromVarName(nomvar3d(varIndex,variableType)).eq.'MM') then
3005 nLevEns = nLevEns_M
3006 PressureProfile => pressureProfile_M
3007 else
3008 nLevEns = nLevEns_T
3009 PressureProfile => pressureProfile_T
3010 end if
3011 do jk=1,nlevEns
3012 write(* ,'(I3,2X,F6.1,2X,F6.1)') jk, PressureProfile(jk)/100.d0, HorizScale(varLevOffset(varIndex)+jk)/1000.d0
3013 write(99,'(I3,2X,F6.1,2X,F6.1)') jk, PressureProfile(jk)/100.d0, HorizScale(varLevOffset(varIndex)+jk)/1000.d0
3014 end do
3015
3016 close(unit=99)
3017 end do
3018
3019 do varIndex = 1, nvar2d
3020 write(*,*)
3021 write(*,*) nomvar2d(varIndex,variableType)
3022
3023 if ( nWaveBand == 1 ) then
3024 outfilename = "./horizScale_"//trim(nomvar2d(varIndex,variableType))//".txt"
3025 else
3026 outfilename = "./horizScale_"//trim(nomvar2d(varIndex,variableType))//"_"//wbnum//".txt"
3027 end if
3028 open (unit=99,file=outfilename,action="write",status="new")
3029
3030 write(* ,'(I3,2X,F6.1,2X,F6.1)') 1, 1010.0, HorizScale(varLevOffset(nvar3d+1)+varIndex)/1000.d0
3031 write(99,'(I3,2X,F6.1,2X,F6.1)') 1, 1010.0, HorizScale(varLevOffset(nvar3d+1)+varIndex)/1000.d0
3032
3033 close(unit=99)
3034 end do
3035
3036 end subroutine CalcHorizScale
3037
3038 !--------------------------------------------------------------------------
3039 ! WRITEPOWERSPEC
3040 !--------------------------------------------------------------------------
3041 subroutine writePowerSpec(powerSpec,variableType)
3042 !
3043 !:Purpose: Write the computed power spectrum to an ascii file
3044 !
3045 implicit none
3046
3047 ! Arguments:
3048 real(8), intent(in) :: powerSpec(nkgdimEns,0:ntrunc)
3049 integer, intent(in) :: variableType
3050
3051 ! Locals:
3052 integer :: jk, nLevEns, nLevStart, nLevEnd, varIndex, jn
3053 real(8) :: waveLength
3054 character(len=128) :: outfilename
3055
3056 if (mmpi_myid /= 0) return
3057
3058 !- Write to txt files
3059 do varIndex = 1, nvar3d
3060
3061 outfilename = "./PowerSpec_"//trim(nomvar3d(varIndex,variableType))//".txt"
3062 open (unit=99,file=outfilename,action="write",status="new")
3063
3064 if(vnl_varLevelFromVarName(nomvar3d(varIndex,variableType)).eq.'MM') then
3065 nLevEns = nLevEns_M
3066 else
3067 nLevEns = nLevEns_T
3068 end if
3069 nLevStart = varLevOffset(varIndex)+ 1
3070 nLevEnd = varLevOffset(varIndex)+ nLevEns
3071
3072 do jn = 0, ntrunc
3073 if ( jn /= 0) then
3074 waveLength=2*MPC_PI_R8*ec_ra/dble(jn)
3075 else
3076 waveLength=0.d0
3077 end if
3078 do jk = nLevStart,nLevEnd
3079 if ( jk == nLevStart ) then
3080 write(99,'(I4,2X,F7.1,2X,e10.3,$)') jn, waveLength/1000.d0, sngl(powerSpec(jk,jn))
3081 else if ( jk == nLevEnd ) then
3082 write(99,'(2X,e10.3)') sngl(powerSpec(jk,jn)) ! Saut de ligne
3083 else
3084 write(99,'(2X,e10.3,$)') sngl(powerSpec(jk,jn))
3085 end if
3086 end do
3087 end do
3088 close(unit=99)
3089 end do
3090
3091 do varIndex = 1, nvar2d
3092
3093 outfilename = "./PowerSpec_"//trim(nomvar2d(varIndex,variableType))//".txt"
3094 open (unit=99,file=outfilename,action="write",status="new")
3095 do jn = 0, ntrunc
3096 if ( jn /= 0) then
3097 waveLength=2*MPC_PI_R8*ec_ra/dble(jn)
3098 else
3099 waveLength=0.d0
3100 end if
3101 write(99,'(I4,2X,F7.1,2X,e10.3)') jn, waveLength/1000.d0, sngl(powerSpec(varLevOffset(nvar3d+1)+varIndex,jn))
3102 end do
3103 close(unit=99)
3104 end do
3105
3106 end subroutine writePowerSpec
3107
3108 !--------------------------------------------------------------------------
3109 ! calcLocalCorrelations (identical to the routine with the same name in calcstatslam)
3110 !--------------------------------------------------------------------------
3111 subroutine calcLocalCorrelations(ensPerts)
3112 !
3113 !:Purpose: Compute local horizontal correlations
3114 !
3115 implicit none
3116
3117 ! Arguments:
3118 type(struct_ens), intent(in) :: ensPerts
3119
3120 ! Locals:
3121 type(struct_gsv) :: statevector_locHorizCor
3122 type(struct_gsv) :: statevector_oneMember
3123 type(struct_gsv) :: statevector_oneMemberTiles
3124 real(8), pointer :: ptr3d_r8(:,:,:)
3125 real(8), pointer :: ptr3d_r8_oneMember(:,:,:)
3126 real(8) :: dnEns
3127 integer :: i, j, k, ens
3128 integer :: blocklength_x, blocklength_y
3129 integer :: iref_id, jref_id, iref, jref
3130 integer :: imin, imax, jmin, jmax
3131 character(len=4), pointer :: varNamesList(:)
3132 integer :: ierr, fclos, fnom, nulnam
3133
3134 ! Namelist variables
3135 integer :: blockpadding
3136 integer :: nirefpoint
3137 integer :: njrefpoint
3138
3139 NAMELIST /NAMHVCORREL_LOCAL/nirefpoint, njrefpoint, blockpadding
3140
3141 !
3142 ! To compute the local horizontal correlation for some 'reference' grid point
3143 ! ... we assume that the ensemble grid point mean was removed and that
3144 ! the ensemble values were divided by the grid point std dev.
3145 !
3146
3147 nirefpoint = 4 ! Number of reference grid point in x
3148 njrefpoint = 2 ! Number of reference grid point in y
3149 blockpadding = 4 ! Number of grid point padding between blocks (to set correlation to 0 between each block)
3150
3151 nulnam = 0
3152 ierr = fnom(nulnam,'./flnml','FTN+SEQ+R/O',0)
3153 read(nulnam,nml=NAMHVCORREL_LOCAL)
3154 if (ierr /= 0) call utl_abort('calcLocalCorrelations: Error reading namelist NAMHVCORREL_LOCAL')
3155 if (mmpi_myid == 0) write(*,nml=NAMHVCORREL_LOCAL)
3156 ierr = fclos(nulnam)
3157
3158 blocklength_x = hco_ens%ni / nirefpoint ! Horizontal correlation will be compute blocklength x blocklength gridpoint
3159 ! around each reference point
3160 blocklength_y = hco_ens%nj / njrefpoint ! Horizontal correlation will be compute blocklength x blocklength gridpoint
3161 ! around each reference point
3162
3163 nullify(varNamesList)
3164 call ens_varNamesList(varNamesList,ensPerts)
3165
3166 call gsv_allocate(statevector_locHorizCor, ens_getNumStep(ensPerts), &
3167 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3168 datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true., &
3169 mpi_distribution_opt='VarsLevs', dataKind_opt=8 )
3170
3171 call gsv_allocate(statevector_oneMemberTiles, ens_getNumStep(ensPerts), &
3172 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3173 datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true., &
3174 mpi_distribution_opt='Tiles', dataKind_opt=8 )
3175
3176 call gsv_allocate(statevector_oneMember, ens_getNumStep(ensPerts), &
3177 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3178 datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true., &
3179 mpi_distribution_opt='VarsLevs', dataKind_opt=8 )
3180
3181 call gsv_zero(statevector_locHorizCor)
3182
3183 dnEns = 1.0d0/dble(nEns-1)
3184
3185 call gsv_getField(statevector_locHorizCor,ptr3d_r8)
3186
3187 do ens = 1, nEns
3188 call ens_copyMember(ensPerts, statevector_oneMemberTiles, ens)
3189 call gsv_transposeTilesToVarsLevs(statevector_oneMemberTiles, statevector_oneMember)
3190 call gsv_getField(statevector_oneMember,ptr3d_r8_oneMember)
3191
3192 do k = statevector_locHorizCor%mykBeg, statevector_locHorizCor%mykEnd
3193 do jref_id = 1, njrefpoint
3194 do iref_id = 1, nirefpoint
3195 iref = (2*iref_id-1)*blocklength_x/2
3196 jref = (2*jref_id-1)*blocklength_y/2
3197 jmin = max(jref-(blocklength_y-blockpadding)/2,1)
3198 jmax = min(jref+(blocklength_y-blockpadding)/2,hco_ens%nj)
3199 imin = max(iref-(blocklength_x-blockpadding)/2,1)
3200 imax = min(iref+(blocklength_x-blockpadding)/2,hco_ens%ni)
3201 do j = jmin, jmax
3202 do i = imin, imax
3203 ptr3d_r8(i,j,k) = ptr3d_r8(i,j,k) + &
3204 ptr3d_r8_oneMember(i,j,k)*ptr3d_r8_oneMember(iref,jref,k)
3205 end do
3206 end do
3207 end do
3208 end do
3209 end do
3210
3211 end do
3212
3213 call gsv_scale(statevector_locHorizCor,dnEns)
3214
3215 write(*,*) 'finished computing the local horizontal correlations...'
3216
3217 !
3218 !- 4. Write to file
3219 !
3220 call gio_writeToFile(statevector_locHorizCor, './horizCorrelLocal.fst', 'HCORREL_LOC', &
3221 typvar_opt = 'E', numBits_opt = 32)
3222
3223 call gsv_deallocate(statevector_locHorizCor)
3224 call gsv_deallocate(statevector_oneMember)
3225 call gsv_deallocate(statevector_oneMemberTiles)
3226
3227 end subroutine calcLocalCorrelations
3228
3229 !--------------------------------------------------------------------------
3230 ! calcLocalVertCorrMatrix
3231 !--------------------------------------------------------------------------
3232 subroutine calcLocalVertCorrMatrix(ensPerts)
3233 !
3234 !:Purpose: Compute all vertical and between-variable local correlations
3235 !
3236 implicit none
3237
3238 ! Arguments:
3239 type(struct_ens), intent(in) :: ensPerts
3240
3241 ! Locals:
3242 type(struct_gsv) :: statevector_vertCorr
3243 type(struct_gsv) :: statevector_oneMember
3244 real(8), pointer :: ptr3d_r8(:,:,:)
3245 real(8), pointer :: ptr3d_r8_oneMember(:,:,:)
3246 real(8) :: dnEns
3247 integer :: lonIndex, latIndex, varLevIndex1, varLevIndex2, memberIndex
3248 integer :: levIndex1, dateStamp
3249 character(len=4), pointer :: varNamesList(:)
3250 character(len=12) :: etiket
3251 character(len=4) :: varName
3252 character(len=3) :: levIndexStr
3253 character(len=256) :: ensFileName, outFileName
3254
3255 ! Set the dateStamp using the first ensemble member
3256 call fln_ensfileName(ensFileName, ens_getPathName(ensPerts), memberIndex_opt=1)
3257 dateStamp = tim_getDatestampFromFile(ensFileName)
3258
3259 ! Get list of variable names in the ensemble
3260 nullify(varNamesList)
3261 call ens_varNamesList(varNamesList,ensPerts)
3262
3263 call gsv_allocate(statevector_vertCorr, ens_getNumStep(ensPerts), &
3264 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3265 datestamp_opt=dateStamp, mpi_local_opt=.true., &
3266 mpi_distribution_opt='Tiles', dataKind_opt=8 )
3267
3268 call gsv_allocate(statevector_oneMember, ens_getNumStep(ensPerts), &
3269 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3270 datestamp_opt=dateStamp, mpi_local_opt=.true., &
3271 mpi_distribution_opt='Tiles', dataKind_opt=8 )
3272
3273 dnEns = 1.0d0/dble(nEns-1)
3274
3275 call gsv_getField(statevector_vertCorr,ptr3d_r8)
3276
3277 ! Loop over all vertical levels and variables
3278 varLev1: do varLevIndex1 = statevector_vertCorr%mykBeg, statevector_vertCorr%mykEnd
3279
3280 varName = ens_getVarNameFromK(ensPerts,varLevIndex1)
3281 levIndex1 = ens_getLevFromK(ensPerts,varLevIndex1)
3282
3283 ! Compute vertical correlations relative to varLev1
3284 call gsv_zero(statevector_vertCorr)
3285 member: do memberIndex = 1, nEns
3286 call ens_copyMember(ensPerts, statevector_oneMember, memberIndex)
3287 call gsv_getField(statevector_oneMember,ptr3d_r8_oneMember)
3288 varLev2: do varLevIndex2 = statevector_vertCorr%mykBeg, statevector_vertCorr%mykEnd
3289 do latIndex = statevector_vertCorr%myLatBeg, statevector_vertCorr%myLatEnd
3290 do lonIndex = statevector_vertCorr%myLonBeg, statevector_vertCorr%myLonEnd
3291 ptr3d_r8(lonIndex,latIndex,varLevIndex2) = &
3292 ptr3d_r8(lonIndex,latIndex,varLevIndex2) + &
3293 ptr3d_r8_oneMember(lonIndex,latIndex,varLevIndex1)* &
3294 ptr3d_r8_oneMember(lonIndex,latIndex,varLevIndex2)
3295 end do
3296 end do
3297 end do varLev2
3298 end do member
3299 call gsv_scale(statevector_vertCorr,dnEns)
3300
3301 ! Write to file the correlation matrix 'row' for this value of varLev1
3302 write(levIndexStr,'(i3.3)') levIndex1
3303 etiket = 'VCOR_' // trim(varName) // levIndexStr
3304 outFileName = './vertCorr_' // trim(varName) // levIndexStr // '.fst'
3305 call gio_writeToFile(statevector_vertCorr, &
3306 trim(outFileName), etiket_in = etiket, &
3307 typvar_opt = 'E', numBits_opt = 32)
3308 write(*,*) 'calcLocalVertCorrMatrix: finished variable/level =', varName, levIndex1
3309 end do varLev1
3310
3311 write(*,*) 'calcLocalVertCorrMatrix: finished computing the local vertical correlation matrix...'
3312
3313 call gsv_deallocate(statevector_vertCorr)
3314 call gsv_deallocate(statevector_oneMember)
3315
3316 end subroutine calcLocalVertCorrMatrix
3317
3318 !--------------------------------------------------------------------------
3319 ! calcVertModesSpec
3320 !--------------------------------------------------------------------------
3321 subroutine calcVertModesSpec(ensPerts, vModes)
3322 !
3323 !:Purpose: Compute the amplitude of the ensemble perturbations after their
3324 ! projection onto the vertical modes. This is the vertical equivalent
3325 ! of power spectra in the horizontal.
3326 !
3327 implicit none
3328
3329 ! Arguments:
3330 type(struct_ens), intent(inout) :: ensPerts
3331 type(struct_vms), intent(in) :: vModes
3332
3333 ! Locals:
3334 type(struct_gsv) :: gridStateVector_oneMember
3335 real(8), allocatable :: vertModesState3d(:,:,:)
3336 real(8), pointer :: gridState3d(:,:,:)
3337 real(8), allocatable :: powerSpec(:,:)
3338 real(8), allocatable :: latWeight(:) ! Weight given to grid point in the statistic computation
3339 real(8) :: sumWeight
3340 character(len=4), pointer :: varNamesList(:)
3341 character(len=128) :: outfilename
3342 integer :: numVar, nLev, varIndex, memberIndex
3343 integer :: nMode, modeIndex, latIndex, lonIndex
3344
3345 !
3346 !- Setup
3347 !
3348 nullify(varNamesList)
3349 call ens_varNamesList(varNamesList,ensPerts)
3350 numVar = size(varNamesList)
3351
3352 allocate(powerSpec(numVar,max(nLevEns_M,nLevEns_T)))
3353 powerSpec(:,:)=0.0d0
3354
3355 call gsv_allocate(gridStateVector_oneMember, ens_getNumStep(ensPerts), &
3356 ens_getHco(ensPerts), ens_getVco(ensPerts), varNames_opt=varNamesList, &
3357 datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true., &
3358 mpi_distribution_opt='Tiles', dataKind_opt=8 )
3359
3360 allocate(latWeight(hco_ens%nj))
3361 do latIndex = 1, hco_ens%nj
3362 latWeight(latIndex) = cos(hco_ens%lat(latIndex))
3363 if (mmpi_myid == 0) then
3364 write(*,*) latIndex, hco_ens%lat(latIndex), cos(hco_ens%lat(latIndex))
3365 end if
3366 end do
3367
3368 sumWeight = 0.d0
3369 do latIndex = myLatBeg, myLatEnd
3370 do lonIndex = myLonBeg, myLonEnd
3371 sumWeight = sumWeight + latWeight(latIndex)
3372 end do
3373 end do
3374
3375 !
3376 !- Vertical modes decomposition and coefficient summation
3377 !
3378 do memberIndex = 1, nEns
3379 write(*,*) 'calcVertModesSpec, member index = ', memberIndex
3380 call ens_copyMember(ensPerts, gridStateVector_oneMember, memberIndex)
3381
3382 do varIndex = 1, numVar
3383 write(*,*) 'calcVertModesSpec, varName = ', varIndex, varNamesList(varIndex)
3384 if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))).eq.'MM') then
3385 nLev = nLevEns_M
3386 nMode = nLev
3387 else if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))).eq.'TH') then
3388 nLev = nLevEns_T
3389 nMode = nLev
3390 else
3391 if (memberIndex == 1) then
3392 write(*,*) ' Skipping this variable not on momentum or thermodynamic levels'
3393 end if
3394 cycle
3395 end if
3396
3397 nullify(gridState3d)
3398 call gsv_getField(gridStateVector_oneMember,gridState3d,varName_opt=varNamesList(varIndex))
3399
3400 if (allocated(vertModesState3d)) deallocate(vertModesState3d)
3401 allocate(vertModesState3d(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nMode))
3402
3403 call vms_transform(vModes, vertModesState3d, gridState3d, &
3404 'GridPointToVertModes', myLonBeg, myLonEnd, &
3405 myLatBeg, myLatEnd, nLev, varNamesList(varIndex))
3406
3407 !$OMP PARALLEL DO PRIVATE (modeIndex,latIndex,lonIndex)
3408 do modeIndex = 1, nMode
3409 do latIndex = myLatBeg, myLatEnd
3410 do lonIndex = myLonBeg, myLonEnd
3411 powerSpec(varIndex,modeIndex) = powerSpec(varIndex,modeIndex) &
3412 + vertModesState3d(lonIndex,latIndex,modeIndex)**2 &
3413 * latWeight(latIndex)
3414 end do
3415 end do
3416 end do
3417 !$OMP END PARALLEL DO
3418
3419 end do
3420
3421 end do
3422
3423 deallocate(vertModesState3d)
3424 call gsv_deallocate(gridStateVector_oneMember)
3425
3426 !
3427 !- Communicate between all tasks
3428 !
3429 call mmpi_allreduce_sumR8_2d(powerSpec, "GRID")
3430 call mmpi_allreduce_sumreal8scalar(sumWeight, "GRID")
3431
3432 !
3433 !- Apply the appropriate scaling
3434 !
3435 powerSpec(:,:) = powerSpec(:,:)/(dble(nEns-1)*sumWeight)
3436
3437 !
3438 !- Write to file
3439 !
3440 if (mmpi_myid == 0) then
3441 do varIndex = 1, numVar
3442
3443 if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))) /= 'MM' .and. &
3444 vnl_varLevelFromVarName(trim(varNamesList(varIndex))) /= 'TH') cycle
3445
3446 outfilename = "./vertModesSpec_"//trim(varNamesList(varIndex))//".txt"
3447 write(*,*) 'calcVertModesSpec, writing ', trim(outfilename)
3448 open (unit=99,file=outfilename,action="write",status="new")
3449
3450 if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))) == 'MM') then
3451 nMode = nLevEns_M
3452 else
3453 nMode = nLevEns_T
3454 end if
3455
3456 do modeIndex = 1, nMode
3457 write(99,'(I4,2X,E10.4)') modeIndex, powerSpec(varIndex,modeIndex)
3458 end do
3459 close(unit=99)
3460 end do
3461 end if
3462
3463 end subroutine calcVertModesSpec
3464
3465 !--------------------------------------------------------------------------
3466 ! vertModesFilter
3467 !--------------------------------------------------------------------------
3468 subroutine vertModesFilter(ensPerts_in, ensPerts_out, vModes, waveBandIndex)
3469 !
3470 !:Purpose: Filter the ensemble perturbations to isolate a given vertical
3471 ! waveband of vertical modes.
3472 !
3473 implicit none
3474
3475 ! Arguments:
3476 type(struct_ens), intent(inout) :: ensPerts_in
3477 type(struct_ens), intent(inout) :: ensPerts_out
3478 type(struct_vms), intent(in) :: vModes
3479 integer, intent(in) :: waveBandIndex
3480
3481 ! Locals:
3482 type(struct_gsv) :: gridStateVector_oneMember
3483 real(8), allocatable :: vertModesState3d(:,:,:)
3484 real(8), pointer :: gridState3d(:,:,:)
3485 real(8), allocatable :: ResponseFunction(:)
3486 character(len=128) :: outfilename
3487 character(len=2) :: wbnum
3488 character(len=4), pointer :: varNamesList(:)
3489 integer :: nMode, nModeMax, modeIndex, latIndex, lonIndex
3490 integer :: numVar, nLev, varIndex, memberIndex
3491
3492 !
3493 !- 1. Pre-compute the response function
3494 !
3495 nModeMax=max(nLevEns_M,nLevEns_T)
3496 allocate(ResponseFunction(nModeMax))
3497 write(wbnum,'(I2.2)') waveBandIndex
3498 outfilename = "./vertResponseFunction_"//wbnum//".txt"
3499 if (mmpi_myid == 0) then
3500 open (unit=99,file=outfilename,action="write",status="new")
3501 end if
3502 do modeIndex = 1, nModeMax
3503 ResponseFunction(modeIndex) = spf_filterResponseFunction(dble(modeIndex), waveBandIndex, &
3504 vertWaveBandPeaks, nVertWaveBand)
3505 write(* ,'(I4,2X,F5.3)') modeIndex, ResponseFunction(modeIndex)
3506 if (mmpi_myid == 0) then
3507 write(99,'(I4,2X,F5.3)') modeIndex, ResponseFunction(modeIndex)
3508 end if
3509 end do
3510 if (mmpi_myid == 0) then
3511 close(unit=99)
3512 end if
3513
3514 !
3515 !- 2. Apply Filter
3516 !
3517 nullify(varNamesList)
3518 call ens_varNamesList(varNamesList,ensPerts_in)
3519 numVar = size(varNamesList)
3520
3521 call gsv_allocate(gridStateVector_oneMember, ens_getNumStep(ensPerts_in), &
3522 ens_getHco(ensPerts_in), ens_getVco(ensPerts_in), varNames_opt=varNamesList, &
3523 datestamp_opt=tim_getDatestamp(), mpi_local_opt=.true., &
3524 mpi_distribution_opt='Tiles', dataKind_opt=8 )
3525
3526 do memberIndex = 1, nEns
3527 call ens_copyMember(ensPerts_in, gridStateVector_oneMember, memberIndex)
3528
3529 do varIndex = 1, numVar
3530
3531 nullify(gridState3d)
3532 call gsv_getField(gridStateVector_oneMember,gridState3d,varName_opt=varNamesList(varIndex))
3533
3534 if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))).eq.'MM') then
3535 nLev = nLevEns_M
3536 nMode = nLev
3537 else if (vnl_varLevelFromVarName(trim(varNamesList(varIndex))).eq.'TH') then
3538 nLev = nLevEns_T
3539 nMode = nLev
3540 else
3541 gridState3d(:,:,:) = 0.d0
3542 cycle
3543 end if
3544
3545 if (allocated(vertModesState3d)) deallocate(vertModesState3d)
3546 allocate(vertModesState3d(myLonBeg:myLonEnd,myLatBeg:myLatEnd,nMode))
3547
3548 !- GridPoint space -> Vertical modes space
3549 call vms_transform(vModes, vertModesState3d, gridState3d, &
3550 'GridPointToVertModes', myLonBeg, myLonEnd, &
3551 myLatBeg, myLatEnd, nLev, varNamesList(varIndex))
3552
3553 !- Filtering
3554 !$OMP PARALLEL DO PRIVATE (modeIndex,latIndex,lonIndex)
3555 do modeIndex = 1, nMode
3556 do latIndex = myLatBeg, myLatEnd
3557 do lonIndex = myLonBeg, myLonEnd
3558 vertModesState3d(lonIndex,latIndex,modeIndex) = ResponseFunction(modeIndex) * &
3559 vertModesState3d(lonIndex,latIndex,modeIndex)
3560 end do
3561 end do
3562 end do
3563 !$OMP END PARALLEL DO
3564
3565 !- Vertical modes space -> GridPoint space
3566 call vms_transform(vModes, vertModesState3d, gridState3d, &
3567 'VertModesToGridPoint', myLonBeg, myLonEnd, &
3568 myLatBeg, myLatEnd, nLev, varNamesList(varIndex))
3569
3570 end do
3571
3572 call ens_insertMember(ensPerts_out, gridStateVector_oneMember, memberIndex)
3573
3574 end do
3575
3576 deallocate(vertModesState3d)
3577 call gsv_deallocate(gridStateVector_oneMember)
3578 deallocate(ResponseFunction)
3579
3580 end subroutine vertModesFilter
3581
3582end module calcStatsGlb_mod