UFO
ufo_rttovonedvarcheck_mod.f90
Go to the documentation of this file.
1 ! (C) Copyright 2017-2020 Met Office
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 !> The main Fortran module for implementing the rttov onedvar check
7 
9 
10 use fckit_configuration_module, only: fckit_configuration
11 use fckit_log_module, only : fckit_log
12 use iso_c_binding
13 use kinds
14 use missing_values_mod
15 use obsspace_mod
16 use oops_variables_mod
31 use ufo_vars_mod
32 
33 implicit none
34 private
38 
39 ! ------------------------------------------------------------------------------
40 contains
41 ! ------------------------------------------------------------------------------
42 !> Setup the main rttov onedvar object in Fortran
43 !!
44 !! \details Makes a call to the main setup routine.
45 !!
46 !! \author Met Office
47 !!
48 !! \date 09/06/2020: Created
49 !!
50 subroutine ufo_rttovonedvarcheck_create(self, obsspace, f_conf, channels, &
51  onedvarflag, passflag)
52 
53  implicit none
54  type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object
55  type(c_ptr), value, intent(in) :: obsspace !< observation database pointer
56  type(fckit_configuration), intent(in) :: f_conf !< yaml file contents
57  integer(c_int), intent(in) :: channels(:) !< all channels that can be used in 1D-Var
58  integer(c_int), intent(in) :: onedvarflag !< flag from qc flags
59  integer(c_int), intent(in) :: passflag !< pass flag from qc flags
60 
61  self % obsdb = obsspace
62  self % onedvarflag = onedvarflag
63  self % passflag = passflag
64 
65  call ufo_rttovonedvarcheck_setup(self, f_conf, channels) ! from init
66 
67 end subroutine ufo_rttovonedvarcheck_create
68 
69 ! ------------------------------------------------------------------------------
70 !> Delete the main rttov onedvar object in Fortran
71 !!
72 !! \author Met Office
73 !!
74 !! \date 09/06/2020: Created
75 !!
77 
78  implicit none
79  type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object
80 
81  if (allocated(self % retrieval_variables)) deallocate(self % retrieval_variables)
82  if (allocated(self % channels)) deallocate(self % channels)
83 
84 end subroutine ufo_rttovonedvarcheck_delete
85 
86 ! ------------------------------------------------------------------------------
87 !> The main routine that applys the rttov onedvar filter
88 !!
89 !! \details Heritage : Ops_SatRad_Do1DVar_RTTOV12.f90
90 !!
91 !! This routine is called from the c++ apply method. The filter performs
92 !! a 1D-Var minimization using rttov
93 !!
94 !! \author Met Office
95 !!
96 !! \date 09/06/2020: Created
97 !!
98 subroutine ufo_rttovonedvarcheck_apply(self, f_conf, vars, hofxdiags_vars, geovals, apply)
99  use ufo_utils_mod, only: cmp_strings
100 
101  implicit none
102  type(ufo_rttovonedvarcheck), intent(inout) :: self !< rttovonedvarcheck main object
103  type(fckit_configuration), intent(in) :: f_conf !< yaml file contents
104  type(oops_variables), intent(in) :: vars !< channels for 1D-Var
105  type(oops_variables), intent(in) :: hofxdiags_vars !< retrieval variables for 1D-Var
106  type(ufo_geovals), intent(in) :: geovals !< model values at observation space
107  logical, intent(in) :: apply(:) !< qc manager flags
108 
109  type(ufo_rttovonedvarcheck_obs) :: obs ! data for all observations read from db
110  type(ufo_metoffice_bmatrixstatic) :: full_bmatrix ! full bmatrix read from file
111  type(ufo_geovals) :: local_geovals ! geoval for one observation
112  type(ufo_rttovonedvarcheck_ob) :: ob ! observation data for a single observation
113  type(ufo_rttovonedvarcheck_profindex) :: prof_index ! index for mapping geovals to 1d-var state profile
114  type(ufo_metoffice_rmatrixradiance) :: full_rmatrix ! full r_matrix read from file
115  type(ufo_rttovonedvarcheck_rsubmatrix) :: r_submatrix ! r_submatrix object
116  type(ufo_geovals) :: hofxdiags ! hofxdiags containing jacobian
117  type(ufo_rttovonedvarcheck_pcemis), target :: ir_pcemis ! Infrared principal components object
118  type(ufo_geoval), pointer :: geoval
119  character(len=max_string) :: sensor_id
120  character(len=max_string) :: var
121  character(len=max_string) :: varname
122  character(len=max_string) :: message
123  integer :: jvar, ivar, jobs, band, ii ! counters
124  integer :: nchans_used ! counter for number of channels used for an ob
125  integer :: jchans_used
126  integer :: fileunit ! unit number for reading in files
127  integer :: apply_count
128  integer :: nprofelements ! number of elements in 1d-var state profile
129  integer, allocatable :: fields_in(:)
130  real(kind_real) :: missing ! missing value
131  real(kind_real) :: t1, t2 ! timing
132  real(kind_real), allocatable :: b_matrix(:,:) ! 1d-var profile b matrix
133  real(kind_real), allocatable :: b_inverse(:,:) ! inverse for each 1d-var profile b matrix
134  real(kind_real), allocatable :: b_sigma(:) ! b_matrix diagonal error
135  real(kind_real), allocatable :: max_error(:) ! max_error = error(stdev) * factor
136  logical :: file_exists ! check if a file exists logical
137  logical :: onedvar_success
138  logical :: cloud_retrieval = .false.
139  type(ufo_radiancerttov) :: rttov_simobs
140  integer(c_size_t), allocatable :: ret_nlevs(:)
141 
142  ! ------------------------------------------
143  ! 1. Setup
144  ! ------------------------------------------
145  missing = missing_value(missing)
146 
147  ! Setup rttov simobs
148  call rttov_simobs % setup(f_conf, self % channels)
149 
150  ! Setup IR emissivity - if needed
151  if (self % pcemiss) then
152  if (len(self % EmisAtlas) > 4) then
153  call ir_pcemis % setup(self % EmisEigVecPath, self % EmisAtlas)
154  else
155  call ir_pcemis % setup(self % EmisEigVecPath)
156  end if
157  end if
158 
159  ! Setup full B matrix object
160  call full_bmatrix % setup(self % retrieval_variables, self % b_matrix_path, &
161  self % qtotal)
162 
163  ! Setup full R matrix object
164  call full_rmatrix % setup(self % r_matrix_path)
165 
166  ! Check if cloud retrievals needed
167  do ii = 1, size(self % retrieval_variables)
168  if (cmp_strings(self % retrieval_variables(ii), "cloud_top_pressure")) then
169  write(*,*) "Simple cloud is part of the state vector"
170  cloud_retrieval = .true.
171  end if
172  end do
173 
174  ! Create profile index for mapping 1d-var profile to b-matrix
175  call prof_index % setup(full_bmatrix, self%nlevels)
176 
177  ! Read in observation data from obsspace
178  call obs % setup(self, prof_index % nprofelements, geovals, vars, ir_pcemis)
179 
180  ! Initialize data arrays
181  allocate(b_matrix(prof_index % nprofelements,prof_index % nprofelements))
182  allocate(b_inverse(prof_index % nprofelements,prof_index % nprofelements))
183  allocate(b_sigma(prof_index % nprofelements))
184  allocate(ret_nlevs(hofxdiags_vars % nvars()))
185 
186  ! Decide on loop parameters - testing
187  if (self % StartOb == 0) self % StartOb = 1
188  if (self % FinishOb == 0) self % FinishOb = obs % iloc
189  if (self % StartOb > self % FinishOb) then
190  write(message,*) "start loop ",self % StartOb," is greater than finish loop ",self % FinishOb
191  call abor1_ftn(message)
192  end if
193 
194  ! Calculate hofxdiags levels for each variable
195  call ufo_rttovonedvarcheck_hofxdiags_levels(hofxdiags_vars, self % nlevels, ret_nlevs)
196 
197  ! ------------------------------------------
198  ! 2. Beginning main observation loop
199  ! ------------------------------------------
200  write(*,*) "Beginning loop over observations: ",trim(self%qcname)
201  apply_count = 0
202  obs_loop: do jobs = self % StartOb, self % FinishOb
203  if (apply(jobs)) then
204 
205  apply_count = apply_count + 1
206  write(message, *) "starting obs number ",jobs
207  call fckit_log % debug(message)
208  !---------------------------------------------------
209  ! 2.1 Setup Jb terms
210  !---------------------------------------------------
211  ! create one ob geovals from full all obs geovals
212  call ufo_geovals_copy_one(local_geovals, geovals, jobs)
213  call ufo_rttovonedvarcheck_check_geovals(self, local_geovals, &
214  prof_index, obs % surface_type(jobs))
215 
216  ! create b matrix arrays for this single observation location
217  call full_bmatrix % reset( obs % lat(jobs), & ! in
218  b_matrix, b_inverse, b_sigma ) ! out
219 
220  !---------------------------------------------------
221  ! 2.2 Setup Jo terms
222  !---------------------------------------------------
223  ! Channel selection based on previous filters flags
224  nchans_used = 0
225  do jvar = 1, self%nchans
226  if( obs % QCflags(jvar,jobs) == self % passflag ) then
227  nchans_used = nchans_used + 1
228  end if
229  end do
230  if (nchans_used == 0) then
231  write(message, *) "No channels selected for observation number ", &
232  jobs, " : skipping"
233  call fckit_log % debug(message)
234  cycle obs_loop
235  end if
236 
237  ! setup ob data for this observation
238  call ob % setup(nchans_used, self % nlevels, prof_index % nprofelements, self % nchans)
239  ob % forward_mod_name = self % forward_mod_name
240  ob % latitude = obs % lat(jobs)
241  ob % longitude = obs % lon(jobs)
242  ob % elevation = obs % elevation(jobs)
243  ob % sensor_zenith_angle = obs % sat_zen(jobs)
244  ob % sensor_azimuth_angle = obs % sat_azi(jobs)
245  ob % solar_zenith_angle = obs % sol_zen(jobs)
246  ob % solar_azimuth_angle = obs % sol_azi(jobs)
247  ob % channels_all = self % channels
248  ob % surface_type = obs % surface_type(jobs)
249  ob % retrievecloud = cloud_retrieval
250  ob % pcemis => ir_pcemis
251  ob % calc_emiss = obs % calc_emiss(jobs)
252  if(self % RTTOV_mwscattSwitch) ob % mwscatt = .true.
253  if(self % RTTOV_usetotalice) ob % mwscatt_totalice = .true.
254 
255  ! Store background T in ob data space
256  call ufo_geovals_get_var(local_geovals, var_ts, geoval)
257  ob % background_T(:) = geoval%vals(:, 1) ! K
258 
259  ! Create obs vector and r matrix
260  jchans_used = 0
261  do jvar = 1, self%nchans
262  if( obs % QCflags(jvar,jobs) == self % passflag ) then
263  jchans_used = jchans_used + 1
264  ob % yobs(jchans_used) = obs % yobs(jvar, jobs)
265  ob % channels_used(jchans_used) = self % channels(jvar)
266  ob % emiss(jchans_used) = obs % emiss(jvar, jobs)
267  end if
268  end do
269  call r_submatrix % setup(nchans_used, ob % channels_used, full_rmatrix=full_rmatrix)
270 
271  ! Setup hofxdiags for this retrieval
272  call ufo_geovals_setup(hofxdiags, hofxdiags_vars, 1, hofxdiags_vars % nvars(), ret_nlevs)
273 
274  if (self % FullDiagnostics) then
275  call ob % info()
276  call r_submatrix % info()
277  write(*, *) "Observations used = ",ob % yobs(:)
278  write(*,*) "ob % emiss = ",ob % emiss
279  write(*,*) "ob % calc_emiss = ",ob % calc_emiss
280  write(*,*) "Channel selection = "
281  write(*,'(15I5)') ob % channels_used
282  write(*,*) "All Channels = "
283  write(*,'(15I5)') ob % channels_all
284  end if
285 
286  !---------------------------------------------------
287  ! 2.3 Call minimization
288  !---------------------------------------------------
289  if (self % UseMLMinimization) then
290  call ufo_rttovonedvarcheck_minimize_ml(self, ob, &
291  r_submatrix, b_matrix, b_inverse, b_sigma, &
292  local_geovals, hofxdiags, rttov_simobs, &
293  prof_index, onedvar_success)
294  else
296  r_submatrix, b_matrix, b_inverse, b_sigma, &
297  local_geovals, hofxdiags, rttov_simobs, &
298  prof_index, onedvar_success)
299  end if
300 
301  obs % output_BT(:, jobs) = ob % output_BT(:)
302  obs % background_BT(:, jobs) = ob % background_BT(:)
303  obs % output_profile(:,jobs) = ob % output_profile(:)
304  obs % final_cost(jobs) = ob % final_cost
305  obs % LWP(jobs) = ob % LWP
306  obs % niter(jobs) = ob % niter
307 
308  ! Set QCflags based on output from minimization
309  if (.NOT. onedvar_success) then
310  do jvar = 1, self%nchans
311  if( obs % QCflags(jvar,jobs) == 0 ) then
312  obs % QCflags(jvar,jobs) = self % onedvarflag
313  end if
314  end do
315  end if
316 
317  ! Check the BTs are within a factor of the error
318  if (self % RetrievedErrorFactor > 0.0 .and. any(obs % QCflags(:,jobs) == 0)) then
319  allocate(max_error(size(ob % channels_used)))
320  call r_submatrix % multiply_factor_by_stdev(self % RetrievedErrorFactor, max_error)
321  if (any(abs(ob % final_bt_diff(:)) > max_error(:))) then
322  do jvar = 1, self % nchans
323  if( obs % QCflags(jvar,jobs) == 0 ) then
324  obs % QCflags(jvar,jobs) = self % onedvarflag
325  end if
326  end do
327  end if
328  deallocate(max_error)
329  end if
330 
331  ! Tidy up memory specific to a single observation
332  call ufo_geovals_delete(local_geovals)
333  call ufo_geovals_delete(hofxdiags)
334  call ob % delete()
335  call r_submatrix % delete()
336 
337  else
338  call fckit_log % info("Final 1Dvar cost, apply = F")
339 
340  endif
341  end do obs_loop
342 
343  !---------------------------------------------------
344  ! 3.0 Return variables and tidy up
345  !---------------------------------------------------
346 
347  write(message, *) "Total number of observations = ", obs % iloc
348  call fckit_log % info(message)
349  write(message, *) "Number tested by 1dvar = ", apply_count
350  call fckit_log % info(message)
351 
352  ! Put qcflags and output variables into observation space
353  call obs % output(self % obsdb, prof_index, vars, self % nchans)
354 
355  ! Tidy up memory used for all observations
356  call full_bmatrix % delete()
357  call full_rmatrix % delete()
358  call obs % delete()
359  if (self % pcemiss) call ir_pcemis % delete()
360  if (allocated(b_matrix)) deallocate(b_matrix)
361  if (allocated(b_inverse)) deallocate(b_inverse)
362  if (allocated(b_sigma)) deallocate(b_sigma)
363  if (allocated(ret_nlevs)) deallocate(ret_nlevs)
364  call rttov_simobs % delete()
365 
366 end subroutine ufo_rttovonedvarcheck_apply
367 
368 end module ufo_rttovonedvarcheck_mod
subroutine, public ufo_geovals_setup(self, vars, nlocs, nvars, nvals)
Initializes and allocates self GeoVaLs with nlocs number of locations for vars variables....
subroutine, public ufo_geovals_delete(self)
subroutine, public ufo_geovals_copy_one(self, other, loc_index)
Copy one location from GeoVaLs into a new object.
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module containing the full b-matrix data type and methods for the 1D-Var.
Fortran derived type to hold data for the observation covariance.
Fortran module for radiancerttov observation operator.
Fortran module constants used throughout the rttovonedvarcheck filter.
Fortran module containing the routines to perform a Marquardt-Levenberg minimization.
subroutine, public ufo_rttovonedvarcheck_minimize_ml(self, ob, r_matrix, b_matrix, b_inv, b_sigma, local_geovals, hofxdiags, rttov_simobs, profile_index, onedvar_success)
Perform a minimization using the Marquardt-Levenberg method.
Fortran module to perform newton steepest descent minimization.
subroutine, public ufo_rttovonedvarcheck_minimize_newton(self, ob, r_matrix, b_matrix, b_inv, b_sigma, local_geovals, hofxdiags, rttov_simobs, profile_index, onedvar_success)
Get the jacobian used in the 1D-Var.
Fortran module containing subroutines used by both minimizers.
subroutine, public ufo_rttovonedvarcheck_check_geovals(self, geovals, profindex, surface_type)
Check the geovals are ready for the first iteration.
subroutine, public ufo_rttovonedvarcheck_hofxdiags_levels(retrieval_vars, nlevels, ret_nlevs)
The main Fortran module for implementing the rttov onedvar check.
subroutine, public ufo_rttovonedvarcheck_delete(self)
Delete the main rttov onedvar object in Fortran.
subroutine, public ufo_rttovonedvarcheck_apply(self, f_conf, vars, hofxdiags_vars, geovals, apply)
The main routine that applys the rttov onedvar filter.
subroutine, public ufo_rttovonedvarcheck_create(self, obsspace, f_conf, channels, onedvarflag, passflag)
Setup the main rttov onedvar object in Fortran.
Fortran module which contains the observation metadata for a single observation.
Fortran module which contains the observation data for a all the obs space.
Fortran module which contains the methods for Infrared Principal Component Emissivity The science can...
Fortran module containing profile index.
Fortran derived type to hold data for the observation covariance.
Fortran module containing main type, setup and utilities for the main rttovonedvarcheck object.
subroutine, public ufo_rttovonedvarcheck_setup(self, f_conf, channels)
Setup the defaults for the main rttovonedvarcheck object and read in the contents of the yaml file.
Fortran module with various useful routines.
logical function, public cmp_strings(str1, str2)
character(len=maxvarlen), parameter, public var_ts
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators
Fortran derived type for the observation type.