SOCA
soca_horizfilt_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2021 UCAR.
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 
6 !> horizontal filtering
8 
9 use atlas_module, only: atlas_geometry
10 use fckit_configuration_module, only: fckit_configuration
11 use iso_c_binding
12 use kinds
13 use mpp_domains_mod, only : mpp_update_domains, mpp_update_domains_ad
14 use oops_variables_mod, only: oops_variables
15 
16 ! soca modules
17 use soca_fields_mod, only: soca_field
18 use soca_geom_mod, only : soca_geom
20 use soca_state_mod, only: soca_state
21 
22 implicit none
23 private
24 
25 !> Variable transform: horizontal filtering
26 type, public :: soca_horizfilt
27  type(oops_variables) :: vars !< Apply filtering to vars
28  real(kind=kind_real), allocatable :: wgh(:,:,:,:) !< Filtering weight
29  real(kind=kind_real) :: scale_flow !< Used with "flow" filter, sea surface height decorrelation scale
30  real(kind=kind_real) :: scale_dist
31  real(kind=kind_real) :: niter !< number of iterations of filter to perform
32  !> \name indices of compute domain
33  !! \{
34  integer :: isc, iec, jsc, jec
35  !> \}
36 
37  !> \name indices of data domain
38  !! \{
39  integer :: isd, ied, jsd, jed
40  !> \}
41 
42 contains
43 
44  !> \copybrief soca_horizfilt_setup \see soca_horizfilt_setup
45  procedure :: setup => soca_horizfilt_setup
46 
47  !> \copybrief soca_horizfilt_delete \see soca_horizfilt_delete
48  procedure :: delete => soca_horizfilt_delete
49 
50  !> \copybrief soca_horizfilt_mult \see soca_horizfilt_mult
51  procedure :: mult => soca_horizfilt_mult
52 
53  !> \copybrief soca_horizfilt_multad \see soca_horizfilt_multad
54  procedure :: multad => soca_horizfilt_multad
55 end type soca_horizfilt
56 
57 
58 ! ------------------------------------------------------------------------------
59 contains
60 ! ------------------------------------------------------------------------------
61 
62 
63 ! ------------------------------------------------------------------------------
64 !> Setup for the horizfilt operator
65 !!
66 !! \relates soca_horizfilt_mod::soca_horizfilt
67 subroutine soca_horizfilt_setup(self, f_conf, geom, traj, vars)
68  class(soca_horizfilt), intent(inout) :: self !< The horizfilt structure
69  type(fckit_configuration), intent(in) :: f_conf !< The configuration
70  type(soca_geom), intent(in) :: geom !< Geometry
71  type(soca_state), intent(in) :: traj !< Trajectory
72  type(oops_variables), intent(in) :: vars !< List of variables
73 
74  type(soca_field), pointer :: ssh
75 
76  integer :: i, j, ii, jj
77  real(kind=kind_real) :: dist(-1:1,-1:1), sum_w, r_dist, r_flow
78  type(atlas_geometry) :: ageometry
79 
80  ! Setup list of variables to apply filtering on
81  self%vars = vars
82 
83  ! Get filter length scales from configuration
84  if (.not. f_conf%get("scale_dist", self%scale_dist)) self%scale_dist = -1
85  if (.not. f_conf%get("scale_flow", self%scale_flow)) self%scale_flow = -1
86  call f_conf%get_or_die("niter", self%niter)
87 
88  ! scale the gaussian length scales depending on the number of iterations
89  ! NOTE: numerical instability creeps in once niter is large and scale_dist
90  ! is much smaller than the size of a grid box
91  self%scale_dist = self%scale_dist / sqrt(self%niter)
92  self%scale_flow = self%scale_flow / sqrt(self%niter)
93 
94  ! Indices for compute/data domain
95  self%isd = geom%isd ; self%ied = geom%ied ; self%jsd = geom%jsd; self%jed = geom%jed
96  self%isc = geom%isc ; self%iec = geom%iec ; self%jsc = geom%jsc; self%jec = geom%jec
97 
98  ! Allocate and compute filtering weights
99  allocate(self%wgh(self%isd:self%ied,self%jsd:self%jed,-1:1,-1:1))
100 
101  ! Create UnitSphere geometry
102  ageometry = atlas_geometry("Earth")
103 
104  ! Compute distance based weights
105  self%wgh = 0.0_kind_real
106  r_dist = 1.0
107  r_flow = 1.0
108  do j = self%jsc, self%jec
109  do i = self%isc, self%iec
110  do ii = -1,1
111  do jj = -1,1
112  ! Great circle distance
113  if(self%scale_dist > 0) then
114  r_dist = ageometry%distance(geom%lon(i,j), geom%lat(i,j), geom%lon(i+ii,j+jj), geom%lat(i+ii,j+jj) )
115  r_dist = exp(-0.5 * (r_dist/self%scale_dist) ** 2)
116  end if
117 
118  ! flow based distance (ssh difference)
119  if(self%scale_flow > 0) then
120  call traj%get("ssh", ssh)
121  r_flow = abs(ssh%val(i,j,1) - ssh%val(i+ii,j+jj,1))
122  r_flow = exp(-0.5 * ((r_flow / self%scale_flow) ** 2))
123  end if
124 
125  ! multiply together and apply the land mask
126  dist(ii,jj) = geom%mask2d(i+ii,j+jj) * r_dist * r_flow
127  end do
128  end do
129 
130  ! Normalize
131  sum_w = sum(dist)
132  if (sum_w>0.0_kind_real) then
133  self%wgh(i,j,:,:) = dist / sum_w
134  endif
135 
136  end do
137  end do
138 
139 end subroutine soca_horizfilt_setup
140 
141 
142 ! ------------------------------------------------------------------------------
143 !> Delete horizfilt
144 !!
145 !! \relates soca_horizfilt_mod::soca_horizfilt
146 subroutine soca_horizfilt_delete(self)
147  class(soca_horizfilt), intent(inout) :: self !< The horizfilt structure
148 
149  deallocate(self%wgh)
150 
151 end subroutine soca_horizfilt_delete
152 
153 
154 ! ------------------------------------------------------------------------------
155 !> Forward filtering
156 !!
157 !! \relates soca_horizfilt_mod::soca_horizfilt
158 subroutine soca_horizfilt_mult(self, dxin, dxout, geom)
159  class(soca_horizfilt), intent(inout) :: self !< The horizfilt structure
160  type(soca_increment), intent(in) :: dxin !< Input: Increment
161  type(soca_increment), intent(inout) :: dxout !< Output: filtered Increment
162  type(soca_geom), intent(in) :: geom
163 
164  type(soca_field), pointer :: field_i, field_o
165 
166  integer :: k, ivar
167  real(kind=kind_real), allocatable, dimension(:,:) :: dxi, dxo
168 
169  allocate(dxi(self%isd:self%ied,self%jsd:self%jed))
170  allocate(dxo(self%isd:self%ied,self%jsd:self%jed))
171 
172  do ivar = 1, self%vars%nvars()
173  call dxin%get(trim(self%vars%variable(ivar)), field_i)
174  call dxout%get(trim(self%vars%variable(ivar)), field_o)
175  do k = 1, field_i%nz
176  dxi = field_i%val(:,:,k)
177  call soca_filt2d(self, dxi, dxo, geom)
178  field_o%val(:,:,k) = dxo
179  end do
180  end do
181  deallocate(dxi, dxo)
182 
183 end subroutine soca_horizfilt_mult
184 
185 
186 ! ------------------------------------------------------------------------------
187 !> Backward filtering
188 !!
189 !! \relates soca_horizfilt_mod::soca_horizfilt
190 subroutine soca_horizfilt_multad(self, dxin, dxout, geom)
191  class(soca_horizfilt), intent(inout) :: self !< The horizfilt structure
192  type(soca_increment), intent(in) :: dxin !< Input:
193  type(soca_increment), intent(inout) :: dxout !< Output:
194  type(soca_geom), intent(in) :: geom
195 
196  type(soca_field), pointer :: field_i, field_o
197  integer :: k, ivar
198  real(kind=kind_real), allocatable, dimension(:,:) :: dxi, dxo
199 
200  allocate(dxi(self%isd:self%ied,self%jsd:self%jed))
201  allocate(dxo(self%isd:self%ied,self%jsd:self%jed))
202 
203  do ivar = 1, self%vars%nvars()
204  call dxin%get(trim(self%vars%variable(ivar)), field_i)
205  call dxout%get(trim(self%vars%variable(ivar)), field_o)
206  do k = 1, field_i%nz
207  dxi = field_i%val(:,:,k)
208  call soca_filt2d_ad(self, dxi, dxo, geom)
209  field_o%val(:,:,k) = dxo
210  end do
211  end do
212  deallocate(dxi, dxo)
213 
214 end subroutine soca_horizfilt_multad
215 
216 
217 ! ------------------------------------------------------------------------------
218 !> Forward filtering for 2D array
219 !!
220 !! used by soca_horizfilt_mod::soca_horizfilt::mult()
221 !! \relates soca_horizfilt_mod::soca_horizfilt
222 subroutine soca_filt2d(self, dxin, dxout, geom)
223  class(soca_horizfilt), intent(in) :: self
224  real(kind=kind_real), allocatable, intent(in) :: dxin(:,:)
225  real(kind=kind_real), allocatable, intent(inout) :: dxout(:,:)
226  type(soca_geom), intent(in) :: geom
227 
228  integer :: i, j
229  real(kind=kind_real), allocatable :: dxtmp(:,:)
230 
231  ! Make a temporary copy of dxin
232  allocate(dxtmp(self%isd:self%ied,self%jsd:self%jed))
233  dxtmp = 0.0_kind_real
234  dxtmp(self%isc:self%iec,self%jsc:self%jec) = dxin(self%isc:self%iec,self%jsc:self%jec)
235 
236  ! Update halo points of input array
237  call mpp_update_domains(dxtmp, geom%Domain%mpp_domain, complete=.true.)
238 
239  ! 9-point distance weighted average
240  dxout = 0.0_kind_real
241  do j = self%jsc, self%jec
242  do i = self%isc, self%iec
243  if (geom%mask2d(i,j)==1) then
244  dxout(i,j) = &
245  self%wgh(i,j,-1,1)*dxtmp(i-1,j+1) + &
246  self%wgh(i,j,0,1)*dxtmp(i,j+1) + &
247  self%wgh(i,j,1,1)*dxtmp(i+1,j+1) + &
248  self%wgh(i,j,-1,0)*dxtmp(i-1,j) + &
249  self%wgh(i,j,0,0)*dxtmp(i,j) + &
250  self%wgh(i,j,1,0)*dxtmp(i+1,j) + &
251  self%wgh(i,j,-1,-1)*dxtmp(i-1,j-1) + &
252  self%wgh(i,j,0,-1)*dxtmp(i,j-1) + &
253  self%wgh(i,j,1,-1)*dxtmp(i+1,j-1)
254  end if
255  end do
256  end do
257 
258  ! Update halo
259  call mpp_update_domains(dxout, geom%Domain%mpp_domain, complete=.true.)
260 
261  deallocate(dxtmp)
262 
263 end subroutine soca_filt2d
264 
265 
266 ! ------------------------------------------------------------------------------
267 !> Backward filtering for 2D array
268 !!
269 !! used by soca_horizfilt_mod::soca_horizfilt::multad()
270 !! \relates soca_horizfilt_mod::soca_horizfilt
271 subroutine soca_filt2d_ad(self, dxin, dxout, geom)
272  class(soca_horizfilt), intent(in) :: self
273  real(kind=kind_real), allocatable, intent(in) :: dxin(:,:)
274  real(kind=kind_real), allocatable, intent(inout) :: dxout(:,:)
275  type(soca_geom), intent(in) :: geom
276 
277  integer :: i, j
278  real(kind=kind_real), allocatable :: dxtmp(:,:)
279 
280  ! Make a temporary copy of dxin
281  allocate(dxtmp(self%isd:self%ied,self%jsd:self%jed))
282  dxtmp = 0.0_kind_real
283  dxtmp(self%isc:self%iec,self%jsc:self%jec) = dxin(self%isc:self%iec,self%jsc:self%jec)
284 
285  dxout = 0.0_kind_real
286  ! Adjoint of 9-point weighted average
287  do j = self%jec, self%jsc, -1
288  do i = self%iec, self%isc, -1
289  if (geom%mask2d(i,j)==1) then
290  dxout(i-1,j+1) = dxout(i-1,j+1) + self%wgh(i,j,-1, 1)*dxtmp(i,j)
291  dxout(i,j+1) = dxout(i,j+1) + self%wgh(i,j, 0, 1)*dxtmp(i,j)
292  dxout(i+1,j+1) = dxout(i+1,j+1) + self%wgh(i,j, 1, 1)*dxtmp(i,j)
293  dxout(i-1,j) = dxout(i-1,j) + self%wgh(i,j,-1, 0)*dxtmp(i,j)
294  dxout(i,j) = dxout(i,j) + self%wgh(i,j, 0, 0)*dxtmp(i,j)
295  dxout(i+1,j) = dxout(i+1,j) + self%wgh(i,j, 1, 0)*dxtmp(i,j)
296  dxout(i-1,j-1) = dxout(i-1,j-1) + self%wgh(i,j,-1,-1)*dxtmp(i,j)
297  dxout(i,j-1) = dxout(i,j-1) + self%wgh(i,j, 0,-1)*dxtmp(i,j)
298  dxout(i+1,j-1) = dxout(i+1,j-1) + self%wgh(i,j, 1,-1)*dxtmp(i,j)
299  end if
300  end do
301  end do
302 
303  ! Adjoint of halo update
304  call mpp_update_domains_ad(dxout, geom%Domain%mpp_domain, complete=.true.)
305 
306  deallocate(dxtmp)
307 
308 end subroutine soca_filt2d_ad
309 
310 end module soca_horizfilt_mod
Handle fields for the model.
Geometry module.
horizontal filtering
Increment fields.
State fields.
Holds all data and metadata related to a single field variable.
Geometry data structure.
Variable transform: horizontal filtering.