UFO
ufo_aodext_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 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 !> Fortran module for aodext observation operator
7 
9 
10  use iso_c_binding
11  use kinds
12  use oops_variables_mod
13  use fckit_log_module, only : fckit_log
14  use ufo_vars_mod
15  use missing_values_mod
16 
17  implicit none
18  private
19 
20 !> Fortran derived type for the observation type
21 
22  type, public :: ufo_aodext
23  private
24  type(oops_variables), public :: obsvars
25  type(oops_variables), public :: geovars
26  real(kind_real), public, allocatable :: wavelength(:)!(nprofiles)
27  integer, public :: nprofiles
28  contains
29  procedure :: setup => ufo_aodext_setup
30  procedure :: simobs => ufo_aodext_simobs
31  final :: destructor
32  end type ufo_aodext
33 
34 !> Default variables required from model
35  character(len=maxvarlen), dimension(2), parameter :: varindefault = (/var_delp, var_airdens/)
36  character(len=maxvarlen), dimension(3), parameter :: extdefault = (/var_ext1, var_ext2, var_ext3/)
37 ! needs at least 2 profiles of extinction in bkg for angstrom law
38 contains
39 
40 !----------------
41 integer function b_channel( bracket, nprofiles, bkg_wavelengths, obs_wavelength ) ! given a wavelength, return the index of bracket wavelengths
42 implicit none
43 integer, intent(in) :: bracket ! bracket index (1 or 2)
44 integer, intent(in) :: nprofiles ! number of bkg profiles
45 real(kind_real), dimension(nprofiles) :: bkg_wavelengths ! array of bkg wavelengths
46 real(kind_real) :: obs_wavelength ! observed wavelength
47 
48 character(len=maxvarlen) :: err_msg
49 integer :: j
50  j = 1
51  do while(j < nprofiles)
52  if(obs_wavelength >= bkg_wavelengths(j) .and.&
53  obs_wavelength < bkg_wavelengths(j+1)) then
54  if (bracket == 1) then
55  b_channel = j
56  else if (bracket == 2) then
57  b_channel = j + 1
58  else
59  write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)"
60  call abor1_ftn(err_msg)
61  endif
62  j = j+ 1
63  else if(obs_wavelength > bkg_wavelengths(j) .and.&
64  obs_wavelength <= bkg_wavelengths(j+1)) then
65  if (bracket == 1) then
66  b_channel = j
67  else if (bracket == 2) then
68  b_channel = j + 1
69  else
70  write(err_msg,*) "ufo_aodext_mod: function b_channel: bracket index should be 1 (lower) or 2 (upper)"
71  call abor1_ftn(err_msg)
72  endif
73  j = j +1
74  endif
75  j = j + 1
76  enddo
77  return
78 end function b_channel
79 
80 ! ------------------------------------------------------------------------------
81 subroutine ufo_aodext_setup(self, f_conf)
82 use fckit_configuration_module, only: fckit_configuration
83 implicit none
84 class(ufo_aodext), intent(inout) :: self
85 type(fckit_configuration), intent(in) :: f_conf
86 
87 !Locals
88 integer n
89 character(len=maxvarlen) :: err_msg
90 
91  ! Fill in geovars: input variables requested from the model
92  ! Need slots for airdens and delp
93 
94  call f_conf%get_or_die("nprofiles", self%nprofiles)
95 
96  if (self%nprofiles < 2 .or. self%nprofiles > 3) then
97  write(err_msg,*) 'ufo_aodext_setup: number of extinction profiles must be 2 or 3'
98  call abor1_ftn(err_msg)
99  endif
100 
101  do n = 1, self%nprofiles
102  call self%geovars%push_back(extdefault(n))
103  enddo
104  call self%geovars%push_back(varindefault)
105 
106  ! Wavelengths for extinction profiles, specified in yaml in croissant order
107  allocate(self%wavelength(self%nprofiles))
108  call f_conf%get_or_die("bkg_wavelengths", self%wavelength)
109  !Check that the wavelengths are in an ascending order
110  n = 1
111  do while (n < self%nprofiles)
112  if(self%wavelength(n) > self%wavelength(n+1)) then
113  write(err_msg,*) ' ufo_aodext_setup: bkg wavelengths should be in an ascending order'
114  call abor1_ftn(err_msg)
115  endif
116  n = n + 1
117  enddo
118 
119 end subroutine ufo_aodext_setup
120 
121 ! ------------------------------------------------------------------------------
122 subroutine destructor(self)
123 implicit none
124 type(ufo_aodext), intent(inout) :: self
125 
126  if (allocated(self%wavelength)) deallocate(self%wavelength)
127 
128 end subroutine destructor
129 ! ------------------------------------------------------------------------------
130 subroutine ufo_aodext_simobs(self, geovals, obss, nvars, nlocs, hofx)
131 use kinds
133 use iso_c_binding
134 use obsspace_mod
135 use ufo_constants_mod, only: grav, zero
136 
137 implicit none
138 class(ufo_aodext), intent(in) :: self
139 integer, intent(in) :: nvars, nlocs
140 type(ufo_geovals), intent(in) :: geovals
141 real(c_double), intent(inout) :: hofx(nvars, nlocs)
142 type(c_ptr), value, intent(in) :: obss
143 
144 ! Local variables
145 type(ufo_geoval), pointer :: ext_profile
146 type(ufo_geoval), pointer :: delp_profile
147 type(ufo_geoval), pointer :: airdens_profile
148 
149 real(kind_real), dimension(:,:,:), allocatable :: ext !(km, nlocs, nch) ext profiles interp at obs loc [km-1]
150 real(kind_real), dimension(:,:), allocatable :: airdens !(km, nlocs) airdens profiles interp at obs loc [kg.m-3]
151 real(kind_real), dimension(:,:), allocatable :: delp !(km, nlocs) air pressure thickness profiles at obs loc[Pa]
152 real(kind_real), dimension(:,:), allocatable :: aod_bkg !(nlocs, nch) AOD computed from modeled ext profiles
153 real(kind_real), dimension(:), allocatable :: obss_wavelength ! (nvars) observed AOD wavelengths [nm]
154 
155 real(kind_real) :: angstrom ! (Angstrom coefficient calculated from bkg AOD and wavelengths)
156 real(kind_real) :: logm
157 
158 character(len=MAXVARLEN) :: geovar
159 real(c_double) :: missing
160 
161 character(len=MAXVARLEN) :: message
162 integer :: nlayers
163 integer :: km, nobs, nch, ic, i, j, k
164 
165  ! Get airdens and delp and number of layers from geovals
166  ! -----------------------
167  geovar = self%geovars%variable(1)
168 
169  call ufo_geovals_get_var(geovals, var_delp, delp_profile)
170  nlayers = delp_profile%nval ! number of model layers
171 
172  allocate(delp(nlayers,nlocs))
173  delp = delp_profile%vals
174 
175  call ufo_geovals_get_var(geovals, var_airdens, airdens_profile)
176  allocate(airdens(nlayers,nlocs))
177  airdens = airdens_profile%vals
178 
179  ! Get extinction profiles from geovals
180  ! ---------------------
181  allocate(ext(nlayers, nlocs, self%nprofiles))
182  do nch = 1, self%nprofiles
183  geovar = self%geovars%variable(nch)
184  call ufo_geovals_get_var(geovals, geovar, ext_profile)
185  ext(:,:,nch) = ext_profile%vals
186  enddo
187 
188  ! Get some metadata from obsspace, observed AOD wavelengths
189  ! -----------------------
190  allocate(obss_wavelength(nvars))
191  call obsspace_get_db(obss,"VarMetaData", "obs_wavelength", obss_wavelength)
192 
193  ! Check if observed wavelength AOD is within the range of bkg wavelength to apply angstrom law
194  ! else hofx set to missing value
195  do ic = 1, nvars
196  if(obss_wavelength(ic) < self%wavelength(1) .or. obss_wavelength(ic) > self%wavelength(self%nprofiles)) then
197  write(message,*) 'ufo_aodext_simobs: observed wavelength outside of bkg wavelengths range', obss_wavelength(ic)
198  call fckit_log%info(message)
199  endif
200  enddo
201  ! Observation operator
202  ! ------------------
203 
204  ! Calculation of AOD from extinction profiles
205  ! ------------------
206  allocate(aod_bkg(nlocs, self%nprofiles))
207  aod_bkg = zero
208  do nch = 1, self%nprofiles
209  do nobs = 1, nlocs
210  do k =1, nlayers
211  aod_bkg(nobs,nch) = aod_bkg(nobs, nch) + (ext(k,nobs,nch) * delp(k,nobs)/(airdens(k,nobs))/(grav*1000.0_kind_real))
212  enddo
213  enddo
214  enddo
215 
216  ! hofx: angstrom law
217  ! ------------------
218  missing =missing_value(missing)
219  hofx = zero
220  do nobs = 1, nlocs
221  do ic = 1, nvars
222  if(obss_wavelength(ic) < self%wavelength(1) .or. obss_wavelength(ic) > self%wavelength(self%nprofiles)) then
223  hofx(ic,nobs) = missing
224  else
225  i = b_channel(1, self%nprofiles, self%wavelength, obss_wavelength(ic))
226  j = b_channel(2, self%nprofiles, self%wavelength, obss_wavelength(ic))
227  logm = log(self%wavelength(i)/self%wavelength(j))
228  angstrom = log(aod_bkg(nobs,i)/aod_bkg(nobs,j))/logm
229  hofx(ic,nobs) = aod_bkg(nobs,i) * (obss_wavelength(ic)/self%wavelength(i))**angstrom
230  endif
231  enddo
232  enddo
233  deallocate(ext, airdens, delp, aod_bkg, obss_wavelength)
234 
235 end subroutine ufo_aodext_simobs
236 ! ------------------------------------------------------------------------------
237 end module ufo_aodext_mod
Fortran module for aodext observation operator.
integer function b_channel(bracket, nprofiles, bkg_wavelengths, obs_wavelength)
character(len=maxvarlen), dimension(2), parameter varindefault
Default variables required from model.
subroutine ufo_aodext_setup(self, f_conf)
subroutine ufo_aodext_simobs(self, geovals, obss, nvars, nlocs, hofx)
subroutine destructor(self)
character(len=maxvarlen), dimension(3), parameter extdefault
real(kind_real), parameter, public grav
real(kind_real), parameter, public zero
subroutine, public ufo_geovals_get_var(self, varname, geoval)
character(len=maxvarlen), parameter, public var_ext2
character(len=maxvarlen), parameter, public var_airdens
character(len=maxvarlen), parameter, public var_ext3
character(len=maxvarlen), parameter, public var_delp
character(len=maxvarlen), parameter, public var_ext1
Fortran derived type for the observation type.
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators