1module tovsLin_mod
2 ! MODULE tovsLin_mod (prefix='tvslin' category='5. Observation operators')
3 !
4 !:Purpose: Derived types, public variables and procedures related to the
5 ! tangent-linear and adjoint versions of RTTOV
6 !
7 use rttovInterfaces_mod
8 use rttov_types, only : &
9 rttov_profile ,&
10 rttov_profile_cloud ,&
11 rttov_radiance ,&
12 rttov_transmission ,&
13 rttov_chanprof ,&
14 rttov_emissivity
15 use rttov_const, only : &
16 gas_unit_specconc ,&
17 sensor_id_mw ,&
18 surftype_sea ,&
19 errorStatus_success
20 use parkind1, only : jpim, jprb
21 use verticalCoord_mod
22 use tovsNL_mod
23 use utilities_mod
24 use MathPhysConstants_mod
25 use obsSpaceData_mod
26 use columnData_mod
27
28 implicit none
29 save
30 private
31
32 public :: tvslin_rttov_tl, tvslin_rttov_ad
33
34
35contains
36
37 !--------------------------------------------------------------------------
38 ! tvslin_rttov_tl
39 !--------------------------------------------------------------------------
40 subroutine tvslin_rttov_tl(columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData)
41 !
42 !:Purpose: Tangent linear of computation of radiance with rttov_tl
43 !
44 implicit none
45
46 ! Arguments:
47 type(struct_obs), intent(inout) :: obsSpaceData ! obsSpaceData structure
48 type(struct_columnData), intent(in) :: columnAnlInc ! column structure for pertubation profile
49 type(struct_columnData), intent(in) :: columnTrlOnAnlIncLev ! column structure for background profile
50
51 ! Locals:
52 type(struct_vco), pointer :: vco_anl
53 integer, allocatable :: sensorTovsIndexes(:)
54 integer, allocatable :: sensorHeaderIndexes(:)
55 integer :: allocStatus(5)
56 integer :: nobmax
57 integer :: sensorIndex, tovsIndex
58 integer :: ilowlvl_M,ilowlvl_T,profileCount,headerIndex,levelIndex,nlv_M,nlv_T
59 integer :: profileIndex
60 integer :: Vcode
61 character(len=4) :: ozoneVarName
62 logical, allocatable :: surfTypeIsWater(:)
63 real(8), pointer :: delTT(:), delHU(:), delP(:)
64 real(8), pointer :: delO3(:)
65 real(8), pointer :: delCLW(:)
66 real(8), pointer :: delCIW(:), delRF(:), delSF(:)
67 integer :: btCount
68 integer,external :: omp_get_num_threads
69 integer :: nthreads, max_nthreads
70 integer :: btIndex, bodyIndex
71 integer :: instrum
72 integer :: sensorType !sensor type(1=infrared; 2=microwave; 3=high resolution, 4=polarimetric)
73 integer :: errorStatus
74 integer,allocatable :: sensorBodyIndexes(:)
75 logical,allocatable :: lchannel_subset(:,:)
76 real(8), allocatable :: surfem1(:)
77 integer, allocatable :: frequencies(:)
78 type(rttov_emissivity), pointer :: emissivity_local(:)
79 type(rttov_emissivity), pointer :: emissivity_tl(:)
80 type(rttov_radiance) :: radiancedata_d ! radiances full structure buffer used in rttov calls
81 type(rttov_radiance) :: radiancedata_tl ! tl radiances full structure buffer used in rttov calls
82 type(rttov_transmission) :: transmission ! transmission
83 type(rttov_transmission) :: transmission_tl ! transmission tl
84 type(rttov_profile), pointer :: profilesdata_tl(:) ! tl profiles buffer used in rttov calls
85 type(rttov_profile_cloud), pointer :: cld_profiles_tl(:) !tl profiles buffer used in RttovScatt calls
86 type(rttov_chanprof), pointer :: chanprof(:)
87 logical, pointer :: calcemis(:)
88 logical :: runObsOperatorWithClw_tl
89 logical :: runObsOperatorWithHydrometeors_tl
90 integer :: asw
91 real(8) :: obsOMP
92 type (rttov_profile), pointer :: profiles(:)
93 type(rttov_profile_cloud), pointer :: cld_profiles(:)
94
95 if (tvs_nobtov == 0) return ! exit if there are not tovs data
96
97 write(*,*) 'tvslin_rttov_tl: Starting'
98
99 call tvs_getProfile(profiles, 'tlad', cld_profiles)
100
101 if (.not. tvs_useO3Climatology .and. .not. col_varExist(columnTrlOnAnlIncLev,'TO3') .and. .not. col_varExist(columnTrlOnAnlIncLev,'O3L') ) then
102 call utl_abort('tvslin_rttov_tl: if tvs_useO3Climatology is set to .true. the ozone variable must be included as an analysis variable in NAMSTATE.')
103 else if (.not.tvs_useO3Climatology) then
104 if (col_varExist(columnTrlOnAnlIncLev,'TO3')) then
105 ozoneVarName = 'TO3'
106 else
107 ozoneVarName = 'O3L'
108 end if
109 end if
110
111 ! 1. Set index for model's lowest level and model top
112
113 nlv_M = col_getNumLev(columnTrlOnAnlIncLev,'MM')
114 nlv_T = col_getNumLev(columnTrlOnAnlIncLev,'TH')
115
116 if ( col_getPressure(columnTrlOnAnlIncLev,1,1,'TH') < col_getPressure(columnTrlOnAnlIncLev,nlv_T,1,'TH') ) then
117 ilowlvl_M = nlv_M
118 ilowlvl_T = nlv_T
119 else
120 ilowlvl_M = 1
121 ilowlvl_T = 1
122 end if
123
124 vco_anl => col_getVco(columnTrlOnAnlIncLev)
125 Vcode = vco_anl%Vcode
126
127
128 ! 1. Get number of threads available and allocate memory for some variables
129 ! . ----------------------------------------------------------------------
130
131 !$omp parallel
132 max_nthreads = omp_get_num_threads()
133 !$omp end parallel
134
135 allocStatus(:) = 0
136 allocate ( sensorTovsIndexes(tvs_nobtov), stat = allocStatus(1) )
137 call utl_checkAllocationStatus(allocStatus(1:1), ' tvslin_rttov_tl sensorTovsIndexes')
138
139 ! 2. Computation of hx for tovs data only
140
141
142 ! Loop over all sensors specified by user
143
144 sensor_loop: do sensorIndex = 1, tvs_nsensors
145
146 runObsOperatorWithClw_tl = col_varExist(columnTrlOnAnlIncLev,'LWCR') .and. &
147 tvs_isInstrumUsingCLW(tvs_instruments(sensorIndex)) .and. &
148 tvs_mwInstrumUsingCLW_tl
149 runObsOperatorWithHydrometeors_tl = col_varExist(columnTrlOnAnlIncLev,'LWCR') .and. &
150 col_varExist(columnTrlOnAnlIncLev,'IWCR') .and. &
151 tvs_isInstrumUsingHydrometeors(tvs_instruments(sensorIndex)) .and. &
152 tvs_mwInstrumUsingHydrometeors_tl
153
154 sensorType = tvs_coefs(sensorIndex) % coef % id_sensor
155 instrum = tvs_coefs(sensorIndex) % coef % id_inst
156 ! loop over all obs.
157 profileCount = 0
158 do tovsIndex = 1, tvs_nobtov
159 ! Currently processed sensor?
160 if ( tvs_lsensor(tovsIndex) == sensorIndex ) then
161 profileCount = profileCount + 1
162 sensorTovsIndexes(profileCount) = tovsIndex
163 end if
164 end do
165
166 if (profileCount == 0) cycle sensor_loop
167 nobmax = sensorTovsIndexes(profileCount)
168 ! compute the number of calculated radiances for one call
169 btCount = tvs_countRadiances(sensorTovsIndexes(1:profileCount), obsSpaceData, &
170 assim_flag_val_opt=obs_assimilated)
171 if ( btCount == 0 ) cycle sensor_loop
172
173 allocate (sensorHeaderIndexes (profileCount), stat= allocStatus(1))
174 allocate (profilesdata_tl(profileCount), stat= allocStatus(2))
175 if (runObsOperatorWithClw_tl) write(*,*) 'tvslin_rttov_tl: using clw_data'
176 if (runObsOperatorWithHydrometeors_tl) write(*,*) 'tvslin_rttov_tl: using hydrometeor data'
177 allocate (surfTypeIsWater(profileCount),stat= allocStatus(3))
178 call utl_checkAllocationStatus(allocStatus, ' tvslin_rttov_tl')
179
180 sensorHeaderIndexes(:) = 0
181
182 surfTypeIsWater(:) = .false.
183
184 ! allocate profiledata_tl structures
185 asw = 1 ! 1 to allocate
186 call rttov_alloc_tl( &
187 allocStatus(1), &
188 asw, &
189 nprofiles=profileCount, &
190 nchanprof=btCount, &
191 nlevels=nlv_T, &
192 chanprof=chanprof, &
193 opts=tvs_opts(sensorIndex), &
194 profiles_tl=profilesdata_tl, &
195 coefs=tvs_coefs(sensorIndex), &
196 transmission=transmission, &
197 transmission_tl=transmission_tl, &
198 radiance=radiancedata_d, &
199 radiance_tl=radiancedata_tl, &
200 calcemis=calcemis, &
201 emissivity=emissivity_local, &
202 emissivity_tl=emissivity_tl, &
203 init=.true.)
204 if (runObsOperatorWithHydrometeors_tl) then
205 allocate(cld_profiles_tl(profileCount))
206 call rttov_alloc_scatt_prof ( allocStatus(2), &
207 profileCount, &
208 cld_profiles_tl, &
209 nlv_T, &
210 nhydro=5, &
211 nhydro_frac=1, &
212 asw=asw, &
213 init=.false., &
214 flux_conversion=[1,2,0,0,0])
215 end if
216
217 call utl_checkAllocationStatus(allocStatus(1:2), ' tvslin_rtttov_tl rttov_alloc_tl 1')
218
219 profileCount = 0
220
221 obs_loop: do tovsIndex = 1, nobmax
222 if (tvs_lsensor(tovsIndex) /= sensorIndex) cycle obs_loop
223 headerIndex = tvs_headerIndex(tovsIndex)
224 profileCount = profileCount + 1
225 surfTypeIsWater(profileCount) = ( tvs_ChangedStypValue(obsSpaceData,headerIndex) == surftype_sea )
226 sensorHeaderIndexes(profileCount) = headerIndex
227 end do obs_loop
228
229 do profileIndex = 1 , profileCount
230 profilesdata_tl(profileIndex) % gas_units = gas_unit_specconc ! all gas profiles should be provided in kg/kg
231 profilesdata_tl(profileIndex) % nlevels = nlv_T
232 profilesdata_tl(profileIndex) % nlayers = nlv_T - 1
233 if (tvs_coefs(sensorIndex)%coef%nozone > 0) then
234 if (tvs_useO3Climatology) then
235 profilesdata_tl(profileIndex) % o3(:) = 0.0d0
236 else
237 delO3 => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),trim(ozoneVarName))
238 profilesdata_tl(profileIndex) % o3(1:nlv_T) = delO3(1:nlv_T) * 1.0d-9 ! Assumes model ozone in ug/kg
239 profilesdata_tl(profileIndex) % s2m % o = col_getElem(columnAnlInc,ilowlvl_T,sensorHeaderIndexes(profileIndex),trim(ozoneVarName)) * 1.0d-9 ! Assumes model ozone in ug/kg
240 end if
241 end if
242
243 ! using the zero CLW value for land FOV
244 if (runObsOperatorWithClw_tl) then
245 if (surfTypeIsWater(profileIndex)) then
246 delCLW => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'LWCR')
247 profilesdata_tl(profileIndex) % clw(1:nlv_T) = delCLW(:)
248 else
249 profilesdata_tl(profileIndex) % clw(1:nlv_T) = 0.d0
250 end if
251 end if
252
253 if (runObsOperatorWithHydrometeors_tl) then
254 if (surfTypeIsWater(profileIndex)) then
255 ! rain flux
256 if (col_varExist(columnAnlInc,'RF')) then
257 delRF => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'RF')
258 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,1) = delRF(:)
259 else
260 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,1) = 0.0d0
261 end if
262
263 ! snow flux
264 if (col_varExist(columnAnlInc,'SF')) then
265 delSF => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'SF')
266 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,2) = delSF(:)
267 else
268 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,2) = 0.0d0
269 end if
270
271 ! graupel
272 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,3) = 0.d0 ! no information for graupel
273
274 ! cloud liquid water content
275 delCLW => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'LWCR')
276 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,4) = delCLW(:)
277
278 ! cloud ice water content
279 delCIW => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'IWCR')
280 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,5) = delCIW(:)
281 else
282 cld_profiles_tl(profileIndex) % hydro(1:nlv_T,1:5) = 0.d0
283 end if ! surfTypeIsWater
284
285 cld_profiles_tl(profileIndex) % hydro_frac(1:nlv_T,1) = 0.d0 ! no perturbation on cloud fraction as it is a binary variable (or or 1.0) in this implementation
286 end if ! runObsOperatorWithHydrometeors_tl
287
288 profilesdata_tl(profileIndex) % ctp = 0.0d0
289 profilesdata_tl(profileIndex) % cfraction = 0.0d0
290 profilesdata_tl(profileIndex) % zenangle = 0.0d0
291 profilesdata_tl(profileIndex) % azangle = 0.0d0
292 profilesdata_tl(profileIndex) % skin % surftype = 0
293 profilesdata_tl(profileIndex) % skin % t = col_getElem(columnAnlInc,1,sensorHeaderIndexes(profileIndex),'TG')
294 profilesdata_tl(profileIndex) % skin % fastem(:)= 0.0d0
295 profilesdata_tl(profileIndex) % skin % salinity = 0.0d0
296 profilesdata_tl(profileIndex) % s2m % t = col_getElem(columnAnlInc,ilowlvl_T,sensorHeaderIndexes(profileIndex),'TT')
297 profilesdata_tl(profileIndex) % s2m % q = 0.d0
298
299 profilesdata_tl(profileIndex) % s2m % p = col_getElem(columnAnlInc,1,sensorHeaderIndexes(profileIndex),'P0')*MPC_MBAR_PER_PA_R8
300 profilesdata_tl(profileIndex) % s2m % u = col_getElem(columnAnlInc,ilowlvl_M,sensorHeaderIndexes(profileIndex),'UU')
301 profilesdata_tl(profileIndex) % s2m % v = col_getElem(columnAnlInc,ilowlvl_M,sensorHeaderIndexes(profileIndex),'VV')
302
303 delP => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'P_T')
304 profilesdata_tl(profileIndex) % p(1:nlv_T) = delP(:) * MPC_MBAR_PER_PA_R8
305 delTT => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'TT')
306 profilesdata_tl(profileIndex) % t(1:nlv_T) = delTT(:)
307 delHU => col_getColumn(columnAnlInc,sensorHeaderIndexes(profileIndex),'HU')
308 profilesdata_tl(profileIndex) % q(1:nlv_T) = delHU(:)
309 if (runObsOperatorWithHydrometeors_tl) then
310 cld_profiles_tl(profileIndex) % ph (1) = 0.d0
311 cld_profiles_tl(profileIndex) % cfrac = 0.d0
312 do levelIndex = 1, nlv_T - 1
313 cld_profiles_tl(profileIndex) % ph (levelIndex+1) = 0.5d0 * (profilesdata_tl(profileIndex) % p(levelIndex) + profilesdata_tl(profileIndex) % p(levelIndex+1) )
314 end do
315 cld_profiles_tl(profileIndex) % ph (nlv_T+1) = profilesdata_tl(profileIndex) % s2m % p
316 end if
317 end do
318
319 deallocate (sensorHeaderIndexes, stat= allocStatus(1) )
320 deallocate (surfTypeIsWater,stat= allocStatus(2))
321 call utl_checkAllocationStatus(allocStatus, ' tvslin_rttov_tl', .false.)
322
323 ! set nthreads to actual number of threads which will be used.
324
325 nthreads = min(max_nthreads, profileCount)
326
327 ! 2.2 Prepare all input variables required by rttov.
328
329 allocate ( surfem1(btCount) ,stat=allocStatus(1))
330 allocate ( sensorBodyIndexes(btCount) ,stat=allocStatus(2))
331 if (runObsOperatorWithHydrometeors_tl) then
332 allocate (frequencies(btCount), stat=allocStatus(3))
333 end if
334 call utl_checkAllocationStatus(allocStatus(1:3), ' tvslin_rtttov_tl')
335
336 ! get Hyperspecral IR emissivities
337 if ( tvs_isInstrumHyperSpectral(instrum) ) call tvs_getHIREmissivities(sensorTovsIndexes(1:profileCount), &
338 obsSpaceData, surfem1)
339 allocate( lchannel_subset(profileCount,tvs_nchan(sensorIndex)) )
340 call tvs_getChanprof(sensorTovsIndexes(1:profileCount), obsSpaceData, chanprof, &
341 iptobs_cma_opt=sensorBodyIndexes, lchannel_subset_opt = lchannel_subset)
342 if (runObsOperatorWithHydrometeors_tl) then
343 call rttov_scatt_setupindex ( &
344 errorStatus, &
345 profileCount, & ! number of profiles
346 tvs_nchan(sensorIndex), & ! number of channels
347 tvs_coefs(sensorIndex), & ! coef structure read in from rttov coef file
348 tvs_coef_scatt(sensorIndex), & ! coef structure read in from rttov coef file
349 btcount, & ! number of calculated channels
350 chanprof, & ! channels and profile numbers
351 frequencies, & ! array, frequency number for each channel
352 lchannel_subset ) ! OPTIONAL array of logical flags to indicate a subset of channels
353 if (errorStatus /= errorStatus_success) then
354 write(*,*) 'tvslin_rttov_tl: fatal error in rttov_scatt_setupindex ', errorStatus
355 call utl_abort('tvslin_rttov_tl')
356 end if
357 end if
358 deallocate( lchannel_subset )
359
360 call tvs_getOtherEmissivities(chanprof, sensorTovsIndexes, sensorType, instrum, surfem1, calcemis)
361
362 if (sensorType == sensor_id_mw) then
363 call tvs_getMWemissivityFromAtlas(surfem1(1:btcount), emissivity_local, sensorIndex, chanprof, sensorTovsIndexes(1:profileCount))
364 else
365 emissivity_local(:)%emis_in = surfem1(:)
366 end if
367
368 ! 2.3 Compute tl radiance with rttov_tl
369
370 errorStatus = errorStatus_success
371 emissivity_tl(:)%emis_in = 0.0d0
372
373 if (runObsOperatorWithHydrometeors_tl) then
374 call rttov_scatt_tl( &
375 errorStatus, & ! out
376 tvs_opts_scatt(sensorIndex), & ! in
377 nlv_T, & ! in
378 chanprof, & ! in
379 frequencies, & ! in
380 profiles(sensorTovsIndexes(1:profileCount)), & ! in
381 cld_profiles(sensorTovsIndexes(1:profileCount)),& ! in
382 tvs_coefs(sensorIndex), & ! in
383 tvs_coef_scatt(sensorIndex), & ! in
384 calcemis, & ! in
385 emissivity_local, & ! inout
386 profilesdata_tl, & ! in
387 cld_profiles_tl, & ! in
388 emissivity_tl, & ! inout
389 radiancedata_d, & ! inout
390 radiancedata_tl) ! inout
391 else
392 call rttov_parallel_tl( &
393 errorStatus, & ! out
394 chanprof, & ! in
395 tvs_opts(sensorIndex), & ! in
396 profiles(sensorTovsIndexes(1:profileCount)), & ! in
397 profilesdata_tl, & ! inout
398 tvs_coefs(sensorIndex), & ! in
399 transmission, & ! inout
400 transmission_tl, & ! inout
401 radiancedata_d, & ! inout
402 radiancedata_tl, & ! inout
403 calcemis=calcemis, & ! in
404 emissivity=emissivity_local, & ! in
405 emissivity_tl=emissivity_tl, & ! inout
406 nthreads=nthreads ) ! in
407 end if
408
409 if (errorStatus /= errorStatus_success) then
410 write(*,*) 'Error in rttov_parallel_tl', errorStatus
411 write(*,*) 'temperature profile=',profiles(sensorTovsIndexes(1)) % t(:)
412 write(*,*) 'temperature increment profile=',profilesdata_tl(1) % t(:)
413 call utl_abort('tvslin_rttov_tl')
414 end if
415
416 ! 2.4 Store hx in obsSpaceData,OBS_WORK
417
418 do btIndex = 1, btCount
419
420 bodyIndex = sensorBodyIndexes(btIndex)
421 call obs_bodySet_r(obsSpaceData,OBS_WORK,bodyIndex, &
422 radiancedata_tl % bt(btIndex) )
423 if ( tvs_debug ) then
424 obsOMP = obs_bodyElem_r(obsSpaceData,OBS_OMP,bodyIndex)
425 write(*,'(a,i4,2f8.2)') ' ichn,sim,obs= ', &
426 chanprof(btIndex)%chan, radiancedata_tl % bt(btIndex), obsOMP
427 end if
428
429 end do
430
431 ! de-allocate memory
432
433 asw = 0 ! 0 to deallocate
434 if (runObsOperatorWithHydrometeors_tl) then
435 call rttov_alloc_scatt_prof (allocStatus(1), &
436 profileCount, &
437 cld_profiles_tl, &
438 nlv_T, &
439 nhydro=5, &
440 nhydro_frac=1, &
441 asw=asw, &
442 flux_conversion=[1,2,0,0,0])
443 deallocate(cld_profiles_tl)
444 end if
445 call rttov_alloc_tl( &
446 allocStatus(2), &
447 asw, &
448 nprofiles=profileCount, &
449 nchanprof=btCount, &
450 nlevels=nlv_T, &
451 chanprof=chanprof, &
452 opts=tvs_opts(sensorIndex), &
453 profiles_tl=profilesdata_tl, &
454 coefs=tvs_coefs(sensorIndex), &
455 transmission=transmission, &
456 transmission_tl=transmission_tl, &
457 radiance=radiancedata_d, &
458 radiance_tl=radiancedata_tl, &
459 calcemis=calcemis, &
460 emissivity=emissivity_local, &
461 emissivity_tl=emissivity_tl )
462
463 deallocate ( surfem1, stat=allocStatus(3) )
464 if (allocated(frequencies)) deallocate(frequencies, stat=allocStatus(4))
465 deallocate ( sensorBodyIndexes,stat=allocStatus(5) )
466 call utl_checkAllocationStatus(allocStatus(1:5), ' tvslin_rtttov_tl', .false.)
467
468 end do sensor_loop
469
470 deallocate ( sensorTovsIndexes )
471 nullify( profiles )
472 write(*,*) 'tvslin_rttov_tl: Finished'
473
474 end subroutine tvslin_rttov_tl
475
476 !--------------------------------------------------------------------------
477 ! tvslin_rttov_ad
478 !--------------------------------------------------------------------------
479 subroutine tvslin_rttov_ad( columnAnlInc, columnTrlOnAnlIncLev, obsSpaceData )
480 !
481 !:Purpose: Adjoint of computation of radiance with rttov_ad
482 !
483
484 implicit none
485
486 ! Arguments:
487 type(struct_columnData), intent(inout) :: columnAnlInc
488 type(struct_columnData), intent(in) :: columnTrlOnAnlIncLev
489 type(struct_obs), intent(in) :: obsSpaceData
490
491 ! Locals:
492 type(struct_vco), pointer :: vco_anl
493 integer, allocatable :: sensorTovsIndexes(:)
494 integer, allocatable :: sensorHeaderIndexes(:)
495 integer :: allocStatus(17)
496 integer :: omp_get_num_threads, nthreads
497 integer :: nobmax
498 integer :: sensorIndex, tovsIndex
499 integer :: ilowlvl_T,ilowlvl_M,profileCount,headerIndex,nlv_M,nlv_T
500 integer :: profileIndex, levelIndex
501 integer :: Vcode
502 real(8), allocatable :: tt_ad(:,:)
503 real(8), allocatable :: hu_ad(:,:)
504 real(8), allocatable :: pressure_ad(:,:)
505 real(8), allocatable :: ozone_ad(:,:)
506 character(len=4) :: ozoneVarName
507 real(8), allocatable :: clw_ad(:,:)
508 real(8), allocatable :: ciw_ad(:,:), rf_ad(:,:), sf_ad(:,:)
509 logical, allocatable :: surfTypeIsWater(:), lchannel_subset(:,:)
510 real(8), pointer :: uu_column(:),vv_column(:),tt_column(:),hu_column(:),ps_column(:)
511 real(8), pointer :: tg_column(:),p_column(:),o3_column(:),clw_column(:)
512 real(8), pointer :: ciw_column(:), rf_column(:),sf_column(:)
513 integer :: btCount
514 integer :: max_nthreads
515 integer :: instrum
516 integer :: btIndex, bodyIndex
517 integer :: sensorType ! sensor type (1=infrared; 2=microwave; 3=high resolution, 4=polarimetric)
518 integer, allocatable :: sensorBodyIndexes(:)
519 integer :: errorStatus
520 real(8), allocatable :: surfem1(:)
521 integer, allocatable :: frequencies(:)
522 type(rttov_emissivity), pointer :: emissivity_local(:)
523 type(rttov_emissivity), pointer :: emissivity_ad(:)
524 type(rttov_transmission) :: transmission,transmission_ad
525 type(rttov_radiance) :: radiancedata_ad, radiancedata_d
526 type(rttov_profile), pointer :: profilesdata_ad(:) ! ad profiles buffer used in rttov calls
527 type(rttov_profile), pointer :: profiles(:)
528 type(rttov_profile_cloud), pointer :: cld_profiles(:)
529 type(rttov_profile_cloud), pointer :: cld_profiles_ad(:)
530 type(rttov_chanprof), pointer :: chanprof(:)
531 integer :: asw
532 logical, pointer :: calcemis (:)
533 logical :: runObsOperatorWithClw_ad
534 logical :: runObsOperatorWithHydrometeors_ad
535
536 if (tvs_nobtov == 0) return ! exit if there are not tovs data
537 write(*,*) 'tvslin_rttov_ad: Starting'
538
539 call tvs_getProfile(profiles, 'tlad', cld_profiles)
540
541 if (.not. tvs_useO3Climatology .and. .not. col_varExist(columnTrlOnAnlIncLev,'TO3') .and. .not. col_varExist(columnTrlOnAnlIncLev,'O3L') ) then
542 call utl_abort('tvslin_rttov_ad: if tvs_useO3Climatology is set to .true. the ozone variable must be included as an analysis variable in NAMSTATE.')
543 else if (.not.tvs_useO3Climatology) then
544 if (col_varExist(columnTrlOnAnlIncLev,'TO3')) then
545 ozoneVarName = 'TO3'
546 else
547 ozoneVarName = 'O3L'
548 end if
549 end if
550
551 ! 1. Set index for model's lowest level and model top
552
553 nlv_M = col_getNumLev(columnTrlOnAnlIncLev,'MM')
554 nlv_T = col_getNumLev(columnTrlOnAnlIncLev,'TH')
555
556 if ( col_getPressure(columnTrlOnAnlIncLev,1,1,'TH') < col_getPressure(columnTrlOnAnlIncLev,nlv_T,1,'TH') ) then
557 ilowlvl_M = nlv_M
558 ilowlvl_T = nlv_T
559 else
560 ilowlvl_M = 1
561 ilowlvl_T = 1
562 end if
563
564 vco_anl => col_getVco(columnTrlOnAnlIncLev)
565 Vcode = vco_anl%Vcode
566
567 ! 1. Get number of threads available and allocate memory for some variables
568
569 !$omp parallel
570 max_nthreads = omp_get_num_threads()
571 !$omp end parallel
572
573 allocate ( sensorTovsIndexes(tvs_nobtov) )
574
575 ! 2. Computation of adjoint hx for tovs data only
576
577 ! Loop over all sensors specified by user
578
579 sensor_loop:do sensorIndex = 1, tvs_nsensors
580
581 runObsOperatorWithClw_ad = col_varExist(columnTrlOnAnlIncLev,'LWCR') .and. &
582 tvs_isInstrumUsingCLW(tvs_instruments(sensorIndex)) .and. &
583 tvs_mwInstrumUsingCLW_tl
584 runObsOperatorWithHydrometeors_ad = col_varExist(columnTrlOnAnlIncLev,'LWCR') .and. &
585 col_varExist(columnTrlOnAnlIncLev,'IWCR') .and. &
586 tvs_isInstrumUsingHydrometeors(tvs_instruments(sensorIndex)) .and. &
587 tvs_mwInstrumUsingHydrometeors_tl
588
589 sensorType = tvs_coefs(sensorIndex) % coef% id_sensor
590 instrum = tvs_coefs(sensorIndex) % coef% id_inst
591
592 profileCount = 0
593 do tovsIndex = 1, tvs_nobtov
594 ! Currently processed sensor?
595 if ( tvs_lsensor(tovsIndex) == sensorIndex ) then
596 profileCount = profileCount + 1
597 sensorTovsIndexes(profileCount) = tovsIndex
598 end if
599 end do
600
601 if (profileCount == 0) cycle sensor_loop
602
603 nobmax = sensorTovsIndexes(profileCount)
604
605 ! compute the number of radiances/tbs to be calculated
606 btCount = tvs_countRadiances(sensorTovsIndexes(1:profileCount), obsSpaceData)
607
608 if (btCount == 0) cycle sensor_loop
609
610 allocStatus(:) = 0
611 allocate (sensorHeaderIndexes(profileCount), stat= allocStatus(1))
612 allocate (tt_ad (nlv_T,profileCount), stat= allocStatus(2))
613 allocate (hu_ad (nlv_T,profileCount), stat= allocStatus(3))
614 allocate (pressure_ad (nlv_T,profileCount), stat= allocStatus(4))
615 if (.not. tvs_useO3Climatology) then
616 if (tvs_coefs(sensorIndex) %coef %nozone > 0) then
617 allocate (ozone_ad(nlv_T,profileCount), stat= allocStatus(5))
618 end if
619 end if
620 if (runObsOperatorWithClw_ad .or. runObsOperatorWithHydrometeors_ad) then
621 allocate (clw_ad(nlv_T,profileCount), stat= allocStatus(6))
622 end if
623 allocate (surfTypeIsWater(profileCount),stat= allocStatus(7))
624 surfTypeIsWater(:) = .false.
625 if (runObsOperatorWithHydrometeors_ad) then
626 allocate (ciw_ad(nlv_T,profileCount), stat= allocStatus(8))
627 allocate (rf_ad(nlv_T,profileCount), stat= allocStatus(9))
628 allocate (sf_ad(nlv_T,profileCount), stat= allocStatus(10))
629 end if
630
631 call utl_checkAllocationStatus(allocStatus, ' tvslin_rttov_ad')
632
633 ! loop over all obs.
634 profileCount = 0
635
636 ! loop over all obs.
637 obs_loop: do tovsIndex = 1, nobmax
638 if (tvs_lsensor(tovsIndex)/=sensorIndex) cycle obs_loop
639 headerIndex = tvs_headerIndex(tovsIndex)
640 profileCount = profileCount + 1
641 sensorHeaderIndexes(profileCount) = headerIndex
642 end do obs_loop
643
644 ! 2.1 Calculate the actual number of threads which will be used.
645
646 nthreads = min(max_nthreads, profileCount )
647
648 ! 2.2 Prepare all input variables required by rttov_ad.
649
650 asw = 1 ! 1 for allocation, 0 for deallocation
651
652 call rttov_alloc_ad( &
653 allocStatus(1), &
654 asw, &
655 profileCount, &
656 btCount, &
657 nlv_T, &
658 chanprof, &
659 opts=tvs_opts(sensorIndex), &
660 profiles_ad=profilesdata_ad, &
661 coefs=tvs_coefs(sensorIndex), &
662 transmission= transmission, &
663 transmission_ad= transmission_ad,&
664 radiance=radiancedata_d, &
665 radiance_ad=radiancedata_ad, &
666 calcemis=calcemis, &
667 emissivity=emissivity_local, &
668 emissivity_ad=emissivity_ad, &
669 init=.true.)
670
671 allocate (surfem1(btCount), stat=allocStatus(2))
672 if (runObsOperatorWithHydrometeors_ad) allocate ( frequencies(btCount), stat=allocStatus(3))
673 allocate (sensorBodyIndexes(btCount), stat=allocStatus(4))
674 if (runObsOperatorWithHydrometeors_ad) then
675 allocate(cld_profiles_ad(profileCount), stat=allocStatus(5))
676 call rttov_alloc_scatt_prof (allocStatus(6), &
677 profileCount, &
678 cld_profiles_ad, &
679 nlv_T, &
680 nhydro=5, &
681 nhydro_frac=1, &
682 asw=asw, &
683 flux_conversion=[1,2,0,0,0])
684 end if
685 call utl_checkAllocationStatus(allocStatus(1:6), ' tvslin_rttov_ad')
686
687 ! get Hyperspectral IR emissivities
688
689 if ( tvs_isInstrumHyperSpectral(instrum) ) call tvs_getHIREmissivities(sensorTovsIndexes(1:profileCount), obsSpaceData, surfem1)
690
691 ! Build the list of channels/profiles indices
692 allocate( lchannel_subset(profileCount,tvs_nchan(sensorIndex)) )
693 call tvs_getChanprof(sensorTovsIndexes(1:profileCount), obsSpaceData, chanprof, &
694 iptobs_cma_opt = sensorBodyIndexes, lchannel_subset_opt = lchannel_subset)
695 if (runObsOperatorWithHydrometeors_ad) then
696 call rttov_scatt_setupindex ( &
697 errorStatus, &
698 profileCount, & ! number of profiles
699 tvs_nchan(sensorIndex), & ! number of channels
700 tvs_coefs(sensorIndex), & ! coef structure read in from rttov coef file
701 tvs_coef_scatt(sensorIndex), & ! coef structure read in from rttov coef file
702 btcount, & ! number of calculated channels
703 chanprof, & ! channels and profile numbers
704 frequencies, & ! array, frequency number for each channel
705 lchannel_subset ) ! OPTIONAL array of logical flags to indicate a subset of channels
706 if (errorStatus /= errorStatus_success) then
707 write(*,*) 'tvslin_rttov_ad: fatal error in rttov_scatt_setupindex ', errorStatus
708 call utl_abort('tvslin_rttov_ad')
709 end if
710 end if
711 deallocate( lchannel_subset )
712 ! get non Hyperspectral IR emissivities
713 call tvs_getOtherEmissivities(chanprof, sensorTovsIndexes, sensorType, instrum, surfem1, calcemis)
714
715 if (sensorType == sensor_id_mw) then
716 call tvs_getMWemissivityFromAtlas(surfem1(1:btcount), emissivity_local, sensorIndex, chanprof, sensorTovsIndexes(1:profileCount))
717 else
718 emissivity_local(:)%emis_in = surfem1(:)
719 end if
720
721 do btIndex = 1, btCount
722 bodyIndex = sensorBodyIndexes(btIndex)
723 radiancedata_ad % bt( btIndex ) = obs_bodyElem_r(obsSpaceData,OBS_WORK,bodyIndex)
724 end do
725
726 ! 2.3 Compute ad radiance with rttov_ad
727
728 errorStatus = errorStatus_success
729 emissivity_ad(:) % emis_in = 0.0d0
730 emissivity_ad(:) % emis_out = 0.0d0
731
732 if (runObsOperatorWithHydrometeors_ad) then
733 call rttov_scatt_ad( &
734 errorStatus, &! out
735 tvs_opts_scatt(sensorIndex), &! in
736 nlv_T, &! in
737 chanprof, &! in
738 frequencies, &! in
739 profiles(sensorTovsIndexes(1:profileCount)), &! in
740 cld_profiles(sensorTovsIndexes(1:profileCount)),&! in
741 tvs_coefs(sensorIndex), &! in
742 tvs_coef_scatt(sensorIndex), &! in
743 calcemis, &! in
744 emissivity_local, &! inout
745 profilesdata_ad, &! inout
746 cld_profiles_ad, &! inout
747 emissivity_ad, &! inout
748 radiancedata_d, &! inout
749 radiancedata_ad) ! inout
750 else
751 call rttov_parallel_ad( &
752 errorstatus, &! out
753 chanprof, &! in
754 tvs_opts(sensorIndex), &! in
755 profiles(sensorTovsIndexes(1:profileCount)), &! in
756 profilesdata_ad, &! in
757 tvs_coefs(sensorIndex), &! in
758 transmission, &! inout
759 transmission_ad, &! inout
760 radiancedata_d, &! inout
761 radiancedata_ad, &! inout
762 calcemis=calcemis, &! in
763 emissivity=emissivity_local, &! inout
764 emissivity_ad=emissivity_ad, &! inout
765 nthreads = nthreads )
766 end if
767 if (errorStatus /= errorStatus_success) then
768 write(*,*) 'Error in rttov_parallel_ad', errorStatus
769 call utl_abort('tvslin_rttov_ad')
770 end if
771
772 ! 2.0 Store adjoints in columnData object
773 tt_ad(:,:) = 0.d0
774 hu_ad(:,:) = 0.d0
775 pressure_ad(:,:) = 0.d0
776 if (.not. tvs_useO3Climatology) then
777 if (tvs_coefs(sensorIndex) %coef %nozone > 0) ozone_ad(:,:) = 0.d0
778 endif
779 if (runObsOperatorWithClw_ad .or. runObsOperatorWithHydrometeors_ad) clw_ad(:,:) = 0.d0
780 if (runObsOperatorWithHydrometeors_ad) then
781 ciw_ad(:,:) = 0.d0
782 rf_ad(:,:) = 0.d0
783 sf_ad(:,:) = 0.d0
784 end if
785
786 do btIndex = 1, btCount
787 profileIndex = chanprof(btIndex)%prof
788 headerIndex = sensorHeaderIndexes(profileIndex)
789
790 ps_column => col_getColumn(columnAnlInc,headerIndex,'P0')
791 p_column => col_getColumn(columnAnlInc,headerIndex,'P_T')
792 tg_column => col_getColumn(columnAnlInc,headerIndex,'TG')
793 tt_column => col_getColumn(columnAnlInc,headerIndex,'TT')
794 hu_column => col_getColumn(columnAnlInc,headerIndex,'HU')
795 uu_column => col_getColumn(columnAnlInc,headerIndex,'UU')
796 vv_column => col_getColumn(columnAnlInc,headerIndex,'VV')
797
798 tt_ad(:,profileIndex) = profilesdata_ad(profileIndex) % t(:)
799 hu_ad(:,profileIndex) = profilesdata_ad(profileIndex) % q(:)
800 pressure_ad(:,profileIndex) = profilesdata_ad(profileIndex) % p(:)
801 tg_column(1) = profilesdata_ad(profileIndex) % skin % t
802 tt_column(ilowlvl_T) = profilesdata_ad(profileIndex) % s2m % t
803 ps_column(1) = profilesdata_ad(profileIndex) % s2m % p * MPC_MBAR_PER_PA_R8
804 hu_column(ilowlvl_T) = 0.d0
805 uu_column(ilowlvl_M) = profilesdata_ad(profileIndex) % s2m % u
806 vv_column(ilowlvl_M) = profilesdata_ad(profileIndex) % s2m % v
807
808 if (.not. tvs_useO3Climatology) then
809 if (tvs_coefs(sensorIndex) %coef %nozone > 0) then
810 ! This step is just to transfer the value for ilowlvl_T to the memory space defined by 'col_getColumn(...trim(ozoneVarName))
811 o3_column => col_getColumn(columnAnlInc,headerIndex,trim(ozoneVarName))
812 o3_column(ilowlvl_T) = profilesdata_ad(profileIndex) % s2m % o * 1.0d-9
813 ozone_ad(:,profileIndex) = profilesdata_ad(profileIndex) % o3(:)
814 end if
815 end if
816
817 if (runObsOperatorWithClw_ad) then
818 clw_ad(:,profileIndex) = profilesdata_ad(profileIndex) % clw(:)
819 end if
820
821 if (runObsOperatorWithHydrometeors_ad) then
822 rf_ad(:,profileIndex) = cld_profiles_ad(profileIndex) % hydro(:,1)
823 sf_ad(:,profileIndex) = cld_profiles_ad(profileIndex) % hydro(:,2)
824 clw_ad(:,profileIndex) = cld_profiles_ad(profileIndex) % hydro(:,4)
825 ciw_ad(:,profileIndex) = cld_profiles_ad(profileIndex) % hydro(:,5)
826 end if
827 end do
828
829 ! . 2.1 Store adjoints in columnData object
830 ! . -----------------------------------
831
832 do profileIndex = 1 , profileCount
833 ps_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex), 'P0')
834 p_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex), 'P_T')
835 tt_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex), 'TT')
836 hu_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex), 'HU')
837
838 do levelIndex = 1, nlv_T
839 p_column(levelIndex) = p_column(levelIndex) + pressure_ad (levelIndex,profileIndex) * MPC_MBAR_PER_PA_R8
840 tt_column(levelIndex) = tt_column(levelIndex) + tt_ad (levelIndex,profileIndex)
841 hu_column(levelIndex) = hu_column(levelIndex) + hu_ad (levelIndex,profileIndex)
842 end do
843 end do
844
845 if (.not. tvs_useO3Climatology) then
846 if (tvs_coefs(sensorIndex) %coef %nozone > 0) then
847 do profileIndex = 1 , profileCount
848 o3_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),trim(ozoneVarName))
849 do levelIndex = 1, col_getNumLev(columnAnlInc,'TH')
850 o3_column(levelIndex) = o3_column(levelIndex) + ozone_ad(levelIndex,profileIndex) * 1.0d-9
851 end do
852 end do
853 end if
854 end if
855
856 if (runObsOperatorWithClw_ad) then
857 do profileIndex = 1 , profileCount
858 surfTypeIsWater(profileIndex) = ( tvs_ChangedStypValue(obsSpaceData,sensorHeaderIndexes(profileIndex)) == surftype_sea )
859 if (surfTypeIsWater(profileIndex)) then
860 clw_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),'LWCR')
861 do levelIndex = 1, col_getNumLev(columnAnlInc,'TH')
862 clw_column(levelIndex) = clw_column(levelIndex) + &
863 clw_ad(levelIndex,profileIndex)
864 end do
865 end if
866 end do
867 end if
868
869 if (runObsOperatorWithHydrometeors_ad) then
870 do profileIndex = 1 , profileCount
871 surfTypeIsWater(profileIndex) = (tvs_ChangedStypValue(obsSpaceData,sensorHeaderIndexes(profileIndex)) == surftype_sea)
872 if (surfTypeIsWater(profileIndex)) then
873 ! rain flux
874 if (col_varExist(columnAnlInc,'RF')) then
875 rf_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),'RF')
876 do levelIndex = 1, col_getNumLev(columnAnlInc,'TH')
877 rf_column(levelIndex) = rf_column(levelIndex) + rf_ad(levelIndex,profileIndex)
878 end do
879 end if
880
881 ! snow flux
882 if (col_varExist(columnAnlInc,'SF')) then
883 sf_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),'SF')
884 do levelIndex = 1, col_getNumLev(columnAnlInc,'TH')
885 sf_column(levelIndex) = sf_column(levelIndex) + sf_ad(levelIndex,profileIndex)
886 end do
887 end if
888
889 ! cloud liquid/ice water content
890 clw_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),'LWCR')
891 ciw_column => col_getColumn(columnAnlInc, sensorHeaderIndexes(profileIndex),'IWCR')
892 do levelIndex = 1, col_getNumLev(columnAnlInc,'TH')
893 clw_column(levelIndex) = clw_column(levelIndex) + clw_ad(levelIndex,profileIndex)
894 ciw_column(levelIndex) = ciw_column(levelIndex) + ciw_ad(levelIndex,profileIndex)
895 end do
896 end if ! surfTypeIsWater
897 end do ! profileIndex
898 end if ! runObsOperatorWithHydrometeors_ad
899
900 deallocate (sensorHeaderIndexes, stat= allocStatus(1) )
901 deallocate (tt_ad, stat= allocStatus(2) )
902 deallocate (hu_ad, stat= allocStatus(3) )
903 deallocate (pressure_ad, stat= allocStatus(4) )
904 if (.not. tvs_useO3Climatology) then
905 if (tvs_coefs(sensorIndex) %coef %nozone > 0) then
906 deallocate (ozone_ad, stat= allocStatus(5) )
907 end if
908 end if
909 if ( allocated(clw_ad) ) then
910 deallocate (clw_ad,stat=allocStatus(6))
911 end if
912 deallocate (surfTypeIsWater,stat=allocStatus(7))
913 if ( allocated(ciw_ad) ) then
914 deallocate (ciw_ad, stat= allocStatus(8))
915 deallocate (rf_ad, stat= allocStatus(9))
916 deallocate (sf_ad, stat= allocStatus(10))
917 end if
918
919 call utl_checkAllocationStatus(allocStatus, ' tvslin_fill_profiles_ad', .false.)
920
921 ! de-allocate memory
922
923 asw = 0 ! 0 to deallocate
924 if (runObsOperatorWithHydrometeors_ad) then
925 call rttov_alloc_scatt_prof (allocStatus(1), &
926 profileCount, &
927 cld_profiles_ad, &
928 nlv_T, &
929 nhydro=5, &
930 nhydro_frac=1, &
931 asw=asw, &
932 flux_conversion= [1,2,0,0,0])
933 deallocate(cld_profiles_ad, stat=allocStatus(2))
934 end if
935 call rttov_alloc_ad( &
936 allocStatus(3), &
937 asw, &
938 profileCount, &
939 btCount, &
940 nlv_T, &
941 chanprof, &
942 opts=tvs_opts(sensorIndex), &
943 profiles_ad=profilesdata_ad, &
944 coefs=tvs_coefs(sensorIndex), &
945 transmission= transmission, &
946 transmission_ad= transmission_ad,&
947 radiance=radiancedata_d, &
948 radiance_ad=radiancedata_ad, &
949 calcemis=calcemis, &
950 emissivity=emissivity_local, &
951 emissivity_ad=emissivity_ad )
952
953
954 deallocate ( surfem1, stat=allocStatus(4))
955 deallocate ( sensorBodyIndexes, stat=allocStatus(5))
956 if (allocated(frequencies)) deallocate ( frequencies, stat=allocStatus(6))
957 call utl_checkAllocationStatus(allocStatus(1:6), ' tvslin_rttov_ad', .false.)
958
959 end do sensor_loop
960
961 ! 3. Close up
962
963 deallocate ( sensorTovsIndexes )
964 nullify( profiles )
965 write(*,*) 'tvslin_rttov_ad: Finished'
966
967 end subroutine tvslin_rttov_ad
968
969end module tovsLin_mod