SOCA
soca_balance_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 
7 
8 use fckit_configuration_module, only: fckit_configuration
9 use fms_io_mod, only: fms_io_init, fms_io_exit
10 use fms_mod, only: read_data
11 use kinds, only: kind_real
12 
13 ! soca modules
14 use soca_fields_mod, only: soca_field
15 use soca_geom_mod, only: soca_geom
17 use soca_ksshts_mod, only: soca_ksshts, soca_steric_jacobian
18 use soca_kst_mod, only: soca_kst, soca_soft_jacobian
19 use soca_state_mod, only: soca_state
20 
21 implicit none
22 private
23 
24 
25 !> Variable transform for the balance operators (K)
26 !!
27 !! The core of the balance transformations are provided by
28 !! soca_ksshts_mod::soca_ksshts and soca_kst_mod::soca_kst
29 type, public :: soca_balance
30  ! private members
31  type(soca_kst), private :: kst !< T/S balance
32  type(soca_ksshts), private :: ksshts !< SSH/T/S balance
33  real(kind=kind_real), private, allocatable :: kct(:,:) !< C/T Jacobian
34  type(soca_geom), pointer, private :: geom !< geometry
35 
36 contains
37  !> \copybrief soca_balance_setup \see soca_balance_setup
38  procedure :: setup => soca_balance_setup
39 
40  !> \copybrief soca_balance_delete \see soca_balance_delete
41  procedure :: delete => soca_balance_delete
42 
43  !> \copybrief soca_balance_mult \see soca_balance_mult
44  procedure :: mult => soca_balance_mult
45 
46  !> \copybrief soca_balance_multad \see soca_balance_multad
47  procedure :: multad => soca_balance_multad
48 
49  !> \copybrief soca_balance_multinv \see soca_balance_multinv
50  procedure :: multinv => soca_balance_multinv
51 
52  !> \copybrief soca_balance_multinvad \see soca_balance_multinvad
53  procedure :: multinvad => soca_balance_multinvad
54 
55 end type soca_balance
56 
57 
58 ! ------------------------------------------------------------------------------
59 contains
60 ! ------------------------------------------------------------------------------
61 
62 
63 ! ------------------------------------------------------------------------------
64 !> Initialization of the balance operator and its trajectory.
65 !!
66 !! - balances always used: T,S,SSH
67 !! - optional balances depending on input fields: cicen
68 !! \relates soca_balance_mod::soca_balance
69 subroutine soca_balance_setup(self, f_conf, traj, geom)
70  class(soca_balance), intent(inout) :: self
71  type(fckit_configuration), intent(in) :: f_conf !< configuration
72  type(soca_state), target, intent(in) :: traj !< trajectory
73  type(soca_geom), target, intent(in) :: geom !< geometry
74 
75  integer :: isc, iec, jsc, jec
76  integer :: isd, ied, jsd, jed
77  integer :: i, j, k, nl
78  real(kind=kind_real), allocatable :: jac(:)
79  type(soca_field), pointer :: tocn, socn, hocn, cicen, mld, layer_depth
80 
81  ! declarations related to the dynamic height Jacobians
82  character(len=:), allocatable :: filename, mask_name
83  real(kind=kind_real), allocatable :: jac_mask(:,:) !> mask for Jacobian
84  real(kind=kind_real) :: threshold
85  integer :: nlayers !> dynamic height Jac=0 in nlayers upper layers
86  logical :: mask_detadt = .false. !> if true, set deta/dt to 0
87  logical :: mask_detads = .false. !> if true, set deta/ds to 0
88 
89  ! declarations related to the sea-ice Jacobian
90  character(len=:), allocatable :: kct_name
91  real(kind=kind_real), allocatable :: kct(:,:) !> dc/dT
92 
93  self%geom => geom
94 
95  ! Indices for compute domain
96  isc=geom%isc; iec=geom%iec
97  jsc=geom%jsc; jec=geom%jec
98  isd=geom%isd; ied=geom%ied
99  jsd=geom%jsd; jed=geom%jed
100 
101  ! Setup mask for Jacobians related to the dynamic height balance
102  allocate(jac_mask(isd:ied,jsd:jed))
103  jac_mask = 1.0_kind_real
104  nlayers = 0.0_kind_real
105  if ( f_conf%has("jac_mask") ) then
106  jac_mask = 0.0_kind_real
107  call f_conf%get_or_die("jac_mask.filename", filename)
108  call f_conf%get_or_die("jac_mask.name", mask_name)
109  call f_conf%get_or_die("jac_mask.threshold", threshold)
110  call f_conf%get_or_die("jac_mask.nlayers", nlayers)
111  call f_conf%get_or_die("jac_mask.detadt", mask_detadt)
112  call f_conf%get_or_die("jac_mask.detads", mask_detads)
113  call fms_io_init()
114  call read_data(filename, mask_name, jac_mask, domain=geom%Domain%mpp_domain)
115  call fms_io_exit()
116  where(jac_mask<threshold)
117  jac_mask = 0.0_kind_real
118  end where
119  end if
120 
121  ! Get configuration for Kst
122  call f_conf%get_or_die("dsdtmax", self%kst%dsdtmax)
123  call f_conf%get_or_die("dsdzmin", self%kst%dsdzmin)
124  call f_conf%get_or_die("dtdzmin", self%kst%dtdzmin)
125  call f_conf%get_or_die("nlayers", self%kst%nlayers) ! Set jac to 0 in the
126  ! nlayers top layers
127 
128  ! Get required fields
129  call traj%get("tocn", tocn)
130  call traj%get("socn", socn)
131  call traj%get("hocn", hocn)
132  call traj%get("mld", mld)
133  call traj%get("layer_depth", layer_depth)
134  if (traj%has("cicen")) call traj%get("cicen", cicen)
135 
136  ! allocate space
137  nl = hocn%nz
138  allocate(self%kst%jacobian(isc:iec,jsc:jec,geom%nzo))
139  allocate(jac(nl))
140  self%kst%jacobian=0.0
141 
142  ! Compute and store Jacobian of Kst
143  do i = isc, iec
144  do j = jsc, jec
145  jac=0.0
146  call soca_soft_jacobian(jac,&
147  &tocn%val(i,j,:),&
148  &socn%val(i,j,:),&
149  &hocn%val(i,j,:),&
150  &self%kst%dsdtmax, self%kst%dsdzmin, self%kst%dtdzmin)
151  ! Set Jacobian to 0 above mixed layer
152  do k=1,nl
153  if (layer_depth%val(i,j,k) < mld%val(i,j,1)) then
154  jac(k) = 0.0_kind_real
155  end if
156  end do
157  self%kst%jacobian(i,j,:) = jac(:)
158  self%kst%jacobian(i,j,1:self%kst%nlayers) = 0.0_kind_real
159  end do
160  end do
161  deallocate(jac)
162 
163  ! Compute Jacobian of Ksshts
164  allocate(self%ksshts%kssht, mold=self%kst%jacobian)
165  allocate(self%ksshts%ksshs, mold=self%kst%jacobian)
166  allocate(jac(2))
167  self%ksshts%kssht=0.0_kind_real
168  self%ksshts%ksshs=0.0_kind_real
169  do i = isc, iec
170  do j = jsc, jec
171  do k = 1, nl
172  call soca_steric_jacobian (jac, &
173  tocn%val(i,j,k), &
174  socn%val(i,j,k), &
175  &layer_depth%val(i,j,k),&
176  &hocn%val(i,j,k),&
177  &geom%lon(i,j),&
178  &geom%lat(i,j))
179  self%ksshts%kssht(i,j,k) = jac(1)*jac_mask(i,j)
180  self%ksshts%ksshs(i,j,k) = jac(2)*jac_mask(i,j)
181  end do
182  if (nlayers>0) then
183  self%ksshts%kssht(i,j,1:nlayers) = 0.0_kind_real
184  self%ksshts%ksshs(i,j,1:nlayers) = 0.0_kind_real
185  end if
186  end do
187  end do
188  deallocate(jac)
189 
190  ! Zero-out Jacobians if required by configuration
191  if (mask_detadt) self%ksshts%kssht = 0.0_kind_real
192  if (mask_detads) self%ksshts%ksshs = 0.0_kind_real
193 
194  ! Compute Kct
195  if (traj%has("cicen")) then
196  ! Setup dc/dT
197  allocate(kct(isd:ied,jsd:jed))
198  kct = 0.0_kind_real
199  if ( f_conf%has("dcdt") ) then
200  call f_conf%get_or_die("dcdt.filename", filename)
201  call f_conf%get_or_die("dcdt.name", kct_name)
202  call fms_io_init()
203  call read_data(filename, kct_name, kct, domain=geom%Domain%mpp_domain)
204  call fms_io_exit()
205  end if
206  allocate(self%kct(isc:iec,jsc:jec))
207  self%kct = 0.0_kind_real
208  do i = isc, iec
209  do j = jsc, jec
210  if (sum(cicen%val(i,j,:)) > 1.0e-3_kind_real) then
211  self%kct = kct(i,j)
212  end if
213  end do
214  end do
215  end if
216 
217 end subroutine soca_balance_setup
218 
219 
220 ! ------------------------------------------------------------------------------
221 !> Destructor for the balance oprator
222 !!
223 !! \relates soca_balance_mod::soca_balance
224 subroutine soca_balance_delete(self)
225  class(soca_balance), intent(inout) :: self
226 
227  ! the following always exist
228  deallocate(self%kst%jacobian)
229  deallocate(self%ksshts%kssht)
230  deallocate(self%ksshts%ksshs)
231 
232  ! only exists if cicen was given
233  if (allocated(self%kct)) deallocate(self%kct)
234 end subroutine soca_balance_delete
235 
236 
237 ! ------------------------------------------------------------------------------
238 !> Apply forward balance operator
239 !!
240 !! \relates soca_balance_mod::soca_balance
241 subroutine soca_balance_mult(self, dxa, dxm)
242  class(soca_balance), intent(in) :: self
243  type(soca_increment), intent(in) :: dxa !< input increment
244  type(soca_increment), intent(inout) :: dxm !< output increment
245 
246  type(soca_field), pointer :: fld_m, fld_a
247  type(soca_field), pointer :: tocn_a, socn_a
248 
249  integer :: i, j, k, n
250 
251  !> [ I 0 0 0 ]
252  !> [ Kst I 0 0 ]
253  !> K= [ Ketat Ketas I 0 ]
254  !> [ Kct 0 0 I ]
255 
256  call dxa%get("tocn",tocn_a)
257  call dxa%get("socn",socn_a)
258 
259  do n=1, size(dxm%fields)
260  fld_m => dxm%fields(n)
261  fld_a => dxa%fields(n)
262 
263  do i = self%geom%isc, self%geom%iec
264  do j = self%geom%jsc, self%geom%jec
265  select case(fld_m%name)
266  case default
267  fld_m%val(i,j,:) = fld_a%val(i,j,:)
268 
269  case("socn") ! Salinity
270  fld_m%val(i,j,:) = fld_a%val(i,j,:) + &
271  & self%kst%jacobian(i,j,:) * tocn_a%val(i,j,:)
272 
273  case ("ssh") ! SSH
274  fld_m%val(i,j,:) = fld_a%val(i,j,:)
275  do k = 1, tocn_a%nz
276  fld_m%val(i,j,:) = fld_m%val(i,j,:) + &
277  & self%ksshts%kssht(i,j,k) * tocn_a%val(i,j,k) + &
278  & self%ksshts%ksshs(i,j,k) * socn_a%val(i,j,k)
279  end do
280 
281  case ("cicen") ! Ice fraction
282  do k = 1, fld_m%nz
283  fld_m%val(i,j,k) = fld_a%val(i,j,k) + &
284  & self%kct(i,j) * tocn_a%val(i,j,1)
285  end do
286 
287  end select
288  end do
289  end do
290  end do
291 end subroutine soca_balance_mult
292 
293 
294 ! ------------------------------------------------------------------------------
295 !> Apply backward balance operator
296 !!
297 !! \relates soca_balance_mod::soca_balance
298 subroutine soca_balance_multad(self, dxa, dxm)
299  class(soca_balance) , intent(in) :: self
300  type(soca_increment), intent(in) :: dxm !< input increment
301  type(soca_increment), intent(inout) :: dxa !< output increment
302 
303  type(soca_field), pointer :: fld_a, fld_m
304  type(soca_field), pointer :: socn_m, ssh_m, cicen_m
305  integer :: i, j, n
306 
307  cicen_m => null()
308 
309  call dxm%get("socn", socn_m)
310  call dxm%get("ssh", ssh_m)
311  if (dxm%has("cicen")) call dxm%get("cicen",cicen_m)
312 
313  do n = 1, size(dxa%fields)
314  fld_a => dxa%fields(n)
315  fld_m => dxm%fields(n)
316 
317  do i = self%geom%isc, self%geom%iec
318  do j = self%geom%jsc, self%geom%jec
319  select case(fld_a%name)
320  case default
321  fld_a%val(i,j,:) = fld_m%val(i,j,:)
322 
323  case ("tocn") ! Temperature
324  fld_a%val(i,j,:) = fld_m%val(i,j,:) + &
325  & self%kst%jacobian(i,j,:) * socn_m%val(i,j,:) + &
326  & self%ksshts%kssht(i,j,:) * ssh_m%val(i,j,1)
327 
328  if (associated(cicen_m)) then ! use cicen only if present
329  fld_a%val(i,j,1) = fld_a%val(i,j,1) + &
330  & self%kct(i,j) * sum(cicen_m%val(i,j,:))
331  end if
332 
333  case ("socn") ! Salinity
334  fld_a%val(i,j,:) = fld_m%val(i,j,:) + &
335  & self%ksshts%ksshs(i,j,:) * ssh_m%val(i,j, 1)
336 
337  end select
338  end do
339  end do
340  end do
341 end subroutine soca_balance_multad
342 
343 
344 ! ------------------------------------------------------------------------------
345 !> Apply inverse of the forward balance operator
346 !!
347 !! \relates soca_balance_mod::soca_balance
348 subroutine soca_balance_multinv(self, dxa, dxm)
349  class(soca_balance), intent(in) :: self
350  type(soca_increment), intent(in) :: dxm !< input increment
351  type(soca_increment), intent(inout) :: dxa !< output increment
352 
353  integer :: i, j, k, n
354  type(soca_field), pointer :: fld_m, fld_a
355  type(soca_field), pointer :: tocn_m, socn_m
356 
357  call dxm%get("tocn", tocn_m)
358  call dxm%get("socn", socn_m)
359 
360  do n = 1, size(dxa%fields)
361  fld_a => dxa%fields(n)
362  fld_m => dxm%fields(n)
363 
364  do i = self%geom%isc, self%geom%iec
365  do j = self%geom%jsc, self%geom%jec
366  select case(fld_a%name)
367  case default
368  fld_a%val(i,j,:) = fld_m%val(i,j,:)
369 
370  case ('socn') ! Salinity
371  fld_a%val(i,j,:) = fld_m%val(i,j,:) - &
372  & self%kst%jacobian(i,j,:) * tocn_m%val(i,j,:)
373 
374  case ('ssh') ! SSH
375  fld_a%val(i,j, :) = fld_m%val(i,j, :)
376  do k = 1, tocn_m%nz
377  fld_a%val(i,j,:) = fld_a%val(i,j,:) + &
378  & ( self%ksshts%ksshs(i,j,k) * self%kst%jacobian(i,j,k) - &
379  & self%ksshts%kssht(i,j,k) ) * tocn_m%val(i,j,k) - &
380  & self%ksshts%ksshs(i,j,k) * socn_m%val(i,j,k)
381  end do
382 
383  case ('cicen') ! Ice fraction
384  fld_a%val(i,j,:) = fld_m%val(i,j,:)
385  do k = 1, fld_m%nz
386  fld_a%val(i,j,k) = fld_a%val(i,j,k) - &
387  & self%kct(i,j) * tocn_m%val(i,j,1)
388  end do
389 
390  end select
391  end do
392  end do
393  end do
394 end subroutine soca_balance_multinv
395 
396 
397 ! ------------------------------------------------------------------------------
398 !> Apply inverse of the backward balance operator
399 !!
400 !! \relates soca_balance_mod::soca_balance
401 subroutine soca_balance_multinvad(self, dxa, dxm)
402  class(soca_balance), intent(in) :: self
403  type(soca_increment), intent(inout) :: dxm !< output increment
404  type(soca_increment), intent(in) :: dxa !< input increment
405 
406  integer :: i, j, n
407  type(soca_field), pointer :: fld_a, fld_m
408  type(soca_field), pointer :: socn_a, ssh_a, cicen_a
409 
410  cicen_a => null()
411 
412  call dxa%get("socn", socn_a)
413  call dxa%get("ssh", ssh_a)
414  if (dxa%has("cicen")) call dxa%get("cicen",cicen_a)
415 
416  do n = 1, size(dxm%fields)
417  fld_m => dxm%fields(n)
418  fld_a => dxa%fields(n)
419 
420  do i = self%geom%isc, self%geom%iec
421  do j = self%geom%jsc, self%geom%jec
422  select case (fld_m%name)
423  case default
424  fld_m%val(i,j,:) = fld_a%val(i,j,:)
425 
426  case ('tocn') ! Temperature
427  fld_m%val(i,j,:) = fld_a%val(i,j,:) &
428  & - self%kst%jacobian(i,j,:) * socn_a%val(i,j,:) &
429  & + ( self%ksshts%ksshs(i,j,:) * self%kst%jacobian(i,j,:) &
430  & - self%ksshts%kssht(i,j,:) ) * ssh_a%val(i,j,1)
431 
432  if (associated(cicen_a)) then ! use cicen only if present
433  fld_m%val(i,j,1) = fld_m%val(i,j,1) &
434  & - self%kct(i,j) * sum(cicen_a%val(i,j,:))
435  end if
436 
437  case ('socn') ! Salinity
438  fld_m%val(i,j,:) = fld_a%val(i,j,:) - &
439  & self%ksshts%ksshs(i,j,:) * ssh_a%val(i,j,1)
440 
441  end select
442  end do
443  end do
444  end do
445 end subroutine soca_balance_multinvad
446 
447 end module soca_balance_mod
Handle fields for the model.
Geometry module.
Increment fields.
variable transform: SSH balance
variable transform: S/T balance
Definition: soca_kst_mod.F90:7
State fields.
Variable transform for the balance operators (K)
Holds all data and metadata related to a single field variable.
Geometry data structure.
Hold the jacobians for the ssh to s/t balance transform.
Hold the configuration and jacobians for the s/t balance transform.