UFO
ufo_avgkernel_tlad_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2020 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 !> Fortran module for avgkernel tl/ad observation operator
7 
9  use oops_variables_mod
10  use ufo_vars_mod
11  use ufo_geovals_mod
12  use missing_values_mod
13  use kinds
14  use iso_c_binding
15 
16  implicit none
17  private
18  integer, parameter :: max_string=800
19 
20 ! ------------------------------------------------------------------------------
21 
22  type, public :: ufo_avgkernel_tlad
23  private
24  type(oops_variables), public :: obsvars
25  type(oops_variables), public :: geovars
26  integer :: nlayers_kernel
27  integer :: nlocs, nvars, nval
28  real(kind_real), allocatable, dimension(:) :: ak_kernel, bk_kernel
29  character(kind=c_char,len=:), allocatable :: obskernelvar, tracervars(:)
30  logical :: troposphere, totalcolumn
31  real(kind_real) :: convert_factor_model, convert_factor_hofx
32  real(kind_real), allocatable, dimension(:,:) :: avgkernel_obs, prsl_obs, prsi_obs
33  real(kind_real), allocatable, dimension(:,:) :: prsl, prsi, temp, phii
34  real(kind_real), allocatable, dimension(:) :: airmass_tot, airmass_trop
35  integer, allocatable, dimension(:) :: troplev_obs
36  logical :: flip_it
37  contains
38  procedure :: setup => avgkernel_tlad_setup_
39  procedure :: cleanup => avgkernel_tlad_cleanup_
40  procedure :: settraj => avgkernel_tlad_settraj_
41  procedure :: simobs_tl => avgkernel_simobs_tl_
42  procedure :: simobs_ad => avgkernel_simobs_ad_
43  final :: destructor
44  end type ufo_avgkernel_tlad
45 
46 contains
47 
48 ! ------------------------------------------------------------------------------
49 subroutine avgkernel_tlad_setup_(self, f_conf)
50  use fckit_configuration_module, only: fckit_configuration
51  use ufo_constants_mod, only: one
52  implicit none
53  class(ufo_avgkernel_tlad), intent(inout) :: self
54  type(fckit_configuration), intent(in) :: f_conf
55  type(fckit_configuration) :: f_confOpts
56  integer :: nlevs_yaml
57  integer :: ivar, nvars
58  character(len=max_string) :: err_msg
59  character(len=:), allocatable :: str_array(:)
60 
61  ! get configuration for the averaging kernel operator
62  call f_conf%get_or_die("obs options",f_confopts)
63 
64  call f_confopts%get_or_die("nlayers_kernel", self%nlayers_kernel)
65  nlevs_yaml = f_confopts%get_size("ak")
66  if (nlevs_yaml /= self%nlayers_kernel+1) then
67  write(err_msg, *) "ufo_avgkernel_tlad_setup error: YAML nlayers_kernel != size of ak array"
68  call abor1_ftn(err_msg)
69  end if
70  nlevs_yaml = f_confopts%get_size("bk")
71  if (nlevs_yaml /= self%nlayers_kernel+1) then
72  write(err_msg, *) "ufo_avgkernel_tlad_setup error: YAML nlayers_kernel != size of bk array"
73  call abor1_ftn(err_msg)
74  end if
75 
76  ! allocate and read ak/bk for the averaging kernel
77  allocate(self%ak_kernel(nlevs_yaml))
78  allocate(self%bk_kernel(nlevs_yaml))
79  call f_confopts%get_or_die("ak", self%ak_kernel)
80  call f_confopts%get_or_die("bk", self%bk_kernel)
81 
82  ! get variable name from IODA for observation averaging kernel
83  if (.not. f_confopts%get("AvgKernelVar", self%obskernelvar)) then
84  self%obskernelvar = "averaging_kernel" ! default option
85  end if
86 
87  ! get name of geoval/tracer to use from the model
88  nvars = self%obsvars%nvars()
89  call f_confopts%get_or_die("tracer variables", str_array)
90  self%tracervars = str_array
91 
92  ! determine if this is a total column or troposphere calculation
93  if (.not. f_confopts%get("tropospheric column", self%troposphere)) self%troposphere = .false.
94  if (.not. f_confopts%get("total column", self%totalcolumn)) self%totalcolumn = .false.
95 
96  ! both of these cannot be true
97  if (self%troposphere .and. self%totalcolumn) then
98  write(err_msg, *) "ufo_avgkernel_tlad_setup error: both tropospheric and total column set to TRUE, only one can be TRUE"
99  call abor1_ftn(err_msg)
100  end if
101 
102  ! do we need a conversion factor, say between ppmv and unity?
103  if (.not. f_confopts%get("model units coeff", self%convert_factor_model)) self%convert_factor_model = one
104  if (.not. f_confopts%get("hofx units coeff", self%convert_factor_hofx)) self%convert_factor_hofx = one
105 
106  ! add variables to geovars that are needed
107  ! specified tracers
108  do ivar = 1, nvars
109  call self%geovars%push_back(self%tracervars(ivar))
110  end do
111 
112 end subroutine avgkernel_tlad_setup_
113 
114 ! ------------------------------------------------------------------------------
115 subroutine destructor(self)
116  implicit none
117  type(ufo_avgkernel_tlad), intent(inout) :: self
118 
119  call self%cleanup()
120 
121 end subroutine destructor
122 
123 ! ------------------------------------------------------------------------------
124 subroutine avgkernel_tlad_settraj_(self, geovals_in, obss)
125  use iso_c_binding
127  use ufo_constants_mod, only: half
128  use obsspace_mod
129  implicit none
130  class(ufo_avgkernel_tlad), intent(inout) :: self
131  type(ufo_geovals), intent(in) :: geovals_in
132  type(c_ptr), value, intent(in) :: obss
133 
134  ! Local variables
135  type(ufo_geoval), pointer :: prsi, psfc
136  integer :: ivar, iobs, ilev
137  character(len=MAXVARLEN) :: varstring
138  character(len=4) :: levstr
139  type(ufo_geovals) :: geovals
140  type(ufo_geoval), pointer :: prsl, temp, phii, p_temp
141 
142  ! get nlocs and nvars
143  self%nlocs = obsspace_get_nlocs(obss)
144  self%nvars = obsspace_get_nvars(obss)
145 
146  ! get geovals of atmospheric pressure
147  call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in
148  call ufo_geovals_get_var(geovals, var_prs, p_temp)
149  if (p_temp%vals(1,1) < p_temp%vals(p_temp%nval,1) ) then
150  self%flip_it = .true.
151  else
152  self%flip_it = .false.
153  end if
154  call ufo_geovals_reorderzdir(geovals, var_prs, "bottom2top")
155  call ufo_geovals_get_var(geovals, var_ps, psfc)
156  call ufo_geovals_get_var(geovals, var_prs, prsl)
157  call ufo_geovals_get_var(geovals, var_prsi, prsi)
158  call ufo_geovals_get_var(geovals, var_ts, temp)
159  call ufo_geovals_get_var(geovals, var_zi, phii)
160 
161  ! get nval
162  self%nval = prsl%nval
163 
164  allocate(self%prsl(self%nval, self%nlocs))
165  allocate(self%temp(self%nval, self%nlocs))
166  allocate(self%phii(self%nval+1, self%nlocs))
167  allocate(self%prsi(self%nval+1, self%nlocs))
168  do iobs = 1, self%nlocs
169  self%prsl(:,iobs) = prsl%vals(:,iobs)
170  self%prsi(:,iobs) = prsi%vals(:,iobs)
171  self%temp(:,iobs) = temp%vals(:,iobs)
172  self%phii(:,iobs) = phii%vals(:,iobs)
173  end do
174 
175  ! grab necesary metadata from IODA
176  ! get observation averaging kernel
177  ! once 2D arrays are allowed, rewrite/simplify this part
178  allocate(self%avgkernel_obs(self%nlayers_kernel, self%nlocs))
179  do ilev = 1, self%nlayers_kernel
180  write(levstr, fmt = "(I3)") ilev
181  levstr = adjustl(levstr)
182  varstring = trim(self%obskernelvar)//"_"//trim(levstr)
183  call obsspace_get_db(obss, "MetaData", trim(varstring), self%avgkernel_obs(ilev, :))
184  end do
185 
186  ! compute prsl_obs/prsi_obs from ak/bk/psfc
187  allocate(self%prsl_obs(self%nlayers_kernel, self%nlocs))
188  allocate(self%prsi_obs(self%nlayers_kernel+1, self%nlocs))
189  ! prsi_obs calculation
190  do ilev = 1, self%nlayers_kernel+1
191  self%prsi_obs(ilev,:) = self%ak_kernel(ilev) + self%bk_kernel(ilev) * psfc%vals(1,:)
192  end do
193  ! using simple averaging for now for prsl, can use more complex way later
194  do ilev = 1, self%nlayers_kernel
195  self%prsl_obs(ilev,:) = (self%prsi_obs(ilev,:) + self%prsi_obs(ilev+1,:)) * half
196  end do
197 
198  if (self%troposphere) then
199  allocate(self%troplev_obs(self%nlocs))
200  allocate(self%airmass_trop(self%nlocs))
201  allocate(self%airmass_tot(self%nlocs))
202  call obsspace_get_db(obss, "MetaData", "troposphere_layer_index", self%troplev_obs)
203  call obsspace_get_db(obss, "MetaData", "air_mass_factor_troposphere", self%airmass_trop)
204  call obsspace_get_db(obss, "MetaData", "air_mass_factor_total", self%airmass_tot)
205  end if
206 
207 end subroutine avgkernel_tlad_settraj_
208 
209 ! ------------------------------------------------------------------------------
210 subroutine avgkernel_simobs_tl_(self, geovals_in, obss, nvars, nlocs, hofx)
211  use iso_c_binding
213  use obsspace_mod
215  implicit none
216  class(ufo_avgkernel_tlad), intent(in) :: self
217  type(ufo_geovals), intent(in) :: geovals_in
218  integer, intent(in) :: nvars, nlocs
219  real(c_double), intent(inout) :: hofx(nvars, nlocs)
220  type(c_ptr), value, intent(in) :: obss
221  type(ufo_geoval), pointer :: tracer
222  integer :: ivar, iobs
223  character(len=MAXVARLEN) :: geovar
224  real(kind_real) :: hofx_tmp
225  type(ufo_geovals) :: geovals
226  real(c_double) :: missing
227 
228  missing = missing_value(missing)
229 
230  call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in
231 
232  ! loop through all variables
233  do ivar = 1, nvars
234  geovar = self%tracervars(ivar)
235  call ufo_geovals_get_var(geovals, geovar, tracer)
236  do iobs = 1, nlocs
237  if (self%avgkernel_obs(1,iobs) /= missing) then ! take care of missing obs
238  if(self%flip_it) tracer%vals(1:tracer%nval,iobs) = tracer%vals(tracer%nval:1:-1,iobs)
239  if (self%troposphere) then
240  call simulate_column_ob_tl(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), &
241  self%prsi_obs(:,iobs), self%prsi(:,iobs),&
242  self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),&
243  self%phii(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, &
244  hofx_tmp, self%troplev_obs(iobs), self%airmass_tot(iobs), self%airmass_trop(iobs))
245  hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx
246  else if (self%totalcolumn) then
247  call simulate_column_ob_tl(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), &
248  self%prsi_obs(:,iobs), self%prsi(:,iobs),&
249  self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),&
250  self%phii(:,iobs), tracer%vals(:,iobs)*self%convert_factor_model, &
251  hofx_tmp)
252  hofx(ivar,iobs) = hofx_tmp * self%convert_factor_hofx
253  end if
254  else
255  hofx(ivar,iobs) = missing
256  end if
257  end do
258  end do
259  call ufo_geovals_delete(geovals)
260 
261 end subroutine avgkernel_simobs_tl_
262 
263 ! ------------------------------------------------------------------------------
264 subroutine avgkernel_simobs_ad_(self, geovals_in, obss, nvars, nlocs, hofx)
265  use iso_c_binding
268  use obsspace_mod
269  implicit none
270  class(ufo_avgkernel_tlad), intent(in) :: self
271  type(ufo_geovals), intent(inout) :: geovals_in
272  integer, intent(in) :: nvars, nlocs
273  real(c_double), intent(in) :: hofx(nvars, nlocs)
274  type(c_ptr), value, intent(in) :: obss
275  type(ufo_geoval), pointer :: tracer
276  character(len=MAXVARLEN) :: geovar
277  type(ufo_geovals) :: geovals
278  real(kind_real) :: hofx_tmp
279  integer :: ivar, iobs
280  real(c_double) :: missing
281 
282  missing = missing_value(missing)
283 
284  if (.not. geovals_in%linit ) geovals_in%linit=.true. ! need this for var exe
285 
286  ! loop through all variables
287  do ivar = 1, nvars
288  geovar = self%tracervars(ivar)
289  call ufo_geovals_get_var(geovals_in, geovar, tracer)
290 
291  do iobs = 1, nlocs
292  if (hofx(ivar,iobs) /= missing) then ! take care of missing obs
293  if (self%troposphere) then
294  hofx_tmp = hofx(ivar,iobs) * self%convert_factor_hofx
295  call simulate_column_ob_ad(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), &
296  self%prsi_obs(:,iobs), self%prsi(:,iobs),&
297  self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),&
298  self%phii(:,iobs), tracer%vals(:,iobs), &
299  hofx_tmp, self%troplev_obs(iobs), self%airmass_tot(iobs), self%airmass_trop(iobs))
300  tracer%vals(:,iobs) = tracer%vals(:,iobs) * self%convert_factor_model
301  else if (self%totalcolumn) then
302  hofx_tmp = hofx(ivar,iobs) * self%convert_factor_hofx
303  call simulate_column_ob_ad(self%nlayers_kernel, tracer%nval, self%avgkernel_obs(:,iobs), &
304  self%prsi_obs(:,iobs), self%prsi(:,iobs),&
305  self%prsl_obs(:,iobs), self%prsl(:,iobs), self%temp(:,iobs),&
306  self%phii(:,iobs), tracer%vals(:,iobs), hofx_tmp)
307  tracer%vals(:,iobs) = tracer%vals(:,iobs) * self%convert_factor_model
308  end if
309  end if
310  if(self%flip_it) tracer%vals(1:tracer%nval,iobs) = tracer%vals(tracer%nval:1:-1,iobs)
311  end do
312  end do
313 
314 end subroutine avgkernel_simobs_ad_
315 
316 ! ------------------------------------------------------------------------------
317 subroutine avgkernel_tlad_cleanup_(self)
318  implicit none
319  class(ufo_avgkernel_tlad), intent(inout) :: self
320  self%nvars = 0
321  self%nlocs = 0
322  self%nval = 0
323  ! deallocate things
324  if (allocated(self%ak_kernel)) deallocate(self%ak_kernel)
325  if (allocated(self%bk_kernel)) deallocate(self%bk_kernel)
326  if (allocated(self%obskernelvar)) deallocate(self%obskernelvar)
327  if (allocated(self%tracervars)) deallocate(self%tracervars)
328  if (allocated(self%avgkernel_obs)) deallocate(self%avgkernel_obs)
329  if (allocated(self%prsl_obs)) deallocate(self%prsl_obs)
330  if (allocated(self%prsi_obs)) deallocate(self%prsi_obs)
331  if (allocated(self%prsl)) deallocate(self%prsl)
332  if (allocated(self%prsi)) deallocate(self%prsi)
333  if (allocated(self%temp)) deallocate(self%temp)
334  if (allocated(self%phii)) deallocate(self%phii)
335  if (allocated(self%airmass_tot)) deallocate(self%airmass_tot)
336  if (allocated(self%airmass_trop)) deallocate(self%airmass_trop)
337  if (allocated(self%troplev_obs)) deallocate(self%troplev_obs)
338 end subroutine avgkernel_tlad_cleanup_
339 
340 ! ------------------------------------------------------------------------------
341 
342 end module ufo_avgkernel_tlad_mod
subroutine simulate_column_ob_ad(nlayers_obs, nlayers_model, avgkernel_obs, prsi_obs, prsi_model, prsl_obs, prsl_model, temp_model, zi_model, profile_model_ad, hofx_ad, troplev_obs, airmass_tot, airmass_trop)
subroutine simulate_column_ob_tl(nlayers_obs, nlayers_model, avgkernel_obs, prsi_obs, prsi_model, prsl_obs, prsl_model, temp_model, zi_model, profile_model, hofx, troplev_obs, airmass_tot, airmass_trop)
Fortran module for avgkernel tl/ad observation operator.
subroutine avgkernel_simobs_ad_(self, geovals_in, obss, nvars, nlocs, hofx)
subroutine avgkernel_tlad_settraj_(self, geovals_in, obss)
subroutine avgkernel_simobs_tl_(self, geovals_in, obss, nvars, nlocs, hofx)
subroutine avgkernel_tlad_cleanup_(self)
subroutine avgkernel_tlad_setup_(self, f_conf)
real(kind_real), parameter, public one
real(kind_real), parameter, public half
subroutine, public ufo_geovals_get_var(self, varname, geoval)
character(len=maxvarlen), parameter, public var_prsi
character(len=maxvarlen), parameter, public var_prs
character(len=maxvarlen), parameter, public var_zi
character(len=maxvarlen), parameter, public var_ts
character(len=maxvarlen), parameter, public var_ps
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators