FV3 Bundle
qg_covariance_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2009-2016 ECMWF.
2 !
3 ! This software is licensed under the terms of the Apache Licence Version 2.0
4 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
5 ! In applying this licence, ECMWF does not waive the privileges and immunities
6 ! granted to it by virtue of its status as an intergovernmental organisation nor
7 ! does it submit to any jurisdiction.
8 
9 !> Structure holding configuration variables for the 3d error
10 !! covariance matrices of the QG analysis.
11 
13 
14 use kinds
15 implicit none
16 
17 !> Fortran derived type to hold configuration data for the QG background/model covariance
19  integer :: nx !< Zonal grid dimension
20  integer :: ny !< Meridional grid dimension
21  real(kind=kind_real) :: sigma !< Standard deviation
22  real(kind=kind_real) :: vert_corr !< Vertical correlation between levels
23  real(kind=kind_real), allocatable :: sqrt_merid(:,:) !< sqrt(meridional correlation matrix)
24  real(kind=kind_real), allocatable :: sqrt_inv_merid(:,:) !< Its inverse
25  real(kind=kind_real), allocatable :: sqrt_zonal(:) !< Spectral weights for sqrt(zonal corr)
26 end type qg_3d_covar_config
27 
28 #define LISTED_TYPE qg_3d_covar_config
29 
30 !> Linked list interface - defines registry_t type
31 #include "oops/util/linkedList_i.f"
32 
33 !> Global registry
34 type(registry_t) :: qg_3d_cov_registry
35 
36 ! ------------------------------------------------------------------------------
37 contains
38 ! ------------------------------------------------------------------------------
39 !> Linked list implementation
40 #include "oops/util/linkedList_c.f"
41 ! ------------------------------------------------------------------------------
42 
43 ! ------------------------------------------------------------------------------
44 
45 !> Setup for the QG model's 3d error covariance matrices (B and Q_i)
46 
47 !> This routine queries the configuration for the parameters that define the
48 !! covariance matrix, and stores the relevant values in the
49 !! error covariance structure.
50 
51 subroutine qg_3d_covar_setup(c_model, geom, config)
52 
53 use qg_constants
54 use qg_geom_mod
55 use iso_c_binding
56 use config_mod
57 use fft_mod
58 use kinds
59 use fckit_log_module, only : fckit_log
60 
61 implicit none
62 type(c_ptr), intent(in) :: c_model !< The configuration
63 type(qg_geom), intent(in) :: geom !< Geometry
64 type(qg_3d_covar_config), intent(inout) :: config !< The covariance structure
65 
66 integer :: ifax(13)
67 real(kind=kind_real), allocatable :: trigs(:), struct_fn(:), evals(:), &
68  work(:), evects(:,:), revals(:)
69 character(len=160) :: record
70 
71 real(kind=kind_real) :: corr_length_scale, dy, z, dx, condition_number, threshold
72 integer :: i, j, k, info
73 
74 config%nx = geom%nx
75 config%ny = geom%ny
76 config%sigma = config_get_real(c_model,"standard_deviation")
77 config%vert_corr = config_get_real(c_model,"vertical_correlation")
78 corr_length_scale = config_get_real(c_model,"horizontal_length_scale")
79 condition_number = config_get_real(c_model,"maximum_condition_number")
80 
81 dx = domain_zonal/real(config%nx,kind_real)
82 dy = domain_meridional/real(1+config%ny,kind_real)
83 
84 if (mod(config%nx,2)/=0) then
85  write(record,*) "c_qg_3d_covar_setup: number of zonal gridpoints nx=",config%nx
86  call fckit_log%error(record)
87  call abor1_ftn("c_qg_3d_covar_setup: odd number of zonal grid points")
88 endif
89 
90 !--- Generate the lower triangle of the meridional correlation matrix
91 
92 allocate(evects(config%ny,config%ny))
93 
94 do i=1,config%ny
95  z = (sqrt(0.5_kind_real)*dy/corr_length_scale)*real(i-1,kind_real)
96  evects(i,1) = exp(-z*z)
97 enddo
98 
99 do j=2,config%ny
100  do i=j,config%ny
101  evects(i,j) = evects(i-j+1,1)
102  enddo
103 enddo
104 
105 !--- Calculate the eigen-decomposition (NB: assuming 8-byte reals)
106 
107 allocate(evals(config%ny))
108 allocate(work((config%ny+3)*config%ny))
109 
110 call dsyev ('V','L',config%ny,evects,config%ny,evals,work,size(work),info)
111 
112 if (info/=0) then
113  write(record,*) "c_qg_3d_covar_setup: DSYEV returns info=",info
114  call fckit_log%error(record)
115  call abor1_ftn ("c_qg_3d_covar_setup: DSYEV failed to sqrt matrix")
116 endif
117 
118 deallocate(work)
119 
120 !--- Calculate the lower triangle of the symmetric square-root and its inverse
121 !--- (NB: assuming 8-byte reals)
122 
123 allocate(revals(config%ny))
124 
125 threshold = maxval(evals(:))/condition_number
126 
127 do j=1,config%ny
128  evals(j) = sqrt(max(threshold,evals(j)))
129  revals(j) = 1.0_kind_real/evals(j)
130 enddo
131 
132 allocate(config%sqrt_merid(config%ny,config%ny))
133 allocate(config%sqrt_inv_merid(config%ny,config%ny))
134 
135 do j=1,config%ny
136  do i=j,config%ny
137  config%sqrt_merid(i,j) = 0.0_kind_real
138  config%sqrt_inv_merid(i,j) = 0.0_kind_real
139 
140  do k=1,config%ny
141  config%sqrt_merid(i,j) = config%sqrt_merid(i,j) &
142  & + evects(i,k)*evects(j,k)*evals(k)
143  config%sqrt_inv_merid(i,j) = config%sqrt_inv_merid(i,j) &
144  & + evects(i,k)*evects(j,k)*revals(k)
145  enddo
146  enddo
147 enddo
148 
149 deallocate(revals)
150 deallocate(evects)
151 deallocate(evals)
152 
153 !--- Calculate spectral weights for zonal correlations:
154 !--- First we construct the structure function in grid space and FFT it
155 
156 allocate(struct_fn(config%nx))
157 do j=1,config%nx
158  z = (sqrt(0.5_kind_real)*dx/corr_length_scale) &
159  & *real(min(j-1,1+config%nx-j),kind_real)
160  struct_fn(j) = exp(-z*z)
161 enddo
162 
163 allocate(work(config%nx+2))
164 
165 call fft_fwd(config%nx,struct_fn,work)
166 work(:) = work(:) / real(config%nx, kind_real)
167 
168 !--- We are after sqrt(B) or sqrt(Q), so square-root the coefficents.
169 !--- The factor N ensures we get the struture function back if we apply
170 !--- B or Q to a unit delta function.
171 
172 allocate(config%sqrt_zonal(0:config%nx/2))
173 
174 threshold = 0.0_kind_real
175 do i=0,config%nx/2
176  threshold = max(threshold,work(1+2*i))
177 enddo
178 threshold = threshold/condition_number
179 
180 do i=0,config%nx/2
181  config%sqrt_zonal(i) = sqrt(max(threshold,real(config%nx,kind_real)*work(1+2*i)))
182 enddo
183 
184 deallocate(struct_fn)
185 deallocate(work)
186 
187 return
188 end subroutine qg_3d_covar_setup
189 
190 ! ------------------------------------------------------------------------------
191 
192 !> Delete for the QG model's 3d error covariance matrices
193 
194 subroutine qg_3d_covar_delete(self)
195 implicit none
196 type(qg_3d_covar_config) :: self
197 
198 deallocate(self%sqrt_zonal)
199 deallocate(self%sqrt_merid)
200 deallocate(self%sqrt_inv_merid)
201 
202 end subroutine qg_3d_covar_delete
203 
204 ! ------------------------------------------------------------------------------
205 
206 !> Multiply streamfunction by inverse(sqrt(C)), where C is 3d covariance matrix
207 
208 subroutine qg_3d_covar_sqrt_inv_mult(kx,ky,xctl,xincr,config)
209 use iso_c_binding
210 use fft_mod
211 use kinds
212 use qg_fields
213 
214 implicit none
215 integer(c_int), intent(in) :: kx !< Zonal grid dimension
216 integer(c_int), intent(in) :: ky !< Meridional grid dimension
217 real(c_double), intent(inout) :: xctl(kx,ky,2) !< inv(sqrt(C)) times psi
218 type(qg_field), intent(in) :: xincr !< Streamfunction: psi
219 type(qg_3d_covar_config), intent(in) :: config !< covar config structure
220 
221 real(kind=kind_real) :: zfour(kx+2), work(ky)
222 integer :: i, j, k, iri, m
223 real(kind=kind_real) :: zc, zero, one
224 
225 !--- multiply by standard deviation
226 
227 zc = 1.0_kind_real/config%sigma
228 do k=1,2
229  do j=1,ky
230  do i=1,kx
231  xctl(i,j,k) = zc * xincr%x(i,j,k)
232  enddo
233  enddo
234 enddo
235 
236 !--- multiply by inverse square-root of zonal correlation matrix
237 
238 do k=1,2
239  do j=1,ky
240  call fft_fwd(kx,xctl(:,j,k),zfour)
241  do m=0,kx/2
242  do iri=1,2
243  zfour(2*m+iri) = zfour(2*m+iri) / config%sqrt_zonal(m)
244  enddo
245  enddo
246  call fft_inv(kx,zfour,xctl(:,j,k))
247  enddo
248 enddo
249 
250 !--- multiply by inverse square-root of meridional correlation matrix
251 
252 zero = 0.0_kind_real
253 one = 1.0_kind_real
254 do k=1,2
255  do i=1,kx
256  call dsymv('L',ky,one,config%sqrt_inv_merid,ky,xctl(i,1,k),kx,zero,work,1)
257  do j=1,ky
258  xctl(i,j,k) = work(j)
259  enddo
260  enddo
261 enddo
262 
263 !--- multiply by inverse symmetric square-root of vertical correlation matrix
264 
265 zc = 1.0_kind_real / sqrt(1.0_kind_real-config%vert_corr*config%vert_corr)
266 do j=1,ky
267  do i=1,kx
268  xctl(i,j,2) = zc * (xctl(i,j,2) - config%vert_corr * xctl(i,j,1))
269  enddo
270 enddo
271 
272 end subroutine qg_3d_covar_sqrt_inv_mult
273 
274 ! ------------------------------------------------------------------------------
275 
276 !> Multiply streamfunction by inverse(sqrt(C)) - Adjoint
277 
278 subroutine qg_3d_covar_sqrt_inv_mult_ad(kx,ky,xctl,xincr,config)
279 use iso_c_binding
280 use fft_mod
281 use kinds
282 use qg_fields
283 
284 implicit none
285 integer(c_int), intent(in) :: kx !< Zonal grid dimension
286 integer(c_int), intent(in) :: ky !< Meridional grid dimension
287 type(qg_field), intent(inout) :: xincr !< sqrt(C) times streamfunction
288 real(c_double), intent(in) :: xctl(kx,ky,2) !< Streamfunction
289 type(qg_3d_covar_config), intent(in) :: config !< covariance config structure
290 
291 real(kind=kind_real), allocatable :: xout(:,:,:)
292 real(kind=kind_real) :: zfour(kx+2), work(ky)
293 integer :: i, j, k, iri, m
294 real(kind=kind_real) :: zc, zero, one
295 
296 !--- adoint of multiplication by inverse symmetric square-root of vertical
297 !--- correlation matrix
298 
299 allocate(xout(kx,ky,2))
300 
301 zc = sqrt(1.0_kind_real-config%vert_corr*config%vert_corr)
302 do j=1,ky
303  do i=1,kx
304  xout(i,j,1) = xctl(i,j,1) -config%vert_corr * xctl(i,j,2) &
305  & *(1.0_kind_real/zc)
306  xout(i,j,2) = xctl(i,j,2)*(1.0_kind_real/zc)
307  enddo
308 enddo
309 
310 !--- adjoint multiplication by inverse sqrt of meridional correlation matrix
311 
312 zero = 0.0_kind_real
313 one = 1.0_kind_real
314 do k=1,2
315  do i=1,kx
316  call dsymv('L',ky,one,config%sqrt_inv_merid,ky,xout(i,1,k), &
317  & kx,zero,work,1)
318  do j=1,ky
319  xout(i,j,k) = work(j)
320  enddo
321  enddo
322 enddo
323 
324 !--- multiply by inverse square-root of zonal correlation matrix
325 
326 do k=1,2
327  do j=1,ky
328  call fft_fwd(kx,xout(:,j,k),zfour)
329  do m=0,kx/2
330  do iri=1,2
331  zfour(2*m+iri) = zfour(2*m+iri) / config%sqrt_zonal(m)
332  enddo
333  enddo
334  call fft_inv(kx,zfour,xout(:,j,k))
335  enddo
336 enddo
337 
338 !--- adjoint of multiplication by standard deviation
339 
340 do k=1,2
341  do j=1,ky
342  do i=1,kx
343  xincr%x(i,j,k) = xincr%x(i,j,k) + xout(i,j,k) &
344  & *(1.0_kind_real/config%sigma)
345  enddo
346  enddo
347 enddo
348 
349 deallocate(xout)
350 
351 end subroutine qg_3d_covar_sqrt_inv_mult_ad
352 
353 ! ------------------------------------------------------------------------------
354 
355 !> Multiply streamfunction by sqrt(C), where C is a 3d covariance matrix
356 
357 subroutine qg_3d_covar_sqrt_mult(kx,ky,xincr,xctl,config)
358 use iso_c_binding
359 use fft_mod
360 use kinds
361 use qg_fields
362 
363 implicit none
364 integer(c_int), intent(in) :: kx !< Zonal grid dimension
365 integer(c_int), intent(in) :: ky !< Meridional grid dimension
366 type(qg_field), intent(inout) :: xincr !< sqrt(C) times streamfunction
367 real(c_double), intent(in) :: xctl(kx,ky,2) !< Streamfunction
368 type(qg_3d_covar_config), intent(in) :: config !< covariance config structure
369 
370 integer :: i, j, k, iri, m
371 real(kind=kind_real) :: zc, zero, one
372 real(kind=kind_real) :: zfour(kx+2), work(ky)
373 
374 !--- multiply by symmetric square-root of vertical correlation matrix
375 
376 zc = sqrt(1.0_kind_real-config%vert_corr*config%vert_corr)
377 do j=1,ky
378  do i=1,kx
379  xincr%x(i,j,1) = xctl(i,j,1)
380  xincr%x(i,j,2) = config%vert_corr * xctl(i,j,1) + zc * xctl(i,j,2)
381  enddo
382 enddo
383 
384 !--- multiply by square-root of meridional correlation matrix
385 
386 zero = 0.0_kind_real
387 one = 1.0_kind_real
388 do k=1,2
389  do i=1,kx
390  call dsymv('L',ky,one,config%sqrt_merid,ky,xincr%x(i,1,k),kx,zero,work,1)
391  do j=1,ky
392  xincr%x(i,j,k) = work(j)
393  enddo
394  enddo
395 enddo
396 
397 !--- multiply by square-root of zonal correlation matrix
398 
399 do k=1,2
400  do j=1,ky
401  call fft_fwd(kx,xincr%x(:,j,k),zfour)
402  do m=0,kx/2
403  do iri=1,2
404  zfour(2*m+iri) = zfour(2*m+iri) * config%sqrt_zonal(m)
405  enddo
406  enddo
407  call fft_inv(kx,zfour,xincr%x(:,j,k))
408  enddo
409 enddo
410 
411 !--- multiply by standard deviation
412 
413 do k=1,2
414  do j=1,ky
415  do i=1,kx
416  xincr%x(i,j,k) = xincr%x(i,j,k) * config%sigma
417  enddo
418  enddo
419 enddo
420 
421 end subroutine qg_3d_covar_sqrt_mult
422 
423 ! ------------------------------------------------------------------------------
424 
425 !> Multiply streamfunction by sqrt(C) - Adjoint
426 
427 subroutine qg_3d_covar_sqrt_mult_ad(kx,ky,xincr,xctl,config)
428 use iso_c_binding
429 use fft_mod
430 use kinds
431 use qg_fields
432 
433 implicit none
434 integer(c_int), intent(in) :: kx !< Zonal grid spacing
435 integer(c_int), intent(in) :: ky !< Meridional grid spacing
436 real(c_double), intent(inout) :: xctl(kx,ky,2) !< Result
437 type(qg_field), intent(in) :: xincr !< Streamfunction: psi
438 type(qg_3d_covar_config), intent(in) :: config !< covar config structure
439 
440 real(kind=kind_real), allocatable :: xout(:,:,:)
441 integer :: i, j, k, iri, m
442 real(kind=kind_real) :: zc, zero, one
443 real(kind=kind_real) :: zgrid(kx), zfour(kx+2), work(ky)
444 
445 !--- adjoint of multiplication by standard deviation
446 
447 allocate(xout(kx,ky,2))
448 
449 do k=1,2
450  do j=1,ky
451  zgrid(:) = xincr%x(:,j,k) * config%sigma
452  call fft_fwd(kx,zgrid,zfour)
453  do m=0,kx/2
454  do iri=1,2
455  zfour(2*m+iri) = zfour(2*m+iri) * config%sqrt_zonal(m)
456  enddo
457  enddo
458  call fft_inv(kx,zfour,xout(:,j,k))
459  enddo
460 enddo
461 
462 !--- adjoint of multiplication by square-root of meridional correlation matrix
463 
464 zero = 0.0_kind_real
465 one = 1.0_kind_real
466 do k=1,2
467  do i=1,kx
468  call dsymv('L',ky,one,config%sqrt_merid,ky,xout(i,1,k),kx,zero,work,1)
469  do j=1,ky
470  xout(i,j,k) = work(j)
471  enddo
472  enddo
473 enddo
474 
475 !--- adjoint multiplication by symmetric square-root of vert correlation matrix
476 
477 zc = sqrt(1.0_kind_real-config%vert_corr*config%vert_corr)
478 do j=1,ky
479  do i=1,kx
480  xctl(i,j,1) = xctl(i,j,1) + xout(i,j,1) &
481  & +config%vert_corr * xout(i,j,2)
482  xctl(i,j,2) = xctl(i,j,2) + xout(i,j,2) * zc
483  enddo
484 enddo
485 
486 deallocate(xout)
487 
488 end subroutine qg_3d_covar_sqrt_mult_ad
489 
490 ! ------------------------------------------------------------------------------
491 
492 end module qg_covariance_mod
real(kind=kind_real), parameter domain_meridional
meridional model domain (m)
subroutine, public fft_inv(kk, pf, pg)
Definition: fft_mod.F90:66
subroutine qg_3d_covar_sqrt_inv_mult(kx, ky, xctl, xincr, config)
Multiply streamfunction by inverse(sqrt(C)), where C is 3d covariance matrix.
Structure holding configuration variables for the 3d error covariance matrices of the QG analysis...
subroutine qg_3d_covar_delete(self)
Delete for the QG model&#39;s 3d error covariance matrices.
Constants for the QG model.
Fortran module for Eigen FFTs.
Definition: fft.F90:24
Fortran module handling geometry for the QG model.
Definition: qg_geom_mod.F90:11
subroutine qg_3d_covar_setup(c_model, geom, config)
Linked list implementation.
subroutine qg_3d_covar_sqrt_mult(kx, ky, xincr, xctl, config)
Multiply streamfunction by sqrt(C), where C is a 3d covariance matrix.
type(registry_t) qg_3d_cov_registry
Linked list interface - defines registry_t type.
subroutine qg_3d_covar_sqrt_inv_mult_ad(kx, ky, xctl, xincr, config)
Multiply streamfunction by inverse(sqrt(C)) - Adjoint.
Fortran derived type to hold configuration data for the QG background/model covariance.
Handle fields for the QG model.
Definition: qg_fields.F90:11
#define max(a, b)
Definition: mosaic_util.h:33
subroutine, public fft_fwd(kk, pg, pf)
Definition: fft_mod.F90:49
real(kind=kind_real), parameter domain_zonal
model domain (m) in zonal direction
subroutine qg_3d_covar_sqrt_mult_ad(kx, ky, xincr, xctl, config)
Multiply streamfunction by sqrt(C) - Adjoint.