UFO
ufo_rttovonedvarcheck_minimize_jacobian_mod.f90
Go to the documentation of this file.
1 ! (C) Copyright 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 !> Fortran module to get the jacobian for the 1D-Var
7 
9 
10 use iso_c_binding
11 use kinds
12 use ufo_constants_mod, only: zero
19 use ufo_vars_mod
21 
22 implicit none
23 private
24 
26 
27 contains
28 
29 !------------------------------------------------------------------------------
30 !> Get the jacobian used in the 1D-Var.
31 !!
32 !! \author Met Office
33 !!
34 !! \date 09/06/2020: Created
35 !!
36 subroutine ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, channels, &
37  profindex, &
38  prof_x, hofxdiags, rttov_simobs, &
39  hofx, H_matrix)
40 
41 implicit none
42 
43 ! subroutine arguments
44 type(ufo_rttovonedvarcheck), intent(in) :: self !< Main 1D-Var object
45 type(ufo_geovals), intent(in) :: geovals !< model data at obs location
46 type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata
47 integer, intent(in) :: channels(:) !< channels used for this calculation
48 type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector
49 real(kind_real), intent(in) :: prof_x(:) !< x vector
50 type(ufo_geovals), intent(inout) :: hofxdiags !< model data to pass the jacobian
51 type(ufo_radiancerttov), intent(inout) :: rttov_simobs !< rttov simulate obs object
52 real(kind_real), intent(out) :: hofx(:) !< BT's
53 real(kind_real), intent(out) :: h_matrix(:,:) !< Jacobian
54 
55 select case (trim(ob % forward_mod_name))
56  case ("RTTOV")
57  call ufo_rttovonedvarcheck_gethmatrixrttovsimobs(geovals, ob, self % obsdb, &
58  rttov_simobs, channels, &
59  profindex, hofxdiags, &
60  self % UseQtsplitRain, self % FullDiagnostics, &
61  hofx(:), h_matrix) ! out
62 
63  case default
64  call abor1_ftn("rttovonedvarcheck get jacobian: no suitable forward model => exiting")
65 end select
66 
68 
69 !------------------------------------------------------------------------------
70 !> Get the jacobian from rttov and if neccessary convert
71 !! to variables used in the 1D-Var.
72 !!
73 !! \details Heritage: Ops_SatRad_GetHmatrix_RTTOV12.f90
74 !!
75 !! \warning mwemiss and emisspc not implemented yet
76 !!
77 !! \author Met Office
78 !!
79 !! \date 09/06/2020: Created
80 !!
81 subroutine ufo_rttovonedvarcheck_gethmatrixrttovsimobs(geovals, ob, obsdb, &
82  rttov_data, channels, profindex, &
83  hofxdiags, UseQtsplitRain, FullDiagnostics, &
84  hofx, H_matrix)
85 
86 implicit none
87 
88 ! subroutine arguments
89 type(ufo_geovals), intent(in) :: geovals !< model data at obs location
90 type(ufo_rttovonedvarcheck_ob), intent(inout) :: ob !< satellite metadata
91 type(c_ptr), value, intent(in) :: obsdb !< observation database
92 type(ufo_radiancerttov), intent(inout) :: rttov_data !< structure for running rttov_k
93 integer, intent(in) :: channels(:) !< channels used for this calculation
94 type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex !< index array for x vector
95 type(ufo_geovals), intent(inout) :: hofxdiags !< model data to pass the jacobian
96 logical, intent(in) :: UseQtsplitRain !< flag to make qtsplit use rain
97 logical, intent(in) :: FullDiagnostics
98 real(kind_real), intent(out) :: hofx(:) !< BT's
99 real(kind_real), intent(out) :: H_matrix(:,:) !< Jacobian
100 
101 ! Local arguments
102 integer :: nchans, nlevels, nq_levels
103 integer :: i, j
104 integer :: chan
105 logical :: RTTOV_GasunitConv = .false.
106 real(kind_real),allocatable :: q_kgkg(:)
107 real(kind_real) :: s2m_kgkg
108 type(ufo_geoval), pointer :: geoval
109 real(kind_real), allocatable :: pressure(:)
110 real(kind_real), allocatable :: dq_dqt(:)
111 real(kind_real), allocatable :: dql_dqt(:)
112 real(kind_real), allocatable :: dqi_dqt(:)
113 real(kind_real), allocatable :: dBT_dq(:)
114 real(kind_real), allocatable :: dBT_dql(:)
115 character(len=max_string) :: varname
116 real(c_double) :: BT(size(ob % channels_all))
117 real(kind_real) :: u, v, dBT_du, dBT_dv, windsp
118 
119 nchans = size(channels)
120 
121 call rttov_data % simobs(geovals, obsdb, size(ob % channels_all), 1, bt, hofxdiags, ob_info=ob)
122 
123 ! --------------------
124 !Get hofx for just channels used
125 !--------------------
126 all_chan_loop: do i = 1, size(ob % channels_all)
127  do j = 1, nchans
128  if(channels(j) == ob % channels_all(i)) then
129  hofx(j) = bt(i)
130  cycle all_chan_loop
131  end if
132  end do
133 end do all_chan_loop
134 
135 !----------------------------------------------------------------
136 ! 1.1) Temperature - invert as RTTOV level 1 as top of atmosphere and
137 ! 1Dvar profile and is from the surface up.
138 ! var_ts - air_temperature
139 ! Note : RTTOV jacobian is TOA -> surface same as prof_x
140 !----------------------------------------------------------------
141 if (profindex % t(1) > 0) then
142  nlevels = profindex % t(2) - profindex % t(1) + 1
143  do i = 1, nchans
144  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_ts),"_",channels(i) ! K
145  call ufo_geovals_get_var(hofxdiags , varname, geoval)
146  h_matrix(i,profindex % t(1):profindex % t(2)) = geoval % vals(:,1)
147  end do
148 end if
149 
150 !------
151 ! 1.2) Water vapour
152 !------
153 
154 ! Water Vapour Jacobians must be converted from
155 ! kg/kg to ln(g/kg) - the unit conversion cancels, then:
156 ! dy/d(ln q) = dy/dq * q(kg/kg)
157 ! var_q = "specific_humidity" ! kg/kg
158 ! Note : RTTOV jacobian is TOA -> surface same as prof_x
159 if (profindex % q(1) > 0) then
160 
161  nq_levels = profindex % q(2)-profindex % q(1)+1
162  allocate(q_kgkg(nq_levels))
163 
164  ! Get humidity data from geovals
165  q_kgkg(:) = zero
166  call ufo_geovals_get_var(geovals, var_q, geoval)
167  q_kgkg(:) = geoval%vals(nlevels:1:-1, 1)
168 
169  do i = 1, nchans
170  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_q),"_",channels(i) ! kg/kg
171  call ufo_geovals_get_var(hofxdiags, varname, geoval)
172  h_matrix(i,profindex % q(1):profindex % q(2)) = geoval % vals(:,1) * q_kgkg(:)
173  end do
174 
175  deallocate(q_kgkg)
176 
177 end if
178 
179 !------
180 ! 1.3) Total water
181 !------
182 
183 ! For the sake of this first implementation this will not include liquid
184 ! and ice water content just water vapour which is consistent with the
185 ! profile loaded from GeoVaLs.
186 ! var_q = "specific_humidity" ! kg/kg
187 ! var_clw = "mass_content_of_cloud_liquid_water_in_atmosphere_layer"
188 ! var_cli = "mass_content_of_cloud_ice_in_atmosphere_layer"
189 ! Note : RTTOV jacobian is TOA -> surface same as prof_x
190 if (profindex % qt(1) > 0) then
191 
192  allocate(q_kgkg(nlevels))
193  allocate(pressure(nlevels))
194  allocate(dq_dqt(nlevels))
195  allocate(dql_dqt(nlevels))
196  allocate(dqi_dqt(nlevels))
197  allocate(dbt_dq(nlevels))
198  allocate(dbt_dql(nlevels))
199 
200  ! Get humidity data from geovals
201  q_kgkg(:) = zero
202  call ufo_geovals_get_var(geovals, var_q, geoval)
203  q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1)
204  call ufo_geovals_get_var(geovals, var_clw, geoval)
205  q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1)
206  call ufo_geovals_get_var(geovals, var_cli, geoval)
207  q_kgkg(:) = q_kgkg(:) + geoval%vals(nlevels:1:-1, 1)
208 
209  ! var_prs = "air_pressure" Pa
210  call ufo_geovals_get_var(geovals, trim(var_prs), geoval)
211  pressure(:) = geoval%vals(nlevels:1:-1, 1)
212 
213  ! Calculate the gradients with respect to qtotal
214  call ops_satrad_qsplit ( 0, &
215  pressure(:), &
216  ob % background_T(nlevels:1:-1), &
217  q_kgkg(:), & ! in
218  dq_dqt(:), & ! out
219  dql_dqt(:), & ! out
220  dqi_dqt(:), & ! out
221  useqtsplitrain)
222 
223  ! Calculate jacobian wrt humidity and clw
224  do i = 1, nchans
225 
226  write(varname,"(3a,i0)") "brightness_temperature_jacobian_", trim(var_q), "_", channels(i) ! kg/kg
227  call ufo_geovals_get_var(hofxdiags, varname, geoval)
228  dbt_dq(:) = zero
229  dbt_dq(:) = geoval % vals(:,1)
230 
231  write(varname,"(3a,i0)") "brightness_temperature_jacobian_", trim(var_clw), "_", channels(i) ! kg/kg
232  call ufo_geovals_get_var(hofxdiags, varname, geoval)
233  dbt_dql(:) = zero
234  dbt_dql(:) = geoval % vals(:,1)
235 
236  h_matrix(i,profindex % qt(1):profindex % qt(2)) = &
237  (dbt_dq(:) * dq_dqt(:) + &
238  dbt_dql(:) * dql_dqt(:) ) * q_kgkg(:)
239 
240  end do
241 
242  ! Clean up
243  deallocate(q_kgkg)
244  deallocate(pressure)
245  deallocate(dq_dqt)
246  deallocate(dql_dqt)
247  deallocate(dqi_dqt)
248  deallocate(dbt_dq)
249  deallocate(dbt_dql)
250 
251 end if
252 
253 !----
254 ! 2.) Single-valued variables
255 !----
256 
257 ! 2.1) Surface Temperature - var_sfc_t2m = "surface_temperature"
258 
259 if (profindex % t2 > 0) then
260  do i = 1, nchans
261  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_t2m),"_",channels(i) ! K
262  call ufo_geovals_get_var(hofxdiags, varname, geoval)
263  h_matrix(i,profindex % t2) = geoval % vals(1,1)
264  end do
265 end if
266 
267 ! 2.2) Water vapour - var_sfc_q2m = "specific_humidity_at_two_meters_above_surface" ! (kg/kg)
268 
269 if (profindex % q2 > 0) then
270  s2m_kgkg = zero
271  call ufo_geovals_get_var(geovals, var_sfc_q2m, geoval)
272  s2m_kgkg = geoval%vals(1, 1)
273  do i = 1, nchans
274  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_q2m),"_",channels(i) ! kg/kg
275  call ufo_geovals_get_var(hofxdiags, varname, geoval)
276  h_matrix(i,profindex % q2) = geoval % vals(1,1) * s2m_kgkg
277  end do
278 end if
279 
280 ! 2.3) Surface pressure - var_sfc_p2m = "air_pressure_at_two_meters_above_surface" ! (Pa)
281 
282 if (profindex % pstar > 0) then
283  do i = 1, nchans
284  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_p2m),"_",channels(i)
285  call ufo_geovals_get_var(hofxdiags, varname, geoval)
286  h_matrix(i,profindex % pstar) = geoval % vals(1,1)
287  end do
288 end if
289 
290 ! 2.4) Windspeed - var_sfc_u10 = "uwind_at_10m"
291 ! - var_sfc_v10 = "vwind_at_10m"
292 ! - windsp = sqrt (u*u + v*v)
293 
294 if (profindex % windspeed > 0) then
295  call ufo_geovals_get_var(geovals, trim(var_sfc_u10), geoval)
296  u = geoval % vals(1, 1)
297  call ufo_geovals_get_var(geovals, trim(var_sfc_v10), geoval)
298  v = geoval % vals(1, 1)
299  windsp = sqrt(u ** 2 + v ** 2)
300 
301  do i = 1, nchans
302  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_u10),"_",channels(i)
303  call ufo_geovals_get_var(hofxdiags, varname, geoval)
304  dbt_du = geoval % vals(1,1)
305  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_v10),"_",channels(i)
306  call ufo_geovals_get_var(hofxdiags, varname, geoval)
307  dbt_dv = geoval % vals(1,1)
308  if (windsp > zero) then
309  ! directional derivation of the Jacobian
310  h_matrix(i,profindex % windspeed) = (dbt_du * u + dbt_dv * v) / windsp
311  else
312  h_matrix(i,profindex % windspeed) = zero
313  end if
314  end do
315 end if
316 
317 ! 2.5) Skin temperature - var_sfc_tskin = "skin_temperature" ! (K)
318 
319 if (profindex % tstar > 0) then
320  do i = 1, nchans
321  write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_tskin),"_",channels(i)
322  call ufo_geovals_get_var(hofxdiags, varname, geoval)
323  h_matrix(i,profindex % tstar) = geoval % vals(1,1)
324  end do
325 end if
326 
327 ! This has been left in for future development
328 ! 2.5) Cloud top pressure
329 ! This is not in rttov interface yet
330 !if (profindex % cloudtopp > 0) then
331 ! do i = 1, nchans
332 ! varname = "cloud_top_pressure"
333 ! write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(varname),"_",channels(i)
334 ! call ufo_geovals_get_var(hofxdiags, varname, geoval)
335 ! H_matrix(i,profindex % cloudtopp) = geoval % vals(1,1)
336 ! end do
337 !end if
338 
339 ! This has been left in for future development
340 ! 2.6) Effective cloud fraction
341 ! This is not in rttov interface yet
342 !if (profindex % cloudfrac > 0) then
343 ! do i = 1, nchans
344 ! varname = "cloud_fraction"
345 ! call ufo_geovals_get_var(hofxdiags, varname, geoval)
346 ! H_matrix(i,profindex % cloudfrac) = geoval % vals(1,1)
347 ! end do
348 !end if
349 
350 !----
351 ! 3.) Emissivities
352 ! This has been left in for future development
353 !----
354 
355 ! 3.1 Microwave Emissivity - var_sfc_emiss = "surface_emissivity"
356 
357 !if (profindex % mwemiss(1) > 0) then
358 ! ! The emissivity matrix needs to be "unpacked" as it is only one
359 ! ! dimensional over channels - implying you want to retrieve a
360 ! ! single emissivity value. It is unpacked here so that each emissivity
361 ! ! has a corresponding entry for the relevant channel. Note that this is
362 ! ! a bit physically dubious as several channels have the same frequency, etc.
363 ! ! This complexity is dealt with in the B Matrix.
364 ! ! Check that we want only the diagonal elements to be non-zero
365 ! do j = 1, size(EmissMap)
366 ! chan = EmissMap(j)
367 ! do i = 1, nchans
368 ! if (channels(i) == chan) then
369 ! write(varname,"(3a,i0)") "brightness_temperature_jacobian_",trim(var_sfc_emiss),"_",channels(i)
370 ! call ufo_geovals_get_var(hofxdiags, varname, geoval)
371 ! H_matrix(i,profindex % mwemiss(1) + j - 1) = geoval % vals(1,1)
372 ! end if
373 ! end do
374 ! end do
375 !end if
376 
377 !! 3.2. Infrared Emissivity - work in progress
378 !
379 !IF (profindex % emisspc(1) > 0) THEN
380 !
381 ! DO i = 1, nchans
382 ! emissivity_K(i) = rttov_data % profiles_k(i) % emissivity(1)
383 ! END DO
384 !
385 ! CALL Ops_SatRad_EmisKToPC (nchans, & ! in
386 ! Channels, & ! in
387 ! nemisspc, & ! in
388 ! emiss(:), & ! in
389 ! emissivity_K(:), & ! in
390 ! H_matrix(1:nchans,profindex % emisspc(1):profindex % emisspc(2))) ! out
391 !END IF
392 !
393 ! Here for diagnostics
394 
395 if (fulldiagnostics) then
397  nchans, & ! in
398  profindex % nprofelements, & ! in
399  ob % channels_used, & ! in
400  h_matrix, & ! in
401  profindex ) ! in
402 end if
403 
405 
406 !---------------------------------------------------------------------------
407 !> Routine to print the contents of the jacobian for testing
408 !!
409 !! \details Heritage: Ops_SatRad_PrintHMatrix.f90
410 !!
411 !! \author Met Office
412 !!
413 !! \date 09/06/2020: Created
414 !!
416  nchans, & ! in
417  nprofelements, & ! in
418  channels, & ! in
419  H_matrix, & ! in
420  profindex ) ! in
421 
422 implicit none
423 
424 !Subroutine arguments:
425 integer, intent(in) :: nchans
426 integer, intent(in) :: nprofelements
427 integer, intent(in) :: channels(nchans)
428 real(kind_real), intent(in) :: H_matrix(nchans,nprofelements)
429 type(ufo_rttovonedvarcheck_profindex), intent(in) :: profindex
430 
431 ! Local variables:
432 integer :: i
433 character(len=10) :: int_fmt
434 character(len=12) :: real_fmt
435 character(len=3) :: txt_nchans
436 character(len=*), parameter :: RoutineName = "ufo_rttovonedvarcheck_PrintHmatrix"
437 !-------------------------------------------------------------------------------
438 
439 write( unit=txt_nchans,fmt='(i3)' ) nchans
440 write( unit=int_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'I30)'
441 write( unit=real_fmt,fmt='(a)' ) '(' // trim(txt_nchans) // 'E30.15)'
442 
443 write(*,*)
444 
445 write(*, int_fmt) channels(:)
446 
447  if ( profindex % t(1) > 0 ) THEN
448  write(*, '(a)') 'Temperature Profile'
449  do i = profindex%t(1),profindex%t(2)
450  write(*, real_fmt) h_matrix(:,i)
451  end do
452  end if
453 
454  if ( profindex % q(1) > 0 ) THEN
455  write(*, '(a)') 'q Profile'
456  do i = profindex%q(1),profindex%q(2)
457  write(*, real_fmt) h_matrix(:,i)
458  end do
459  end if
460 
461  if ( profindex % qt(1) > 0 ) THEN
462  write(*, '(a)') 'qt Profile /1000'
463  do i = profindex%qt(1),profindex%qt(2)
464  write(*, real_fmt) h_matrix(:,i)/1000
465  end do
466  end if
467 
468  if ( profindex % o3profile(1) > 0 ) THEN
469  write(*, '(a)') 'Ozone Profile'
470  do i = profindex%o3profile(1),profindex%o3profile(2)
471  write(*, real_fmt) h_matrix(:,i)
472  end do
473  end if
474 
475  if ( profindex % o3total > 0 ) THEN
476  write(*, '(a)') 'Total Column Ozone'
477  write(*, real_fmt) h_matrix(:,profindex%o3total)
478  end if
479 
480 
481  if ( profindex % lwp > 0 ) THEN
482  write(*, '(a)') 'LWP'
483  write(*, real_fmt) h_matrix(i,profindex % lwp)
484  end if
485 
486  if ( profindex % t2 > 0 ) THEN
487  write(*, '(a)') '2m T'
488  write(*, real_fmt) h_matrix(:,profindex % t2)
489  end if
490 
491  if ( profindex % q2 > 0 ) THEN
492  write(*, '(a)') '2m q'
493  write(*, real_fmt) h_matrix(:,profindex % q2)
494  end if
495 
496  if ( profindex % pstar > 0 ) THEN
497  write(*, '(a)') 'P Star'
498  write(*, real_fmt) h_matrix(:,profindex % pstar)
499  end if
500 
501  if ( profindex % windspeed > 0 ) THEN
502  write(*, '(a)') 'Windspeed'
503  write(*, real_fmt) h_matrix(:,profindex % windspeed)
504  end if
505 
506  if ( profindex % tstar > 0 ) THEN
507  write(*, '(a)') 'Skin Temperature'
508  write(*, real_fmt) h_matrix(:,profindex % tstar)
509  end if
510 
511  if ( profindex % mwemiss(1) > 0) THEN
512  write(*, '(a)') 'Microwave emissivity retrieval'
513  do i = profindex%mwemiss(1),profindex%mwemiss(2)
514  write(*, real_fmt) h_matrix(:,i)
515  end do
516  end if
517 
518  if ( profindex % cloudtopp > 0 ) THEN
519  write(*, '(a)') 'Cloud top pressure'
520  write(*, real_fmt) h_matrix(:,profindex % cloudtopp)
521  end if
522 
523  if ( profindex % cloudfrac > 0 ) THEN
524  write(*, '(a)') 'Effective cloud fraction'
525  write(*, real_fmt) h_matrix(:,profindex % cloudfrac)
526  end if
527 
528 write(*,*)
529 write(*, '(a)') 'End H-Matrix'
530 write(*, '(a)') '------------------------'
531 
533 
534 ! ------------------------------------------------------------
535 
real(kind_real), parameter, public zero
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module for radiancerttov observation operator.
Fortran module constants used throughout the rttovonedvarcheck filter.
Fortran module to get the jacobian for the 1D-Var.
subroutine ufo_rttovonedvarcheck_printhmatrix(nchans, nprofelements, channels, H_matrix, profindex)
Routine to print the contents of the jacobian for testing.
subroutine, public ufo_rttovonedvarcheck_get_jacobian(self, geovals, ob, channels, profindex, prof_x, hofxdiags, rttov_simobs, hofx, H_matrix)
Get the jacobian used in the 1D-Var.
subroutine ufo_rttovonedvarcheck_gethmatrixrttovsimobs(geovals, ob, obsdb, rttov_data, channels, profindex, hofxdiags, UseQtsplitRain, FullDiagnostics, hofx, H_matrix)
Get the jacobian from rttov and if neccessary convert to variables used in the 1D-Var.
Fortran module which contains the observation metadata for a single observation.
Fortran module containing profile index.
Fortran module containing main type, setup and utilities for the main rttovonedvarcheck object.
Fortran module with various useful routines.
subroutine, public ops_satrad_qsplit(output_type, p, t, qtotal, q, ql, qi, UseQtSplitRain)
Split the humidity into water vapour, liquid water and ice.
character(len=maxvarlen), parameter, public var_clw
character(len=maxvarlen), parameter, public var_sfc_v10
character(len=maxvarlen), parameter, public var_prs
character(len=maxvarlen), parameter, public var_q
character(len=maxvarlen), parameter, public var_sfc_u10
character(len=maxvarlen), parameter, public var_sfc_q2m
character(len=maxvarlen), parameter, public var_sfc_tskin
character(len=maxvarlen), parameter, public var_sfc_p2m
character(len=maxvarlen), parameter, public var_ts
character(len=maxvarlen), parameter, public var_cli
character(len=maxvarlen), parameter, public var_sfc_t2m
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.