FV3 Bundle
type_vbal.F90
Go to the documentation of this file.
1 !----------------------------------------------------------------------
2 ! Module: type_vbal
3 ! Purpose: vertical balance derived type
4 ! Author: Benjamin Menetrier
5 ! Licensing: this code is distributed under the CeCILL-C license
6 ! Copyright © 2015-... UCAR, CERFACS, METEO-FRANCE and IRIT
7 !----------------------------------------------------------------------
8 module type_vbal
9 
10 use fckit_mpi_module, only: fckit_mpi_sum
11 !$ use omp_lib
12 use netcdf
13 use tools_const, only: msvali,msvalr
14 use tools_func, only: syminv
15 use tools_kinds, only: kind_real
16 use tools_missing, only: msi,msr,isnotmsr
17 use tools_nc, only: ncfloat
18 use tools_repro, only: infeq
19 use type_bpar, only: bpar_type
20 use type_ens, only: ens_type
21 use type_geom, only: geom_type
22 use type_io, only: io_type
23 use type_mpl, only: mpl_type
24 use type_nam, only: nam_type
25 use type_rng, only: rng_type
26 use type_samp, only: samp_type
28 
29 implicit none
30 
31 ! Vertical balance derived type
33  type(samp_type) :: samp ! Sampling
34  integer :: np ! Maximum number of neighbors
35  integer :: nc2b ! Subset Sc2 size, halo B
36  logical :: allocated ! Allocation flag
37  integer,allocatable :: h_n_s(:,:) ! Number of neighbors for the horizontal interpolation
38  integer,allocatable :: h_c2b(:,:,:) ! Index of neighbors for the horizontal interpolation
39  real(kind_real),allocatable :: h_s(:,:,:) ! Weight of neighbors for the horizontal interpolation
40  type(vbal_blk_type),allocatable :: blk(:,:) ! Vertical balance blocks
41 contains
42  procedure :: alloc => vbal_alloc
43  procedure :: dealloc => vbal_dealloc
44  procedure :: read => vbal_read
45  procedure :: write => vbal_write
46  procedure :: run_vbal => vbal_run_vbal
47  procedure :: run_vbal_tests => vbal_run_vbal_tests
48  procedure :: apply => vbal_apply
49  procedure :: apply_inv => vbal_apply_inv
50  procedure :: apply_ad => vbal_apply_ad
51  procedure :: apply_inv_ad => vbal_apply_inv_ad
52  procedure :: test_inverse => vbal_test_inverse
53  procedure :: test_adjoint => vbal_test_adjoint
54 end type vbal_type
55 
56 logical,parameter :: diag_auto = .true. ! Diagonal auto-covariance for the inversion
57 real(kind_real),parameter :: var_th = 0.8 ! Variance threshold to truncate the vertical auto-covariance spectrum
58 
59 private
60 public :: vbal_type
61 
62 contains
63 
64 !----------------------------------------------------------------------
65 ! Subroutine: vbal_alloc
66 ! Purpose: allocate vertical balance
67 !----------------------------------------------------------------------
68 subroutine vbal_alloc(vbal,mpl,nam,geom,bpar)
69 
70 implicit none
71 
72 ! Passed variables
73 class(vbal_type),intent(inout) :: vbal ! Vertical balance
74 type(mpl_type),intent(in) :: mpl ! MPI data
75 type(nam_type),intent(in) :: nam ! Namelist
76 type(geom_type),intent(in) :: geom ! Geometry
77 type(bpar_type),intent(in) :: bpar ! Block parameters
78 
79 ! Local variables
80 integer :: iv,jv
81 
82 ! Find number of neighbors
83 call msi(vbal%np)
84 if (trim(nam%diag_interp)=='bilin') then
85  ! Bilinear interpolation
86  vbal%np = 3
87 elseif (trim(nam%diag_interp)=='natural') then
88  ! Natural neighbors
89  vbal%np = 40
90 else
91  call mpl%abort('wrong interpolation type')
92 end if
93 
94 ! Allocation
95 allocate(vbal%h_n_s(geom%nc0a,geom%nl0i))
96 allocate(vbal%h_c2b(vbal%np,geom%nc0a,geom%nl0i))
97 allocate(vbal%h_S(vbal%np,geom%nc0a,geom%nl0i))
98 allocate(vbal%blk(nam%nv,nam%nv))
99 do iv=1,nam%nv
100  do jv=1,nam%nv
101  if (bpar%vbal_block(iv,jv)) then
102  call vbal%blk(iv,jv)%alloc(nam,geom,vbal%samp%nc2b,iv,jv)
103  end if
104  end do
105 end do
106 
107 ! Update allocation flag
108 vbal%allocated = .true.
109 
110 end subroutine vbal_alloc
111 
112 !----------------------------------------------------------------------
113 ! Subroutine: vbal_dealloc
114 ! Purpose: vertical balance allocation
115 !----------------------------------------------------------------------
116 subroutine vbal_dealloc(vbal,nam)
118 implicit none
119 
120 ! Passed variables
121 class(vbal_type),intent(inout) :: vbal ! Vertical balance
122 type(nam_type),intent(in) :: nam ! Namelist
123 
124 ! Local variables
125 integer :: iv,jv
126 
127 ! Release memory
128 if (allocated(vbal%h_n_s)) deallocate(vbal%h_n_s)
129 if (allocated(vbal%h_c2b)) deallocate(vbal%h_c2b)
130 if (allocated(vbal%h_S)) deallocate(vbal%h_S)
131 if (allocated(vbal%blk)) then
132  do iv=1,nam%nv
133  do jv=1,nam%nv
134  call vbal%blk(iv,jv)%dealloc
135  end do
136  end do
137  deallocate(vbal%blk)
138 end if
139 
140 ! Update allocation flag
141 vbal%allocated = .false.
142 
143 end subroutine vbal_dealloc
144 
145 !----------------------------------------------------------------------
146 ! Subroutine: vbal_read
147 ! Purpose: read vertical balance
148 !----------------------------------------------------------------------
149 subroutine vbal_read(vbal,mpl,nam,geom,bpar)
151 implicit none
152 
153 ! Passed variables
154 class(vbal_type),intent(inout) :: vbal ! Vertical balance
155 type(mpl_type),intent(in) :: mpl ! MPI data
156 type(nam_type),intent(in) :: nam ! Namelist
157 type(geom_type),intent(in) :: geom ! Geometry
158 type(bpar_type),intent(in) :: bpar ! Block parameters
159 
160 ! Local variables
161 integer :: iv,jv
162 integer :: ncid,np_id,nc0a_id,nc2b_id,nl0i_id,nl0_1_id,nl0_2_id,h_n_s_id,h_c2b_id,h_S_id,reg_id(nam%nv,nam%nv)
163 integer :: nc0a_test,nl0i_test,nl0_1_test,nl0_2_test
164 character(len=1024) :: filename
165 character(len=1024),parameter :: subr='vbal_read'
166 
167 ! Open file
168 write(filename,'(a,a,i4.4,a,i4.4,a)') trim(nam%prefix),'_vbal_',mpl%nproc,'-',mpl%myproc,'.nc'
169 call mpl%ncerr(subr,nf90_open(trim(nam%datadir)//'/'//trim(filename),or(nf90_clobber,nf90_64bit_offset),ncid))
170 
171 ! Get dimensions
172 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'np',np_id))
173 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,np_id,len=vbal%np))
174 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'nc0a',nc0a_id))
175 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nc0a_id,len=nc0a_test))
176 if (nc0a_test/=geom%nc0a) call mpl%abort('wrong dimension when reading vbal')
177 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'nc2b',nc2b_id))
178 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nc2b_id,len=vbal%samp%nc2b))
179 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'nl0i',nl0i_id))
180 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nl0i_id,len=nl0i_test))
181 if (nl0i_test/=geom%nl0i) call mpl%abort('wrong dimension when reading vbal')
182 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'nl0_1',nl0_1_id))
183 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nl0_1_id,len=nl0_1_test))
184 if (nl0_1_test/=geom%nl0) call mpl%abort('wrong dimension when reading vbal')
185 call mpl%ncerr(subr,nf90_inq_dimid(ncid,'nl0_2',nl0_2_id))
186 call mpl%ncerr(subr,nf90_inquire_dimension(ncid,nl0_2_id,len=nl0_2_test))
187 if (nl0_2_test/=geom%nl0) call mpl%abort('wrong dimension when reading vbal')
188 
189 ! Allocation
190 allocate(vbal%h_n_s(geom%nc0a,geom%nl0i))
191 allocate(vbal%h_c2b(vbal%np,geom%nc0a,geom%nl0i))
192 allocate(vbal%h_S(vbal%np,geom%nc0a,geom%nl0i))
193 allocate(vbal%blk(nam%nv,nam%nv))
194 do iv=1,nam%nv
195  do jv=1,nam%nv
196  if (bpar%vbal_block(iv,jv)) then
197  call vbal%blk(iv,jv)%alloc(nam,geom,vbal%samp%nc2b,iv,jv)
198  end if
199  end do
200 end do
201 
202 ! Get variable id
203 call mpl%ncerr(subr,nf90_inq_varid(ncid,'h_n_s',h_n_s_id))
204 call mpl%ncerr(subr,nf90_inq_varid(ncid,'h_c2b',h_c2b_id))
205 call mpl%ncerr(subr,nf90_inq_varid(ncid,'h_S',h_s_id))
206 do iv=1,nam%nv
207  do jv=1,nam%nv
208  if (bpar%vbal_block(iv,jv)) call mpl%ncerr(subr,nf90_inq_varid(ncid,trim(vbal%blk(iv,jv)%name)//'_reg',reg_id(iv,jv)))
209  end do
210 end do
211 
212 ! Read data
213 call mpl%ncerr(subr,nf90_get_var(ncid,h_n_s_id,vbal%h_n_s))
214 call mpl%ncerr(subr,nf90_get_var(ncid,h_c2b_id,vbal%h_c2b))
215 call mpl%ncerr(subr,nf90_get_var(ncid,h_s_id,vbal%h_S))
216 do iv=1,nam%nv
217  do jv=1,nam%nv
218  if (bpar%vbal_block(iv,jv)) call mpl%ncerr(subr,nf90_get_var(ncid,reg_id(iv,jv),vbal%blk(iv,jv)%reg))
219  end do
220 end do
221 
222 ! Close file
223 call mpl%ncerr(subr,nf90_close(ncid))
224 
225 end subroutine vbal_read
226 
227 !----------------------------------------------------------------------
228 ! Subroutine: vbal_write
229 ! Purpose: write vertical balance
230 !----------------------------------------------------------------------
231 subroutine vbal_write(vbal,mpl,nam,geom,bpar)
233 implicit none
234 
235 ! Passed variables
236 class(vbal_type),intent(inout) :: vbal ! Vertical balance
237 type(mpl_type),intent(in) :: mpl ! MPI data
238 type(nam_type),intent(in) :: nam ! Namelist
239 type(geom_type),intent(in) :: geom ! Geometry
240 type(bpar_type),intent(in) :: bpar ! Block parameters
241 
242 ! Local variables
243 integer :: iv,jv
244 integer :: ncid,np_id,nc0a_id,nc2b_id,nl0i_id,nl0_1_id,nl0_2_id,h_n_s_id,h_c2b_id,h_S_id
245 integer :: reg_id(nam%nv,nam%nv),auto_id(nam%nv,nam%nv),cross_id(nam%nv,nam%nv),auto_inv_id(nam%nv,nam%nv)
246 character(len=1024) :: filename
247 character(len=1024),parameter :: subr='vbal_write'
248 
249 ! Create file
250 write(filename,'(a,a,i4.4,a,i4.4,a)') trim(nam%prefix),'_vbal_',mpl%nproc,'-',mpl%myproc,'.nc'
251 call mpl%ncerr(subr,nf90_create(trim(nam%datadir)//'/'//trim(filename),or(nf90_clobber,nf90_64bit_offset),ncid))
252 
253 ! Write namelist parameters
254 call nam%ncwrite(mpl,ncid)
255 
256 ! Define dimensions
257 call mpl%ncerr(subr,nf90_def_dim(ncid,'np',vbal%np,np_id))
258 call mpl%ncerr(subr,nf90_def_dim(ncid,'nc0a',geom%nc0a,nc0a_id))
259 call mpl%ncerr(subr,nf90_def_dim(ncid,'nc2b',vbal%samp%nc2b,nc2b_id))
260 call mpl%ncerr(subr,nf90_def_dim(ncid,'nl0i',geom%nl0i,nl0i_id))
261 call mpl%ncerr(subr,nf90_def_dim(ncid,'nl0_1',geom%nl0,nl0_1_id))
262 call mpl%ncerr(subr,nf90_def_dim(ncid,'nl0_2',geom%nl0,nl0_2_id))
263 
264 ! Define variables
265 call mpl%ncerr(subr,nf90_def_var(ncid,'h_n_s',nf90_int,(/nc0a_id,nl0i_id/),h_n_s_id))
266 call mpl%ncerr(subr,nf90_put_att(ncid,h_n_s_id,'_FillValue',msvali))
267 call mpl%ncerr(subr,nf90_def_var(ncid,'h_c2b',nf90_int,(/np_id,nc0a_id,nl0i_id/),h_c2b_id))
268 call mpl%ncerr(subr,nf90_put_att(ncid,h_c2b_id,'_FillValue',msvali))
269 call mpl%ncerr(subr,nf90_def_var(ncid,'h_S',ncfloat,(/np_id,nc0a_id,nl0i_id/),h_s_id))
270 call mpl%ncerr(subr,nf90_put_att(ncid,h_s_id,'_FillValue',msvalr))
271 do iv=1,nam%nv
272  do jv=1,nam%nv
273  if (bpar%vbal_block(iv,jv)) then
274  call mpl%ncerr(subr,nf90_def_var(ncid,trim(vbal%blk(iv,jv)%name)//'_auto',ncfloat,(/nc2b_id,nl0_1_id,nl0_2_id/), &
275  & auto_id(iv,jv)))
276  call mpl%ncerr(subr,nf90_def_var(ncid,trim(vbal%blk(iv,jv)%name)//'_cross',ncfloat,(/nc2b_id,nl0_1_id,nl0_2_id/), &
277  & cross_id(iv,jv)))
278  call mpl%ncerr(subr,nf90_def_var(ncid,trim(vbal%blk(iv,jv)%name)//'_auto_inv',ncfloat,(/nc2b_id,nl0_1_id,nl0_2_id/), &
279  & auto_inv_id(iv,jv)))
280  call mpl%ncerr(subr,nf90_def_var(ncid,trim(vbal%blk(iv,jv)%name)//'_reg',ncfloat,(/nc2b_id,nl0_1_id,nl0_2_id/), &
281  & reg_id(iv,jv)))
282  end if
283  end do
284 end do
285 
286 ! End definition mode
287 call mpl%ncerr(subr,nf90_enddef(ncid))
288 
289 ! Write variables
290 call mpl%ncerr(subr,nf90_put_var(ncid,h_n_s_id,vbal%h_n_s))
291 call mpl%ncerr(subr,nf90_put_var(ncid,h_c2b_id,vbal%h_c2b))
292 call mpl%ncerr(subr,nf90_put_var(ncid,h_s_id,vbal%h_S))
293 do iv=1,nam%nv
294  do jv=1,nam%nv
295  if (bpar%vbal_block(iv,jv)) then
296  call mpl%ncerr(subr,nf90_put_var(ncid,reg_id(iv,jv),vbal%blk(iv,jv)%reg))
297  call mpl%ncerr(subr,nf90_put_var(ncid,auto_id(iv,jv),vbal%blk(iv,jv)%auto))
298  call mpl%ncerr(subr,nf90_put_var(ncid,cross_id(iv,jv),vbal%blk(iv,jv)%cross))
299  call mpl%ncerr(subr,nf90_put_var(ncid,auto_inv_id(iv,jv),vbal%blk(iv,jv)%auto_inv))
300  end if
301  end do
302 end do
303 
304 ! Close file
305 call mpl%ncerr(subr,nf90_close(ncid))
306 
307 end subroutine vbal_write
308 
309 !----------------------------------------------------------------------
310 ! Subroutine: vbal_run_vbal
311 ! Purpose: compute vertical balance
312 !----------------------------------------------------------------------
313 subroutine vbal_run_vbal(vbal,mpl,rng,nam,geom,bpar,io,ens,ensu)
315 implicit none
316 
317 ! Passed variables
318 class(vbal_type),intent(inout) :: vbal ! Vertical balance
319 type(mpl_type),intent(inout) :: mpl ! MPI data
320 type(rng_type),intent(inout) :: rng ! Random number generator
321 type(nam_type),intent(inout) :: nam ! Namelist
322 type(geom_type),intent(in) :: geom ! Geometry
323 type(bpar_type),intent(in) :: bpar ! Block parameters
324 type(io_type),intent(in) :: io ! I/O
325 type(ens_type), intent(in) :: ens ! Ensemble
326 type(ens_type),intent(inout) :: ensu ! Unbalanced ensemble
327 
328 ! Local variables
329 integer :: il0i,i_s,ic0a,ic2b,ic2,ie,ie_sub,ic0,jl0,il0,isub,ic1,ic1a,iv,jv,offset,nc1a
330 real(kind_real) :: fld(geom%nc0a,geom%nl0)
331 real(kind_real) :: auto_avg(nam%nc2,geom%nl0,geom%nl0),cross_avg(nam%nc2,geom%nl0,geom%nl0),auto_inv(geom%nl0,geom%nl0)
332 real(kind_real) :: sbuf(2*nam%nc2*geom%nl0**2),rbuf(2*nam%nc2*geom%nl0**2)
333 real(kind_real),allocatable :: list_auto(:),list_cross(:),auto_avg_tmp(:,:)
334 real(kind_real),allocatable :: fld_1(:,:),fld_2(:,:),auto(:,:,:,:),cross(:,:,:,:)
335 logical :: valid,mask_unpack(geom%nl0,geom%nl0)
336 
337 ! Setup sampling
338 write(mpl%info,'(a)') '-------------------------------------------------------------------'
339 write(mpl%info,'(a,i5,a)') '--- Setup sampling (nc1 = ',nam%nc1,')'
340 call flush(mpl%info)
341 call vbal%samp%setup_sampling(mpl,rng,nam,geom,bpar,io,ens)
342 
343 ! Compute MPI distribution, halo A
344 write(mpl%info,'(a)') '-------------------------------------------------------------------'
345 write(mpl%info,'(a)') '--- Compute MPI distribution, halos A'
346 call flush(mpl%info)
347 call vbal%samp%compute_mpi_a(mpl,nam,geom)
348 
349 ! Compute MPI distribution, halos A-B
350 write(mpl%info,'(a)') '-------------------------------------------------------------------'
351 write(mpl%info,'(a)') '--- Compute MPI distribution, halos A-B'
352 call flush(mpl%info)
353 call vbal%samp%compute_mpi_ab(mpl,nam,geom)
354 
355 ! Copy ensemble
356 call ensu%copy(ens)
357 
358 ! Allocation
359 allocate(fld_1(vbal%samp%nc1a,geom%nl0))
360 allocate(fld_2(vbal%samp%nc1a,geom%nl0))
361 allocate(auto(vbal%samp%nc1a,geom%nl0,geom%nl0,ens%nsub))
362 allocate(cross(vbal%samp%nc1a,geom%nl0,geom%nl0,ens%nsub))
363 if (.not.diag_auto) allocate(auto_avg_tmp(geom%nl0,geom%nl0))
364 call vbal%alloc(mpl,nam,geom,bpar)
365 
366 ! Initialization
367 mask_unpack = .true.
368 vbal%h_n_s = 0
369 call msi(vbal%h_c2b)
370 call msr(vbal%h_S)
371 
372 ! Get interpolation coefficients
373 do il0i=1,geom%nl0i
374  do i_s=1,vbal%samp%h(il0i)%n_s
375  ic0a = vbal%samp%h(il0i)%row(i_s)
376  vbal%h_n_s(ic0a,il0i) = vbal%h_n_s(ic0a,il0i)+1
377  vbal%h_c2b(vbal%h_n_s(ic0a,il0i),ic0a,il0i) = vbal%samp%h(il0i)%col(i_s)
378  vbal%h_S(vbal%h_n_s(ic0a,il0i),ic0a,il0i) = vbal%samp%h(il0i)%S(i_s)
379  end do
380 end do
381 
382 do iv=1,nam%nv
383  do jv=1,nam%nv
384  if (bpar%vbal_block(iv,jv)) then
385  ! Initialization
386  write(mpl%info,'(a7,a)') '','Unbalancing: '//trim(nam%varname(iv))//' with respect to unbalanced '//trim(nam%varname(jv))
387  auto = 0.0
388  cross = 0.0
389 
390  ! Loop on sub-ensembles
391  do isub=1,ensu%nsub
392  if (ensu%nsub==1) then
393  write(mpl%info,'(a10,a)',advance='no') '','Full ensemble, member:'
394  else
395  write(mpl%info,'(a10,a,i4,a)',advance='no') '','Sub-ensemble ',isub,', member:'
396  end if
397  call flush(mpl%info)
398 
399  ! Compute centered moments iteratively
400  do ie_sub=1,ensu%ne/ensu%nsub
401  write(mpl%info,'(i4)',advance='no') ie_sub
402  call flush(mpl%info)
403 
404  ! Full ensemble index
405  ie = ie_sub+(isub-1)*ensu%ne/ensu%nsub
406 
407  ! Copy all separations points
408  !$omp parallel do schedule(static) private(il0,ic1a,ic1,ic0,ic0a)
409  do il0=1,geom%nl0
410  do ic1a=1,vbal%samp%nc1a
411  ! Indice
412  ic1 = vbal%samp%c1a_to_c1(ic1a)
413 
414  if (vbal%samp%c1l0_log(ic1,il0)) then
415  ! Indice
416  ic0 = vbal%samp%c1_to_c0(ic1)
417  ic0a = geom%c0_to_c0a(ic0)
418 
419  ! Copy points
420  fld_1(ic1a,il0) = ensu%fld(ic0a,il0,iv,1,ie)
421  fld_2(ic1a,il0) = ensu%fld(ic0a,il0,jv,1,ie)
422  end if
423  end do
424  end do
425  !$omp end parallel do
426 
427  !$omp parallel do schedule(static) private(il0,jl0)
428  do il0=1,geom%nl0
429  do jl0=1,geom%nl0
430  ! Covariance
431  auto(:,jl0,il0,isub) = auto(:,jl0,il0,isub)+fld_2(:,il0)*fld_2(:,jl0)
432  cross(:,jl0,il0,isub) = cross(:,jl0,il0,isub)+fld_1(:,il0)*fld_2(:,jl0)
433  end do
434  end do
435  !$omp end parallel do
436  end do
437  write(mpl%info,'(a)') ''
438  call flush(mpl%info)
439  end do
440 
441  ! Average covariances
442  write(mpl%info,'(a10,a)',advance='no') '','Average covariances: '
443  call flush(mpl%info)
444  call mpl%prog_init(nam%nc2)
445  do ic2=1,nam%nc2
446  !$omp parallel do schedule(static) private(il0,jl0,nc1a,ic1a,ic1,valid,isub), &
447  !$omp& firstprivate(list_auto,list_cross)
448  do il0=1,geom%nl0
449  do jl0=1,geom%nl0
450  ! Allocation
451  allocate(list_auto(vbal%samp%nc1a))
452  allocate(list_cross(vbal%samp%nc1a))
453 
454  ! Fill lists
455  nc1a = 0
456  do ic1a=1,vbal%samp%nc1a
457  ! Index
458  ic1 = vbal%samp%c1a_to_c1(ic1a)
459 
460  ! Check validity
461  valid = vbal%samp%c1l0_log(ic1,il0).and.vbal%samp%c1l0_log(ic1,jl0).and.vbal%samp%vbal_mask(ic1,ic2)
462 
463  if (valid) then
464  ! Update
465  nc1a = nc1a+1
466 
467  ! Averages for diagnostics
468  list_auto(nc1a) = sum(auto(ic1a,jl0,il0,:))/real(ensu%nsub,kind_real)
469  list_cross(nc1a) = sum(cross(ic1a,jl0,il0,:))/real(ensu%nsub,kind_real)
470  end if
471  end do
472 
473  ! Average
474  if (nc1a>0) then
475  auto_avg(ic2,jl0,il0) = sum(list_auto(1:nc1a))
476  cross_avg(ic2,jl0,il0) = sum(list_cross(1:nc1a))
477  else
478  auto_avg(ic2,jl0,il0) = 0.0
479  cross_avg(ic2,jl0,il0) = 0.0
480  end if
481 
482  ! Release memory
483  deallocate(list_auto)
484  deallocate(list_cross)
485  end do
486  end do
487  !$omp end parallel do
488 
489  ! Update
490  call mpl%prog_print(ic2)
491  end do
492  write(mpl%info,'(a)') '100%'
493  call flush(mpl%info)
494 
495  ! Gather data
496  write(mpl%info,'(a10,a)') '','Gather data'
497  call flush(mpl%info)
498  if (mpl%nproc>1) then
499  ! Pack data
500  offset = 0
501  do ic2=1,nam%nc2
502  sbuf(offset+1:offset+geom%nl0**2) = pack(auto_avg(ic2,:,:),.true.)
503  offset = offset+geom%nl0**2
504  sbuf(offset+1:offset+geom%nl0**2) = pack(cross_avg(ic2,:,:),.true.)
505  offset = offset+geom%nl0**2
506  end do
507 
508  ! Reduce data
509  call mpl%f_comm%allreduce(sbuf,rbuf,fckit_mpi_sum())
510 
511  ! Unpack data
512  offset = 0
513  do ic2=1,nam%nc2
514  auto_avg(ic2,:,:) = unpack(rbuf(offset+1:offset+geom%nl0**2),mask_unpack,auto_avg(ic2,:,:))
515  offset = offset+geom%nl0**2
516  cross_avg(ic2,:,:) = unpack(rbuf(offset+1:offset+geom%nl0**2),mask_unpack,cross_avg(ic2,:,:))
517  offset = offset+geom%nl0**2
518  end do
519  end if
520 
521  ! Compute regressions
522  write(mpl%info,'(a10,a)',advance='no') '','Compute regressions: '
523  call flush(mpl%info)
524  call mpl%prog_init(vbal%samp%nc2b)
525  do ic2b=1,vbal%samp%nc2b
526  ! Global index
527  ic2 = vbal%samp%c2b_to_c2(ic2b)
528 
529  if (diag_auto) then
530  ! Diagonal inversion
531  auto_inv = 0.0
532  do il0=1,geom%nl0
533  if (auto_avg(ic2,il0,il0)>0.0) auto_inv(il0,il0) = 1.0/auto_avg(ic2,il0,il0)
534  end do
535  else
536  ! Inverse the vertical auto-covariance
537  auto_avg_tmp = auto_avg(ic2,:,:)
538  call syminv(mpl,geom%nl0,auto_avg_tmp,auto_inv)
539  end if
540 
541  ! Compute the regression
542  vbal%blk(iv,jv)%auto(ic2b,:,:) = auto_avg(ic2,:,:)
543  vbal%blk(iv,jv)%cross(ic2b,:,:) = cross_avg(ic2,:,:)
544  vbal%blk(iv,jv)%auto_inv(ic2b,:,:) = auto_inv
545  vbal%blk(iv,jv)%reg(ic2b,:,:) = matmul(cross_avg(ic2,:,:),auto_inv)
546 
547  ! Update
548  call mpl%prog_print(ic2b)
549  end do
550  write(mpl%info,'(a)') '100%'
551  call flush(mpl%info)
552  end if
553  end do
554 
555  ! Unbalance ensemble
556  if (any(bpar%vbal_block(iv,1:iv-1))) then
557  write(mpl%info,'(a10,a)',advance='no') '','Unbalance ensemble members: '
558  call flush(mpl%info)
559  do ie=1,ensu%ne
560  write(mpl%info,'(i4)',advance='no') ie
561  call flush(mpl%info)
562  do jv=1,iv-1
563  if (bpar%vbal_block(iv,jv)) then
564  fld = ensu%fld(:,:,jv,1,ie)
565  call vbal%blk(iv,jv)%apply(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld)
566  ensu%fld(:,:,iv,1,ie) = ensu%fld(:,:,iv,1,ie)-fld
567  end if
568  end do
569  end do
570  write(mpl%info,'(a)') ''
571  call flush(mpl%info)
572  end if
573 end do
574 
575 ! Write balance operator
576 call vbal%write(mpl,nam,geom,bpar)
577 
578 end subroutine vbal_run_vbal
579 
580 !----------------------------------------------------------------------
581 ! Subroutine: vbal_run_vbal_tests
582 ! Purpose: compute vertical balance tests
583 !----------------------------------------------------------------------
584 subroutine vbal_run_vbal_tests(vbal,mpl,rng,nam,geom,bpar)
586 implicit none
587 
588 ! Passed variables
589 class(vbal_type),intent(inout) :: vbal ! Vertical balance
590 type(mpl_type),intent(inout) :: mpl ! MPI data
591 type(rng_type),intent(inout) :: rng ! Random number generator
592 type(nam_type),intent(inout) :: nam ! Namelist
593 type(geom_type),intent(in) :: geom ! Geometry
594 type(bpar_type),intent(in) :: bpar ! Block parameters
595 
596 ! Test inverse
597 call vbal%test_inverse(mpl,rng,nam,geom,bpar)
598 
599 ! Test adjoint
600 call vbal%test_adjoint(mpl,rng,nam,geom,bpar)
601 
602 end subroutine vbal_run_vbal_tests
603 
604 !----------------------------------------------------------------------
605 ! Subroutine: vbal_apply
606 ! Purpose: apply vertical balance
607 !----------------------------------------------------------------------
608 subroutine vbal_apply(vbal,nam,geom,bpar,fld)
610 implicit none
611 
612 ! Passed variables
613 class(vbal_type),intent(in) :: vbal ! Vertical balance
614 type(nam_type),intent(in) :: nam ! Namelist
615 type(geom_type),intent(in) :: geom ! Geometry
616 type(bpar_type),intent(in) :: bpar ! Block parameters
617 real(kind_real),intent(inout) :: fld(geom%nc0a,geom%nl0,nam%nv) ! Source/destination vector
618 
619 ! Local variables
620 integer :: iv,jv
621 real(kind_real) :: fld_tmp(geom%nc0a,geom%nl0),fld_out(geom%nc0a,geom%nl0,nam%nv)
622 
623 ! Initialization
624 fld_out = fld
625 
626 ! Add balance component
627 do iv=1,nam%nv
628  do jv=1,nam%nv
629  if (bpar%vbal_block(iv,jv)) then
630  fld_tmp = fld(:,:,jv)
631  call vbal%blk(iv,jv)%apply(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld_tmp)
632  fld_out(:,:,iv) = fld_out(:,:,iv)+fld_tmp
633  end if
634  end do
635 end do
636 
637 ! Final copy
638 fld = fld_out
639 
640 end subroutine vbal_apply
641 
642 !----------------------------------------------------------------------
643 ! Subroutine: vbal_apply_inv
644 ! Purpose: apply inverse vertical balance
645 !----------------------------------------------------------------------
646 subroutine vbal_apply_inv(vbal,nam,geom,bpar,fld)
648 implicit none
649 
650 ! Passed variables
651 class(vbal_type),intent(in) :: vbal ! Vertical balance
652 type(nam_type),intent(in) :: nam ! Namelist
653 type(geom_type),intent(in) :: geom ! Geometry
654 type(bpar_type),intent(in) :: bpar ! Block parameters
655 real(kind_real),intent(inout) :: fld(geom%nc0a,geom%nl0,nam%nv) ! Source/destination vector
656 
657 ! Local variables
658 integer :: iv,jv
659 real(kind_real) :: fld_tmp(geom%nc0a,geom%nl0),fld_out(geom%nc0a,geom%nl0,nam%nv)
660 
661 ! Initialization
662 fld_out = fld
663 
664 ! Remove balance component
665 do iv=1,nam%nv
666  do jv=1,nam%nv
667  if (bpar%vbal_block(iv,jv)) then
668  fld_tmp = fld_out(:,:,jv)
669  call vbal%blk(iv,jv)%apply(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld_tmp)
670  fld_out(:,:,iv) = fld_out(:,:,iv)-fld_tmp
671  end if
672  end do
673 end do
674 
675 ! Final copy
676 fld = fld_out
677 
678 end subroutine vbal_apply_inv
679 
680 !----------------------------------------------------------------------
681 ! Subroutine: vbal_apply_ad
682 ! Purpose: apply adjoint vertical balance
683 !----------------------------------------------------------------------
684 subroutine vbal_apply_ad(vbal,nam,geom,bpar,fld)
686 implicit none
687 
688 ! Passed variables
689 class(vbal_type),intent(in) :: vbal ! Vertical balance
690 type(nam_type),intent(in) :: nam ! Namelist
691 type(geom_type),intent(in) :: geom ! Geometry
692 type(bpar_type),intent(in) :: bpar ! Block parameters
693 real(kind_real),intent(inout) :: fld(geom%nc0a,geom%nl0,nam%nv) ! Source/destination vector
694 
695 ! Local variables
696 integer :: iv,jv
697 real(kind_real) :: fld_tmp(geom%nc0a,geom%nl0),fld_out(geom%nc0a,geom%nl0,nam%nv)
698 
699 ! Initialization
700 fld_out = fld
701 
702 ! Add balance component
703 do iv=1,nam%nv
704  do jv=1,nam%nv
705  if (bpar%vbal_block(iv,jv)) then
706  fld_tmp = fld(:,:,iv)
707  call vbal%blk(iv,jv)%apply_ad(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld_tmp)
708  fld_out(:,:,jv) = fld_out(:,:,jv)+fld_tmp
709  end if
710  end do
711 end do
712 
713 ! Final copy
714 fld = fld_out
715 
716 end subroutine vbal_apply_ad
717 
718 !----------------------------------------------------------------------
719 ! Subroutine: vbal_apply_inv_ad
720 ! Purpose: apply inverse adjoint vertical balance
721 !----------------------------------------------------------------------
722 subroutine vbal_apply_inv_ad(vbal,nam,geom,bpar,fld)
724 implicit none
725 
726 ! Passed variables
727 class(vbal_type),intent(in) :: vbal ! Vertical balance
728 type(nam_type),intent(in) :: nam ! Namelist
729 type(geom_type),intent(in) :: geom ! Geometry
730 type(bpar_type),intent(in) :: bpar ! Block parameters
731 real(kind_real),intent(inout) :: fld(geom%nc0a,geom%nl0,nam%nv) ! Source/destination vector
732 
733 ! Local variables
734 integer :: iv,jv
735 real(kind_real) :: fld_tmp(geom%nc0a,geom%nl0),fld_out(geom%nc0a,geom%nl0,nam%nv)
736 
737 ! Initialization
738 fld_out = fld
739 
740 ! Remove balance component
741 do iv=1,nam%nv
742  do jv=1,nam%nv
743  if (bpar%vbal_block(iv,jv)) then
744  fld_tmp = fld_out(:,:,iv)
745  call vbal%blk(iv,jv)%apply_ad(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld_tmp)
746  fld_out(:,:,jv) = fld_out(:,:,jv)-fld_tmp
747  end if
748  end do
749 end do
750 
751 ! Final copy
752 fld = fld_out
753 
754 end subroutine vbal_apply_inv_ad
755 
756 !----------------------------------------------------------------------
757 ! Subroutine: vbal_test_inverse
758 ! Purpose: test vertical balance inverse
759 !----------------------------------------------------------------------
760 subroutine vbal_test_inverse(vbal,mpl,rng,nam,geom,bpar)
762 implicit none
763 
764 ! Passed variables
765 class(vbal_type),intent(in) :: vbal ! Vertical balance
766 type(mpl_type),intent(in) :: mpl ! MPI data
767 type(rng_type),intent(inout) :: rng ! Random number generator
768 type(nam_type),intent(in) :: nam ! Namelist
769 type(geom_type),intent(in) :: geom ! Geometry
770 type(bpar_type),intent(in) :: bpar ! Block parameters
771 
772 ! Local variables
773 real(kind_real) :: mse,mse_tot
774 real(kind_real),allocatable :: fld(:,:,:),fld_save(:,:,:)
775 
776 ! Allocation
777 allocate(fld(geom%nc0a,geom%nl0,nam%nv))
778 allocate(fld_save(geom%nc0a,geom%nl0,nam%nv))
779 
780 ! Generate random field
781 call rng%rand_real(0.0_kind_real,1.0_kind_real,fld_save)
782 
783 ! Direct / inverse
784 fld = fld_save
785 call vbal%apply(nam,geom,bpar,fld)
786 call vbal%apply_inv(nam,geom,bpar,fld)
787 mse = sum((fld-fld_save)**2)
788 call mpl%f_comm%allreduce(mse,mse_tot,fckit_mpi_sum())
789 write(mpl%info,'(a7,a,e15.8)') '','Vertical balance direct/inverse test: ',mse_tot
790 call flush(mpl%info)
791 
792 ! Inverse / direct
793 fld = fld_save
794 call vbal%apply_inv(nam,geom,bpar,fld)
795 call vbal%apply(nam,geom,bpar,fld)
796 mse = sum((fld-fld_save)**2)
797 call mpl%f_comm%allreduce(mse,mse_tot,fckit_mpi_sum())
798 write(mpl%info,'(a7,a,e15.8)') '','Vertical balance inverse/direct test: ',mse_tot
799 call flush(mpl%info)
800 
801 ! Direct / inverse, adjoint
802 fld = fld_save
803 call vbal%apply_ad(nam,geom,bpar,fld)
804 call vbal%apply_inv_ad(nam,geom,bpar,fld)
805 mse = sum((fld-fld_save)**2)
806 call mpl%f_comm%allreduce(mse,mse_tot,fckit_mpi_sum())
807 write(mpl%info,'(a7,a,e15.8)') '','Vertical balance direct/inverse (adjoint) test: ',mse_tot
808 call flush(mpl%info)
809 
810 ! Inverse / direct
811 fld = fld_save
812 call vbal%apply_inv_ad(nam,geom,bpar,fld)
813 call vbal%apply_ad(nam,geom,bpar,fld)
814 mse = sum((fld-fld_save)**2)
815 call mpl%f_comm%allreduce(mse,mse_tot,fckit_mpi_sum())
816 write(mpl%info,'(a7,a,e15.8)') '','Vertical balance inverse/direct (adjoint) test: ',mse_tot
817 call flush(mpl%info)
818 
819 end subroutine vbal_test_inverse
820 
821 !----------------------------------------------------------------------
822 ! Subroutine: vbal_test_adjoint
823 ! Purpose: test vertical balance adjoint
824 !----------------------------------------------------------------------
825 subroutine vbal_test_adjoint(vbal,mpl,rng,nam,geom,bpar)
827 implicit none
828 
829 ! Passed variables
830 class(vbal_type),intent(in) :: vbal ! Vertical balance
831 type(mpl_type),intent(in) :: mpl ! MPI data
832 type(rng_type),intent(inout) :: rng ! Random number generator
833 type(nam_type),intent(in) :: nam ! Namelist
834 type(geom_type),intent(in) :: geom ! Geometry
835 type(bpar_type),intent(in) :: bpar ! Block parameters
836 
837 ! Local variables
838 integer :: iv,jv
839 real(kind_real) :: sum1,sum2
840 real(kind_real),allocatable :: fld1_blk(:,:,:),fld1_dir(:,:,:),fld1_inv(:,:,:),fld1_save(:,:,:)
841 real(kind_real),allocatable :: fld2_blk(:,:,:),fld2_dir(:,:,:),fld2_inv(:,:,:),fld2_save(:,:,:)
842 
843 ! Allocation
844 allocate(fld1_dir(geom%nc0a,geom%nl0,nam%nv))
845 allocate(fld2_dir(geom%nc0a,geom%nl0,nam%nv))
846 allocate(fld1_inv(geom%nc0a,geom%nl0,nam%nv))
847 allocate(fld2_inv(geom%nc0a,geom%nl0,nam%nv))
848 allocate(fld1_save(geom%nc0a,geom%nl0,nam%nv))
849 allocate(fld2_save(geom%nc0a,geom%nl0,nam%nv))
850 
851 ! Generate random field
852 call rng%rand_real(0.0_kind_real,1.0_kind_real,fld1_save)
853 call rng%rand_real(0.0_kind_real,1.0_kind_real,fld2_save)
854 
855 ! Block adjoint test
856 fld1_blk = fld1_save
857 fld2_blk = fld2_save
858 do iv=1,nam%nv
859  do jv=1,nam%nv
860  if (bpar%vbal_block(iv,jv)) then
861  call vbal%blk(iv,jv)%apply(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld1_blk(:,:,iv))
862  call vbal%blk(iv,jv)%apply_ad(geom,vbal%np,vbal%h_n_s,vbal%h_c2b,vbal%h_S,fld2_blk(:,:,iv))
863  call mpl%dot_prod(fld1_blk(:,:,iv),fld2_save(:,:,iv),sum1)
864  call mpl%dot_prod(fld2_blk(:,:,iv),fld1_save(:,:,iv),sum2)
865  write(mpl%info,'(a7,a,e15.8,a,e15.8,a,e15.8)') '','Vertical balance block adjoint test: ', &
866  & sum1,' / ',sum2,' / ',2.0*abs(sum1-sum2)/abs(sum1+sum2)
867  call flush(mpl%info)
868  end if
869  end do
870 end do
871 
872 ! Direct adjoint test
873 fld1_dir = fld1_save
874 fld2_dir = fld2_save
875 call vbal%apply(nam,geom,bpar,fld1_dir)
876 call vbal%apply_ad(nam,geom,bpar,fld2_dir)
877 
878 ! Inverse adjoint test
879 fld1_inv = fld1_save
880 fld2_inv = fld2_save
881 call vbal%apply_inv(nam,geom,bpar,fld1_inv)
882 call vbal%apply_inv_ad(nam,geom,bpar,fld2_inv)
883 
884 ! Print result
885 call mpl%dot_prod(fld1_dir,fld2_save,sum1)
886 call mpl%dot_prod(fld2_dir,fld1_save,sum2)
887 write(mpl%info,'(a7,a,e15.8,a,e15.8,a,e15.8)') '','Vertical balance direct adjoint test: ', &
888  & sum1,' / ',sum2,' / ',2.0*abs(sum1-sum2)/abs(sum1+sum2)
889 call flush(mpl%info)
890 call mpl%dot_prod(fld1_inv,fld2_save,sum1)
891 call mpl%dot_prod(fld2_inv,fld1_save,sum2)
892 write(mpl%info,'(a7,a,e15.8,a,e15.8,a,e15.8)') '','Vertical balance inverse adjoint test: ', &
893  & sum1,' / ',sum2,' / ',2.0*abs(sum1-sum2)/abs(sum1+sum2)
894 call flush(mpl%info)
895 
896 end subroutine vbal_test_adjoint
897 
898 end module type_vbal
subroutine vbal_dealloc(vbal, nam)
Definition: type_vbal.F90:117
subroutine vbal_apply_inv(vbal, nam, geom, bpar, fld)
Definition: type_vbal.F90:647
subroutine vbal_alloc(vbal, mpl, nam, geom, bpar)
Definition: type_vbal.F90:69
subroutine vbal_test_inverse(vbal, mpl, rng, nam, geom, bpar)
Definition: type_vbal.F90:761
subroutine vbal_apply(vbal, nam, geom, bpar, fld)
Definition: type_vbal.F90:609
integer, parameter, public msvali
Definition: tools_const.F90:23
subroutine vbal_apply_ad(vbal, nam, geom, bpar, fld)
Definition: type_vbal.F90:685
logical, parameter diag_auto
Definition: type_vbal.F90:56
subroutine, public syminv(mpl, n, a, c)
Definition: tools_func.F90:793
real(kind_real), parameter var_th
Definition: type_vbal.F90:57
subroutine vbal_write(vbal, mpl, nam, geom, bpar)
Definition: type_vbal.F90:232
subroutine vbal_run_vbal(vbal, mpl, rng, nam, geom, bpar, io, ens, ensu)
Definition: type_vbal.F90:314
real(kind_real), parameter, public msvalr
Definition: tools_const.F90:24
logical function, public infeq(x, y)
Definition: tools_repro.F90:66
subroutine vbal_run_vbal_tests(vbal, mpl, rng, nam, geom, bpar)
Definition: type_vbal.F90:585
subroutine vbal_read(vbal, mpl, nam, geom, bpar)
Definition: type_vbal.F90:150
subroutine vbal_test_adjoint(vbal, mpl, rng, nam, geom, bpar)
Definition: type_vbal.F90:826
integer, parameter, public kind_real
integer, parameter, public ncfloat
Definition: tools_nc.F90:27
subroutine vbal_apply_inv_ad(vbal, nam, geom, bpar, fld)
Definition: type_vbal.F90:723