UFO
ufo_crtm_utils_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2018 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 to provide code shared between nonlinear and tlm/adm radiance calculations
7 
9 
10 use fckit_configuration_module, only: fckit_configuration
11 use iso_c_binding
12 use kinds
13 
14 use crtm_module
15 
17 use ufo_vars_mod
18 use obsspace_mod
19 use ufo_utils_mod, only: cmp_strings
20 
21 implicit none
22 private
23 
25 public crtm_conf
26 public crtm_conf_setup
27 public crtm_conf_delete
28 public get_var_name
29 public load_atm_data
30 public load_sfc_data
31 public load_geom_data
33 
34 PUBLIC load_aerosol_data
37 PUBLIC :: qsmith
38 PUBLIC :: upper2lower
39 
40 PUBLIC :: grav,rv_rd,&
42 
43 REAL(kind_real), PARAMETER :: &
44  &rdgas = 2.8704e+2_kind_real,&
45  &rvgas = 4.6150e+2_kind_real,&
46  &rv_rd = rvgas/rdgas,&
47  &esl = 0.621971831,&
48  &zvir = rv_rd - 1_kind_real,&
49  &tice = 273.16_kind_real,&
50  &grav = 9.81_kind_real,&
51  &aerosol_concentration_minvalue=1.e-16_kind_real,&
52  &aerosol_concentration_minvalue_layer=tiny(rdgas),&
53  &ozone_default_value=1.e-3_kind_real ! in ppmv in crtm
54 
55 integer, parameter, public :: max_string=800
56 
57 !Type for general config
59  integer :: n_sensors
60  integer :: n_absorbers
61  integer :: n_clouds
62  integer :: n_aerosols
63  integer :: n_surfaces
64  character(len=MAXVARLEN), allocatable :: absorbers(:)
65  integer, allocatable :: absorber_id(:)
66  integer, allocatable :: absorber_units(:)
67  character(len=MAXVARLEN), allocatable :: clouds(:,:)
68  integer, allocatable :: cloud_id(:)
69  character(len=MAXVARLEN), allocatable :: surfaces(:)
70 
71  character(len=255), allocatable :: sensor_id(:)
72  character(len=255) :: endian_type
73  character(len=255) :: coefficient_path
74  character(len=255) :: &
75  irwatercoeff_file, irlandcoeff_file, irsnowcoeff_file, iricecoeff_file, &
76  viswatercoeff_file, vislandcoeff_file, vissnowcoeff_file, visicecoeff_file, &
77  mwwatercoeff_file
78  integer, allocatable :: land_wsi(:)
79  real(kind_real) :: cloud_fraction = -1.0_kind_real
80  integer :: inspect
81  character(len=MAXVARLEN) :: aerosol_option
82  character(len=255) :: salinity_option
83  character(len=MAXVARLEN) :: sfc_wind_geovars
84 end type crtm_conf
85 
87 
90 
91 END INTERFACE
92 
93 INTERFACE qsmith
94 
95  MODULE PROCEDURE qsmith_atm,qsmith_profiles
96 
97 END INTERFACE qsmith
98 
99 
100  ! Add more UFO_Absorbers as needed
101  ! Note: must have same ordering as CRTM_Absorbers,
102  ! CRTM_Absorber_Id, and CRTM_Absorber_Units
103  character(len=MAXVARLEN), parameter :: &
104  ufo_absorbers(3) = &
105  [ var_mixr, var_co2, var_oz ]
106 
107  ! copy of ABSORBER_ID_NAME defined in CRTM_Atmosphere_Define
108  character(len=MAXVARLEN), parameter :: &
109  crtm_absorbers(n_valid_absorber_ids) = &
110  absorber_id_name(1:n_valid_absorber_ids)
111  integer, parameter :: &
112  crtm_absorber_id(n_valid_absorber_ids) = &
113  [ h2o_id, co2_id, o3_id, n2o_id, &
114  co_id, ch4_id, o2_id, no_id, &
115  so2_id, no2_id, nh3_id, hno3_id, &
116  oh_id, hf_id, hcl_id, hbr_id, &
117  hi_id, clo_id, ocs_id, h2co_id, &
118  hocl_id, n2_id, hcn_id, ch3l_id, &
119  h2o2_id, c2h2_id, c2h6_id, ph3_id, &
120  cof2_id, sf6_id, h2s_id,hcooh_id ]
121  integer, parameter :: &
122  crtm_absorber_units(3) = [ &
123  mass_mixing_ratio_units & !H2O
124  , volume_mixing_ratio_units & !CO2
125  , volume_mixing_ratio_units & !O3
126  ]
127 
128  character(len=MAXVARLEN), parameter :: &
129  ufo_clouds(n_valid_cloud_categories,2) = &
130  reshape( &
133  , [n_valid_cloud_categories,2] )
134 
135  ! copy of CLOUD_CATEGORY_NAME defined in CRTM_Cloud_Define
136  character(len=MAXVARLEN), parameter :: &
137  crtm_clouds(n_valid_cloud_categories) = &
138  cloud_category_name(1:n_valid_cloud_categories)
139  integer, parameter :: &
140  crtm_cloud_id(n_valid_cloud_categories) = &
141  [ water_cloud, &
142  ice_cloud, &
143  rain_cloud, &
144  snow_cloud, &
145  graupel_cloud, &
146  hail_cloud ]
147 
148 ! Surface Variables
149 
150  character(len=MAXVARLEN), parameter :: &
151  ufo_surfaces(4) = &
153 
154  character(len=MAXVARLEN), parameter :: &
155  crtm_surfaces(4) = &
156  [ character(len=maxvarlen):: 'Water_Temperature', 'Wind_Speed', 'Wind_Direction', 'Salinity' ]
157 
158  character(len=MAXVARLEN), parameter :: &
159  validsurfacewindgeovars(2) = [character(len=maxvarlen) :: 'vector', 'uv']
160 
161 contains
162 
163 ! ------------------------------------------------------------------------------
164 
165 subroutine crtm_conf_setup(conf, f_confOpts, f_confOper)
166 
167 implicit none
168 type(crtm_conf), intent(inout) :: conf
169 type(fckit_configuration), intent(in) :: f_confopts
170 type(fckit_configuration), intent(in) :: f_confoper
171 
172 character(*), PARAMETER :: routine_name = 'crtm_conf_setup'
173 character(len=255) :: irwatercoeff, viswatercoeff, &
174  irvislandcoeff, irvissnowcoeff, irvisicecoeff, &
175  mwwatercoeff
176 integer :: jspec, ivar
177 character(len=max_string) :: message
178 character(len=:), allocatable :: str
179 character(len=:), allocatable :: str_array(:)
180 
181 CHARACTER(len=MAXVARLEN), ALLOCATABLE :: var_aerosols(:)
182 
183 
184  !Some config needs to come from user
185  !-----------------------------------
186 
187  !Number of sensors, each call to CRTM will be for a single sensor
188  !type (zenith/scan angle will be different)
189  conf%n_Sensors = 1
190 
191  ! absorbers, clouds and aerosols (should match what model will provide)
192 
193  ! Absorbers
194  !----------
195  conf%n_Absorbers = 0
196  if (f_confoper%has("Absorbers")) &
197  conf%n_Absorbers = conf%n_Absorbers + f_confoper%get_size("Absorbers")
198 
199  allocate( conf%Absorbers ( conf%n_Absorbers ), &
200  conf%Absorber_Id ( conf%n_Absorbers ), &
201  conf%Absorber_Units( conf%n_Absorbers ) )
202 
203  if (conf%n_Absorbers > 0) then
204  call f_confoper%get_or_die("Absorbers",str_array)
205  conf%Absorbers(1:conf%n_Absorbers) = str_array
206  end if
207 
208  ! check for duplications
209  do jspec = 2, conf%n_Absorbers
210  if ( any(conf%Absorbers(jspec-1) == conf%Absorbers(jspec:conf%n_Absorbers)) ) then
211  write(message,*) trim(routine_name),' error: ',trim(conf%Absorbers(jspec)),' is duplicated in Absorbers'
212  call abor1_ftn(message)
213  end if
214  end do
215 
216  ! convert from CRTM names to UFO CF names and define Id and Units
217  do jspec = 1, conf%n_Absorbers
218  ivar = ufo_vars_getindex(crtm_absorbers, conf%Absorbers(jspec))
219  if (ivar < 1 .or. ivar > size(ufo_absorbers)) then
220  write(message,*) trim(routine_name),' error: ',trim(conf%Absorbers(jspec)),' not supported by UFO_Absorbers'
221  call abor1_ftn(message)
222  end if
223  conf%Absorbers(jspec) = ufo_absorbers(ivar)
224  conf%Absorber_Id(jspec) = crtm_absorber_id(ivar)
225  conf%Absorber_Units(jspec) = crtm_absorber_units(ivar)
226  end do
227 
228 
229  ! Clouds
230  !-------
231  conf%n_Clouds = 0
232  if (f_confoper%has("Clouds")) &
233  conf%n_Clouds = f_confoper%get_size("Clouds")
234  allocate( conf%Clouds ( conf%n_Clouds,2), &
235  conf%Cloud_Id( conf%n_Clouds ) )
236  if (conf%n_Clouds > 0) then
237  call f_confoper%get_or_die("Clouds",str_array)
238  conf%Clouds(1:conf%n_Clouds,1) = str_array
239 
240  if (f_confoper%has("Cloud_Fraction")) then
241  call f_confoper%get_or_die("Cloud_Fraction",conf%Cloud_Fraction)
242  if ( conf%Cloud_Fraction < 0.0 .or. &
243  conf%Cloud_Fraction > 1.0 ) then
244  write(message,*) trim(routine_name),' error: must specify ' // &
245  ' 0.0 <= Cloud_Fraction <= 1.0' // &
246  ' or remove Cloud_Fraction from conf' // &
247  ' and provide as a geoval'
248  call abor1_ftn(message)
249  end if
250  else
251  message = trim(routine_name) // &
252  ': Cloud_Fraction is not provided in conf.' // &
253  ' Will request as a geoval.'
254  CALL display_message(routine_name, trim(message), warning )
255  end if
256  end if
257 
258  ! check for duplications
259  do jspec = 2, conf%n_Clouds
260  if ( any(conf%Clouds(jspec-1,1) == conf%Clouds(jspec:conf%n_Clouds,1)) ) then
261  write(message,*) trim(routine_name),' error: ',trim(conf%Clouds(jspec,1)), &
262  ' is duplicated in Clouds'
263  call abor1_ftn(message)
264  end if
265  end do
266 
267  ! convert from CRTM names to UFO CF names and define Id
268  do jspec = 1, conf%n_Clouds
269  ivar = ufo_vars_getindex(crtm_clouds, conf%Clouds(jspec,1))
270  if (ivar < 1 .or. ivar > size(ufo_clouds)) then
271  write(message,*) trim(routine_name),' error: ',trim(conf%Clouds(jspec,1)),' not supported by UFO_Clouds'
272  call abor1_ftn(message)
273  end if
274  conf%Clouds(jspec,1:2) = ufo_clouds(ivar,1:2)
275  conf%Cloud_Id(jspec) = crtm_cloud_id(ivar)
276  end do
277 
278  ! Aerosols
279  !---------
280  IF (f_confopts%has("AerosolOption")) THEN
281  call f_confopts%get_or_die("AerosolOption",str)
282  conf%aerosol_option = str
283  conf%aerosol_option = upper2lower(conf%aerosol_option)
284  CALL assign_aerosol_names(conf%aerosol_option,var_aerosols)
285  conf%n_Aerosols=SIZE(var_aerosols)
286  ELSE
287  conf%n_Aerosols = 0
288  conf%aerosol_option = ""
289  ENDIF
290 
291  ! Surface variables
292  !----------
293  conf%n_Surfaces = 0
294  if (f_confoper%has("Surfaces")) &
295  conf%n_Surfaces = conf%n_Surfaces + f_confoper%get_size("Surfaces")
296 
297  allocate( conf%Surfaces ( conf%n_Surfaces ))
298 
299  if (conf%n_Surfaces > 0) then
300  call f_confoper%get_or_die("Surfaces",str_array)
301  conf%Surfaces(1:conf%n_Surfaces) = str_array
302  end if
303 
304  ! check for duplications
305  do jspec = 2, conf%n_Surfaces
306  if ( any(conf%Surfaces(jspec-1) == conf%Surfaces(jspec:conf%n_Surfaces)) ) then
307  write(message,*) 'crtm_conf_setup error: ',trim(conf%Surfaces(jspec)),' is duplicated in Surfaces'
308  call abor1_ftn(message)
309  end if
310  end do
311 
312  ! convert from CRTM names to UFO CF names and define Id and Units
313  do jspec = 1, conf%n_Surfaces
314  ivar = ufo_vars_getindex(crtm_surfaces, conf%Surfaces(jspec))
315  if (ivar < 1 .or. ivar > size(ufo_surfaces)) then
316  write(message,*) 'crtm_conf_setup error: ',trim(conf%Surfaces(jspec)),' not supported by UFO_Surfaces'
317  call abor1_ftn(message)
318  end if
319  conf%Surfaces(jspec) = ufo_surfaces(ivar)
320 
321  end do
322 
323  ! select between two surface wind geovals options
324  ! valid options: vector [default], uv
325  if (f_confoper%get('SurfaceWindGeoVars', str)) then
326  conf%sfc_wind_geovars = str
327  else
328  conf%sfc_wind_geovars = 'vector'
329  endif
330  if (ufo_vars_getindex(validsurfacewindgeovars, conf%sfc_wind_geovars) < 1) then
331  write(message,*) 'crtm_conf_setup error: invalid SurfaceWindGeoVars ',trim(str)
332  call abor1_ftn(message)
333  end if
334 
335  ! Sea_Surface_Salinity
336  !---------
337  IF (f_confopts%get("Salinity",str)) THEN
338  conf%salinity_option = str
339  ELSE
340  conf%salinity_option = 'off'
341  END IF
342 
343  !Allocate SENSOR_ID
344  allocate(conf%SENSOR_ID(conf%n_Sensors))
345 
346  !Get sensor ID from config
347  call f_confopts%get_or_die("Sensor_ID",str)
348  conf%SENSOR_ID(conf%n_Sensors) = str
349 
350  !ENDIAN type
351  call f_confopts%get_or_die("EndianType",str)
352  conf%ENDIAN_TYPE = str
353 
354  !Path to coefficient files
355  call f_confopts%get_or_die("CoefficientPath",str)
356  conf%COEFFICIENT_PATH = str
357 
358  ! Coefficient file prefixes
359  irwatercoeff = "Nalli"
360  if (f_confopts%has("IRwaterCoeff")) then
361  call f_confopts%get_or_die("IRwaterCoeff",str)
362  irwatercoeff = str
363  end if
364  viswatercoeff = "NPOESS"
365  if (f_confopts%has("VISwaterCoeff")) then
366  call f_confopts%get_or_die("VISwaterCoeff",str)
367  viswatercoeff = str
368  end if
369  irvislandcoeff = "NPOESS"
370  if (f_confopts%has("IRVISlandCoeff")) then
371  call f_confopts%get_or_die("IRVISlandCoeff",str)
372  irvislandcoeff = str
373  end if
374  irvissnowcoeff = "NPOESS"
375  if (f_confopts%has("IRVISsnowCoeff")) then
376  call f_confopts%get_or_die("IRVISsnowCoeff",str)
377  irvissnowcoeff = str
378  end if
379  irvisicecoeff = "NPOESS"
380  if (f_confopts%has("IRVISiceCoeff")) then
381  call f_confopts%get_or_die("IRVISiceCoeff",str)
382  irvisicecoeff = str
383  end if
384  mwwatercoeff = "FASTEM6"
385  if (f_confopts%has("MWwaterCoeff")) then
386  call f_confopts%get_or_die("MWwaterCoeff",str)
387  mwwatercoeff = str
388  end if
389 
390  ! Define water, snow, ice (WSI) categories
391  select case (trim(irvislandcoeff))
392  case ("USGS")
393  allocate(conf%Land_WSI(2))
394  conf%Land_WSI(1:2) = (/16,24/)
395  case ("IGBP")
396  allocate(conf%Land_WSI(2))
397  conf%Land_WSI(1:2) = (/15,17/)
398  case default
399  allocate(conf%Land_WSI(1))
400  conf%Land_WSI(1) = -1
401  end select
402 
403  ! IR emissivity coeff files
404  conf%IRwaterCoeff_File = trim(irwatercoeff)//".IRwater.EmisCoeff.bin"
405  conf%IRlandCoeff_File = trim(irvislandcoeff)//".IRland.EmisCoeff.bin"
406  conf%IRsnowCoeff_File = trim(irvissnowcoeff)//".IRsnow.EmisCoeff.bin"
407  conf%IRiceCoeff_File = trim(irvisicecoeff)//".IRice.EmisCoeff.bin"
408 
409  !VIS emissivity coeff files
410  conf%VISwaterCoeff_File = trim(viswatercoeff)//".VISwater.EmisCoeff.bin"
411  conf%VISlandCoeff_File = trim(irvislandcoeff)//".VISland.EmisCoeff.bin"
412  conf%VISsnowCoeff_File = trim(irvissnowcoeff)//".VISsnow.EmisCoeff.bin"
413  conf%VISiceCoeff_File = trim(irvisicecoeff)//".VISice.EmisCoeff.bin"
414 
415  ! MW water emissivity coeff file
416  conf%MWwaterCoeff_File = trim(mwwatercoeff)//".MWwater.EmisCoeff.bin"
417 
418  conf%inspect = 0
419  if (f_confopts%has("InspectProfileNumber")) then
420  call f_confopts%get_or_die("InspectProfileNumber",conf%inspect)
421  endif
422 
423 end subroutine crtm_conf_setup
424 
425 ! -----------------------------------------------------------------------------
426 
427 subroutine crtm_conf_delete(conf)
428 
429 implicit none
430 type(crtm_conf), intent(inout) :: conf
431 
432  deallocate(conf%SENSOR_ID)
433  deallocate(conf%Land_WSI)
434  deallocate(conf%Absorbers)
435  deallocate(conf%Absorber_Id)
436  deallocate(conf%Absorber_Units)
437  deallocate(conf%Clouds)
438  deallocate(conf%Cloud_Id)
439 
440 end subroutine crtm_conf_delete
441 
442 ! ------------------------------------------------------------------------------
443 
444 subroutine crtm_comm_stat_check(stat, PROGRAM_NAME, message, f_comm)
445 use fckit_mpi_module, only: fckit_mpi_comm
446 
447 implicit none
448 
449 integer, intent(in) :: stat
450 character(*), intent(in) :: program_name
451 character(*), intent(in) :: message
452 type(fckit_mpi_comm), intent(in) :: f_comm
453 
454 character(max_string) :: rank_message
455 
456  if ( stat /= success ) THEN
457  write(rank_message,*) trim(message)," on rank ",f_comm%rank()
458  call display_message( program_name, rank_message, failure )
459  call abor1_ftn("Abort from "//program_name)
460  end if
461 
462 end subroutine crtm_comm_stat_check
463 
464 ! ------------------------------------------------------------------------------
465 
466 subroutine ufo_crtm_skip_profiles(n_Profiles,n_Channels,channels,obss,Skip_Profiles)
467 ! Profiles are skipped when the ObsValue of all channels is missing.
468 ! TODO: Use complete QC information
469 ! It would be more comprehensive to use EffectiveQC or EffectiveError. That
470 ! would require those ObsSpace values to be initialized before calls to
471 ! this subroutine within ufo_radiancecrtm_simobs+ufo_radiancecrtm_tlad_settraj.
472 use missing_values_mod
473 
474 implicit none
475 integer, intent(in) :: n_profiles, n_channels
476 type(c_ptr), value, intent(in) :: obss
477 integer(c_int), intent(in) :: channels(:)
478 logical, intent(inout) :: skip_profiles(:)
479 
480 integer :: jprofile, jchannel
481 character(len=MAXVARLEN) :: varname
482 real(kind_real) :: obsval(n_profiles,n_channels)
483 !real(kind_real) :: EffObsErr(n_Profiles,n_Channels)
484 !integer :: EffQC(n_Profiles,n_Channels)
485 
486 real(c_double) :: missing
487 
488  ! Set missing value
489  missing = missing_value(missing)
490 
491  obsval = missing
492 ! EffObsErr = missing
493 ! EffQC = 0
494 
495  do jchannel = 1, n_channels
496  call get_var_name(channels(jchannel),varname)
497  call obsspace_get_db(obss, "ObsValue", varname, obsval(:,jchannel))
498 ! call obsspace_get_db(obss, "EffectiveError", varname, EffObsErr(:,jchannel))
499 ! call obsspace_get_db(obss, "EffectiveQC{iter}", varname, EffQC(:,jchannel))
500  enddo
501 
502  !Loop over all n_Profiles, i.e. number of locations
503  do jprofile = 1, n_profiles
504  skip_profiles(jprofile) = all(obsval(jprofile,:) == missing)
505 ! .OR. all(EffObsErr(jprofile,:) == missing) &
506 ! .OR. all(EffQC(jprofile,:) /= 0)
507  end do
508 
509 end subroutine ufo_crtm_skip_profiles
510 
511 ! ------------------------------------------------------------------------------
512 
513 SUBROUTINE load_atm_data(n_Profiles, n_Layers, geovals, atm, conf)
514 implicit none
515 
516 integer, intent(in) :: n_profiles, n_layers
517 type(ufo_geovals), intent(in) :: geovals
518 type(crtm_atmosphere_type), intent(inout) :: atm(:)
519 type(crtm_conf) :: conf
520 
521 ! Local variables
522 integer :: k1, jspec
523 type(ufo_geoval), pointer :: geoval
524 character(max_string) :: err_msg
525 
526  ! Populate the atmosphere structures for CRTM
527  ! -------------------------------------------
528 
529  call ufo_geovals_get_var(geovals, var_ts, geoval)
530  ! Check model levels is consistent in geovals & crtm
531  if (geoval%nval /= n_layers) then
532  write(err_msg,*) 'Load_Atm_Data error: layers inconsistent!'
533  call abor1_ftn(err_msg)
534  endif
535 
536  do k1 = 1, n_profiles
537  atm(k1)%Temperature(1:n_layers) = geoval%vals(:, k1)
538  end do
539 
540  call ufo_geovals_get_var(geovals, var_prs, geoval)
541  do k1 = 1, n_profiles
542  atm(k1)%Pressure(1:n_layers) = geoval%vals(:, k1) * 0.01 ! to hPa
543  end do
544 
545  call ufo_geovals_get_var(geovals, var_prsi, geoval)
546  do k1 = 1, n_profiles
547  atm(k1)%Level_Pressure(:) = geoval%vals(:, k1) * 0.01 ! to hPa
548  atm(k1)%Climatology = us_standard_atmosphere
549  end do
550 
551  do jspec = 1, conf%n_Absorbers
552  ! O3 Absorber has special treatment for Aerosols
553  if (cmp_strings(conf%Absorbers(jspec), var_oz) .AND. &
554  ufo_vars_getindex(geovals%variables, var_oz) < 0 .AND. &
555  (.NOT. cmp_strings(conf%aerosol_option,""))) then
556  do k1 = 1, n_profiles
557  atm(k1)%Absorber(1:n_layers, jspec) = ozone_default_value
558  end do
559  else
560  call ufo_geovals_get_var(geovals, conf%Absorbers(jspec), geoval)
561  do k1 = 1, n_profiles
562  atm(k1)%Absorber(1:n_layers, jspec) = geoval%vals(:, k1)
563  end do
564  end if
565  do k1 = 1, n_profiles
566  atm(k1)%Absorber_Id(jspec) = conf%Absorber_Id(jspec)
567  atm(k1)%Absorber_Units(jspec) = conf%Absorber_Units(jspec)
568  end do
569  end do
570 
571  do jspec = 1, conf%n_Clouds
572  ! cloud species content
573  CALL ufo_geovals_get_var(geovals, conf%Clouds(jspec,1), geoval)
574  do k1 = 1, n_profiles
575  atm(k1)%Cloud(jspec)%Water_Content = geoval%vals(:, k1)
576  atm(k1)%Cloud(jspec)%Type = conf%Cloud_Id(jspec)
577  end do
578 
579  ! effective radius
580  CALL ufo_geovals_get_var(geovals, conf%Clouds(jspec,2), geoval)
581  do k1 = 1, n_profiles
582  atm(k1)%Cloud(jspec)%Effective_Radius = geoval%vals(:, k1)
583  end do
584  end do
585 
586  ! When n_Clouds>0, Cloud_Fraction must either be provided as geoval or in conf
587  ! If Cloud_Fraction is provided in conf,then use the Cloud_Fraction in conf
588  ! If Cloud_Fraction is not provided in conf , then use the Cloud_Fraction in geoval
589  ! and make sure the cloud fraction interpolated from background is in the valid range [0.0,1.0]
590  if (conf%n_Clouds > 0) then
591  if ( conf%Cloud_Fraction >= 0.0 ) then
592  do k1 = 1, n_profiles
593  atm(k1)%Cloud_Fraction(:) = conf%Cloud_Fraction
594  end do
595  else
596  if ( ufo_vars_getindex(geovals%variables, var_cldfrac) > 0 ) then
597  CALL ufo_geovals_get_var(geovals, var_cldfrac, geoval)
598  do k1 = 1, n_profiles
599  where( geoval%vals(:, k1) < 0_kind_real ) geoval%vals(:, k1) = 0_kind_real
600  where( geoval%vals(:, k1) > 1_kind_real ) geoval%vals(:, k1) = 1_kind_real
601  atm(k1)%Cloud_Fraction(:) = geoval%vals(:, k1)
602  end do
603  end if
604  end if
605  end if
606 
607 end subroutine load_atm_data
608 
609 ! ------------------------------------------------------------------------------
610 
611 subroutine load_sfc_data(n_Profiles, n_Channels, channels, geovals, sfc, chinfo, obss, conf)
612 
613 implicit none
614 
615 integer, intent(in) :: n_profiles, n_channels
616 type(ufo_geovals), intent(in) :: geovals
617 type(crtm_surface_type), intent(inout) :: sfc(:)
618 type(crtm_channelinfo_type), intent(in) :: chinfo(:)
619 type(c_ptr), value, intent(in) :: obss
620 integer(c_int), intent(in) :: channels(:)
621 type(crtm_conf), intent(in) :: conf
622 
623 type(ufo_geoval), pointer :: geoval, u, v
624 integer :: k1, n1
625 integer :: iland
626 
627 ! Surface type definitions for default SfcOptics definitions
628 ! for IR and VIS, this is the NPOESS reflectivities.
629 integer, parameter :: tundra_surface_type = 10 ! NPOESS Land surface type for IR/VIS Land SfcOptics
630 integer, parameter :: scrub_surface_type = 7 ! NPOESS Land surface type for IR/VIS Land SfcOptics
631 integer, parameter :: coarse_soil_type = 1 ! Soil type for MW land SfcOptics
632 integer, parameter :: groundcover_vegetation_type = 7 ! Vegetation type for MW Land SfcOptics
633 integer, parameter :: bare_soil_vegetation_type = 11 ! Vegetation type for MW Land SfcOptics
634 integer, parameter :: sea_water_type = 1 ! Water type for all SfcOptics
635 integer, parameter :: fresh_snow_type = 2 ! NPOESS Snow type for IR/VIS SfcOptics
636 integer, parameter :: fresh_ice_type = 1 ! NPOESS Ice type for IR/VIS SfcOptics
637 
638 character(len=MAXVARLEN) :: varname
639 
640 real(kind_real), allocatable :: obstb(:,:)
641 
642  allocate(obstb(n_profiles, n_channels))
643  obstb = 0.0_kind_real
644 
645  do n1 = 1, n_channels
646  call get_var_name(channels(n1), varname)
647  call obsspace_get_db(obss, "ObsValue", varname, obstb(:, n1))
648  enddo
649 
650  do k1 = 1, n_profiles
651  !Pass sensor information
652  sfc(k1)%sensordata%sensor_id = chinfo(1)%sensor_id
653  sfc(k1)%sensordata%wmo_sensor_id = chinfo(1)%wmo_sensor_id
654  sfc(k1)%sensordata%wmo_satellite_id = chinfo(1)%wmo_satellite_id
655  sfc(k1)%sensordata%sensor_channel = channels
656 
657  !Pass observation value
658  do n1 = 1, n_channels
659  sfc(k1)%sensordata%tb(n1) = obstb(k1, n1)
660  enddo
661 
662  !Water_type
663  sfc(k1)%Water_Type = sea_water_type !** NOTE: need to check how to determine fresh vs sea water types (salinity???)
664  end do
665  deallocate(obstb)
666 
667  if (ufo_vars_getindex(geovals%variables, var_sfc_wspeed) > 0 .and. &
668  ufo_vars_getindex(geovals%variables, var_sfc_wdir) > 0) then
669  ! Directly use model-provided wind speed and direction
670  !Wind_Speed
671  call ufo_geovals_get_var(geovals, var_sfc_wspeed, geoval)
672  do k1 = 1, n_profiles
673  sfc(k1)%Wind_Speed = geoval%vals(1, k1)
674  end do
675 
676  !Wind_Direction
677  call ufo_geovals_get_var(geovals, var_sfc_wdir, geoval)
678  do k1 = 1, n_profiles
679  sfc(k1)%Wind_Direction = geoval%vals(1, k1)
680  end do
681  else if (ufo_vars_getindex(geovals%variables, var_sfc_u) > 0 .and. &
682  ufo_vars_getindex(geovals%variables, var_sfc_v) > 0) then
683  ! Convert 2d wind components to speed and direction
684  call ufo_geovals_get_var(geovals, var_sfc_u, u)
685  call ufo_geovals_get_var(geovals, var_sfc_v, v)
686 
687  !Wind_Speed
688  do k1 = 1, n_profiles
689  sfc(k1)%Wind_Speed = sqrt(u%vals(1, k1)**2 + v%vals(1, k1)**2)
690  end do
691 
692  !Wind_Direction
693  do k1 = 1, n_profiles
694  sfc(k1)%Wind_Direction = uv_to_wdir(u%vals(1, k1), v%vals(1, k1))
695  end do
696  else
697  call abor1_ftn('Load_Sfc_Data error: missing surface wind geovals')
698  end if
699 
700  !Water_Coverage
701  call ufo_geovals_get_var(geovals, var_sfc_wfrac, geoval)
702  do k1 = 1, n_profiles
703  sfc(k1)%Water_Coverage = geoval%vals(1, k1)
704  end do
705 
706  !Water_Temperature
707  call ufo_geovals_get_var(geovals, var_sfc_wtmp, geoval)
708  do k1 = 1, n_profiles
709  sfc(k1)%Water_Temperature = geoval%vals(1, k1)
710  end do
711 
712  !Ice_Coverage
713  call ufo_geovals_get_var(geovals, var_sfc_ifrac, geoval)
714  do k1 = 1, n_profiles
715  sfc(k1)%Ice_Coverage = geoval%vals(1, k1)
716  end do
717 
718  !Ice_Temperature
719  call ufo_geovals_get_var(geovals, var_sfc_itmp, geoval)
720  do k1 = 1, n_profiles
721  sfc(k1)%Ice_Temperature = geoval%vals(1, k1)
722  end do
723 
724  !Snow_Coverage
725  call ufo_geovals_get_var(geovals, var_sfc_sfrac, geoval)
726  do k1 = 1, n_profiles
727  sfc(k1)%Snow_Coverage = geoval%vals(1, k1)
728  end do
729 
730  !Snow_Temperature
731  call ufo_geovals_get_var(geovals, var_sfc_stmp, geoval)
732  do k1 = 1, n_profiles
733  sfc(k1)%Snow_Temperature = geoval%vals(1, k1)
734  end do
735 
736  !Snow_Depth
737  call ufo_geovals_get_var(geovals, var_sfc_sdepth, geoval)
738  do k1 = 1, n_profiles
739  sfc(k1)%Snow_Depth = geoval%vals(1, k1)
740  end do
741 
742  !Land_Coverage
743  call ufo_geovals_get_var(geovals, var_sfc_lfrac, geoval)
744  do k1 = 1, n_profiles
745  sfc(k1)%Land_Coverage = geoval%vals(1, k1)
746  end do
747 
748  !Land_Type
749  ! + used to lookup land sfc emiss. for IR and VIS
750  ! + land sfc emiss. undefined over water/snow/ice
751  call ufo_geovals_get_var(geovals, var_sfc_landtyp, geoval)
752  do k1 = 1, n_profiles
753  iland = int(geoval%vals(1, k1))
754  if (.not.any(iland == conf%Land_WSI)) then
755  sfc(k1)%Land_Type = iland
756  end if
757  end do
758 
759  !Land_Temperature
760  call ufo_geovals_get_var(geovals, var_sfc_ltmp, geoval)
761  do k1 = 1, n_profiles
762  sfc(k1)%Land_Temperature = geoval%vals(1, k1)
763  end do
764 
765  !Lai
766  call ufo_geovals_get_var(geovals, var_sfc_lai, geoval)
767  do k1 = 1, n_profiles
768  sfc(k1)%Lai = geoval%vals(1, k1)
769  end do
770 
771  !Vegetation_Fraction
772  call ufo_geovals_get_var(geovals, var_sfc_vegfrac, geoval)
773  do k1 = 1, n_profiles
774  sfc(k1)%Vegetation_Fraction = geoval%vals(1, k1)
775  end do
776 
777  !Vegetation_Type
778  call ufo_geovals_get_var(geovals, var_sfc_vegtyp, geoval)
779  do k1 = 1, n_profiles
780  sfc(k1)%Vegetation_Type = int(geoval%vals(1, k1))
781  end do
782 
783  !Soil_Type
784  call ufo_geovals_get_var(geovals, var_sfc_soiltyp, geoval)
785  do k1 = 1, n_profiles
786  sfc(k1)%Soil_Type = int(geoval%vals(1, k1))
787  end do
788 
789  !Soil_Moisture_Content
790  call ufo_geovals_get_var(geovals, var_sfc_soilm, geoval)
791  do k1 = 1, n_profiles
792  sfc(k1)%Soil_Moisture_Content = geoval%vals(1, k1)
793  end do
794 
795  !Soil_Temperature
796  call ufo_geovals_get_var(geovals, var_sfc_soilt, geoval)
797  do k1 = 1, n_profiles
798  sfc(k1)%Soil_Temperature = geoval%vals(1, k1)
799  end do
800 
801  !Sea_Surface_Salinity
802  if (cmp_strings(conf%salinity_option, "on")) THEN
803  call ufo_geovals_get_var(geovals, var_sfc_sss, geoval)
804  do k1 = 1, n_profiles
805  sfc(k1)%Salinity = geoval%vals(1, k1)
806  end do
807  end if
808 
809  ! Update Ice_Coverage and Land_Coverage for
810  ! Soil_Type or Vegetation_Type corresponding to Glacial land ice
811  do k1 = 1, n_profiles
812  if (sfc(k1)%Land_Coverage > zero .and. &
813  (sfc(k1)%Soil_Type == 9 .or. sfc(k1)%Vegetation_Type == 13)) then
814  sfc(k1)%Ice_Coverage = min(sfc(k1)%Ice_Coverage + sfc(k1)%Land_Coverage, one)
815  sfc(k1)%Land_Coverage = zero
816  end if
817  end do
818 
819 end subroutine load_sfc_data
820 
821 ! ------------------------------------------------------------------------------
822 
823 subroutine load_geom_data(obss,geo,geo_hf)
824 
825 implicit none
826 type(c_ptr), value, intent(in) :: obss
827 type(crtm_geometry_type), intent(inout) :: geo(:)
828 type(crtm_geometry_type), intent(inout), optional :: geo_hf(:)
829 real(kind_real), allocatable :: tmpvar(:)
830 integer :: nlocs
831 character(kind=c_char,len=101) :: obsname
832 
833  call obsspace_obsname(obss, obsname)
834  nlocs = obsspace_get_nlocs(obss)
835  allocate(tmpvar(nlocs))
836 
837  call obsspace_get_db(obss, "MetaData", "sensor_zenith_angle", tmpvar)
838  geo(:)%Sensor_Zenith_Angle = abs(tmpvar(:)) ! needs to be absolute value
839 
840  call obsspace_get_db(obss, "MetaData", "solar_zenith_angle", tmpvar)
841  geo(:)%Source_Zenith_Angle = tmpvar(:)
842 
843  call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle", tmpvar)
844  geo(:)%Sensor_Azimuth_Angle = tmpvar(:)
845 
846  call obsspace_get_db(obss, "MetaData", "solar_azimuth_angle", tmpvar)
847  geo(:)%Source_Azimuth_Angle = tmpvar(:)
848 
849 ! For some microwave instruments the solar and sensor azimuth angles can be
850 ! missing (given a value of 10^11). Set these to zero to get past CRTM QC.
851  where (geo(:)%Source_Azimuth_Angle < 0.0_kind_real .or. &
852  geo(:)%Source_Azimuth_Angle > 360.0_kind_real) &
853  geo(:)%Source_Azimuth_Angle = 0.0_kind_real
854  where (geo(:)%Sensor_Azimuth_Angle < 0.0_kind_real .or. &
855  geo(:)%Sensor_Azimuth_Angle > 360.0_kind_real) &
856  geo(:)%Sensor_Azimuth_Angle = 0.0_kind_real
857 
858  where (abs(geo(:)%Source_Zenith_Angle) > 180.0_kind_real) &
859  geo(:)%Source_Zenith_Angle = 100.0_kind_real
860 
861  call obsspace_get_db(obss, "MetaData", "scan_position", tmpvar)
862  geo(:)%Ifov = tmpvar(:)
863 
864  call obsspace_get_db(obss, "MetaData", "sensor_view_angle", tmpvar) !The Sensor_Scan_Angle is optional
865  geo(:)%Sensor_Scan_Angle = tmpvar(:)
866 
867  where (abs(geo(:)%Sensor_Scan_Angle) > 80.0_kind_real) &
868  geo(:)%Sensor_Scan_Angle = 0.0_kind_real
869 
870 !read geophysical values for gmi high frequency channels 10-13.
871  if (cmp_strings(trim(obsname),'GMI-GPM') .or. cmp_strings(trim(obsname),'gmi_gpm')) then
872  if ( present(geo_hf) ) then
873  geo_hf = geo
874  if (obsspace_has(obss, "MetaData", "sensor_zenith_angle1")) then
875  call obsspace_get_db(obss, "MetaData", "sensor_zenith_angle1", tmpvar)
876  geo_hf(:)%Sensor_Zenith_Angle = abs(tmpvar(:)) ! needs to be absolute value
877  endif
878  if (obsspace_has(obss, "MetaData", "solar_zenith_angle1")) then
879  call obsspace_get_db(obss, "MetaData", "solar_zenith_angle1", tmpvar)
880  geo_hf(:)%Source_Zenith_Angle = tmpvar(:)
881  endif
882  if (obsspace_has(obss, "MetaData", "sensor_azimuth_angle1")) then
883  call obsspace_get_db(obss, "MetaData", "sensor_azimuth_angle1", tmpvar)
884  geo_hf(:)%Sensor_Azimuth_Angle = tmpvar(:)
885  endif
886  if (obsspace_has(obss, "MetaData", "solar_azimuth_angle1")) then
887  call obsspace_get_db(obss, "MetaData", "solar_azimuth_angle1", tmpvar)
888  geo_hf(:)%Source_Azimuth_Angle = tmpvar(:)
889  endif
890  if (obsspace_has(obss, "MetaData", "sensor_view_angle1")) then
891  call obsspace_get_db(obss, "MetaData", "sensor_view_angle1", tmpvar)
892  geo_hf(:)%Sensor_Scan_Angle = tmpvar(:)
893  endif
894  endif
895  endif
896 
897  deallocate(tmpvar)
898 
899 end subroutine load_geom_data
900 
901 ! ------------------------------------------------------------------------------
902 
903 subroutine get_var_name(n,varname)
904 
905 integer, intent(in) :: n
906 character(len=*), intent(out) :: varname
907 
908 character(len=6) :: chan
909 
910  write(chan, '(I0)') n
911  varname = 'brightness_temperature_' // trim(chan)
912 
913 end subroutine get_var_name
914 
915 ! -----------------------------------------------------------------------------
916 
917 !> \brief Determines the wind direction from U and V components
918 !!
919 !! \details **uv_to_wdir** Calculates the wind direction, as measured clockwise
920 !! from north, similar to an azimuth angle. Takes the eastward and northward
921 !! wind component magnitudes, respectively, as arguments.
922 !! Due to the azimuthal convention used here, the inverse equations are:
923 !! u = w * cos(wdir * deg2rad)
924 !! v = w * sin(wdir * deg2rad)
925 !! where w is the wind speed
926 function uv_to_wdir(u, v) result(wdir)
927 
928 use ufo_constants_mod, only: zero, one, two, pi, rad2deg
929 
930 implicit none
931 
932 real(kind=kind_real), intent(in) :: u !< eastward_wind
933 real(kind=kind_real), intent(in) :: v !< northward_wind
934 real(kind=kind_real) :: wdir
935 real(kind=kind_real) :: windratio, windangle
936 integer :: iquadrant
937 real(kind=kind_real),parameter:: windscale = 999999.0_kind_real
938 real(kind=kind_real),parameter:: windlimit = 0.0001_kind_real
939 real(kind=kind_real),parameter:: quadcof(4,2) = &
940  reshape((/zero, one, one, two, &
941  one, -one, one, -one/), (/4,2/))
942 
943  if (u >= zero .and. v >= zero) iquadrant = 1
944  if (u >= zero .and. v < zero) iquadrant = 2
945  if (u < zero .and. v >= zero) iquadrant = 4
946  if (u < zero .and. v < zero) iquadrant = 3
947 
948  if (abs(v) >= windlimit) then
949  windratio = u / v
950  else
951  windratio = zero
952  if (abs(u) > windlimit) then
953  windratio = windscale * u
954  endif
955  endif
956  windangle = atan(abs(windratio)) ! wind azimuth is in radians
957  wdir = ( quadcof(iquadrant, 1) * pi + windangle * quadcof(iquadrant, 2) ) * rad2deg
958 
959 end function uv_to_wdir
960 
961 ! -----------------------------------------------------------------------------
962 
963 SUBROUTINE load_aerosol_data(n_profiles,n_layers,geovals,&
964  &aerosol_option,atm)
965 
966  USE crtm_aerosolcoeff, ONLY: aeroc
967 
968  INTEGER, INTENT(in) :: n_profiles,n_layers
969  TYPE(ufo_geovals), INTENT(in) :: geovals
970  TYPE(crtm_atmosphere_type), INTENT(inout) :: atm(:)
971 
972  CHARACTER(*) :: aerosol_option
973  CHARACTER(max_string) :: message
974  CHARACTER(len=MAXVARLEN) :: varname
975 
976  TYPE(ufo_geoval), POINTER :: geoval
977 
978  CHARACTER(*), PARAMETER :: routine_name = 'Load_Aerosol_Data'
979 
980  REAL(kind_real), DIMENSION(n_layers,n_profiles) :: rh
981  INTEGER :: ivar
982 
983  IF (cmp_strings(aerosol_option, "aerosols_gocart_default")) THEN
984  varname=var_rh
985  CALL ufo_geovals_get_var(geovals, varname, geoval)
986  rh(1:n_layers,1:n_profiles)=geoval%vals(1:n_layers,1:n_profiles)
987  WHERE (rh > 1_kind_real) rh=1_kind_real
989  ELSEIF (cmp_strings(aerosol_option, "aerosols_gocart_merra_2")) THEN
990  varname=var_rh
991  CALL ufo_geovals_get_var(geovals, varname, geoval)
992  rh(1:n_layers,1:n_profiles)=geoval%vals(1:n_layers,1:n_profiles)
993  WHERE (rh > 1_kind_real) rh=1_kind_real
995  ELSEIF (cmp_strings(aerosol_option, "aerosols_other")) THEN
996  CALL assign_other
997  ELSE
998  message = 'this aerosol not implemented - check later'
999  CALL display_message( aerosol_option, message, failure )
1000  stop
1001  ENDIF
1002 
1003  CONTAINS
1004 
1006 
1007  INTEGER, PARAMETER :: ndust_bins=5, nseas_bins=4
1008  REAL(kind_real), DIMENSION(ndust_bins), PARAMETER :: dust_radii=[&
1009  &0.55_kind_real,1.4_kind_real,2.4_kind_real,4.5_kind_real,8.0_kind_real]
1010 
1011  INTEGER, DIMENSION(nseas_bins), PARAMETER :: seas_types=[&
1012  seasalt_ssam_aerosol,seasalt_sscm1_aerosol,seasalt_sscm2_aerosol,seasalt_sscm3_aerosol]
1013 
1014  REAL(kind_real), DIMENSION(n_layers) :: layer_factors
1015 
1016  INTEGER :: i,k,m
1017 
1018  CHARACTER(len=MAXVARLEN) :: varname
1019 
1020  DO m=1,n_profiles
1021 
1022  CALL calculate_aero_layer_factor(atm(m),layer_factors)
1023 
1025 
1026  varname=var_aerosols_gocart_default(i)
1027  CALL ufo_geovals_get_var(geovals,varname, geoval)
1028 
1029  atm(m)%aerosol(i)%Concentration(1:n_layers)=&
1030  &max(geoval%vals(:,m)*layer_factors,aerosol_concentration_minvalue_layer)
1031 
1032  SELECT CASE (trim(varname))
1033  CASE (var_sulfate)
1034  atm(m)%aerosol(i)%type = sulfate_aerosol
1035  DO k=1,n_layers
1036  atm(m)%aerosol(i)%effective_radius(k)=&
1037  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1038  &rh(k,m))
1039  ENDDO
1040 
1041  CASE (var_bcphobic)
1042  atm(m)%aerosol(i)%type = black_carbon_aerosol
1043  atm(m)%aerosol(i)%effective_radius(:)=&
1044  &aeroc%Reff(1,atm(m)%aerosol(i)%type)
1045  CASE (var_bcphilic)
1046  atm(m)%aerosol(i)%type = black_carbon_aerosol
1047  DO k=1,n_layers
1048  atm(m)%aerosol(i)%effective_radius(k)=&
1049  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1050  &rh(k,m))
1051  ENDDO
1052 
1053  CASE (var_ocphobic)
1054  atm(m)%aerosol(i)%type = organic_carbon_aerosol
1055  atm(m)%aerosol(i)%effective_radius(:)=&
1056  &aeroc%Reff(1,atm(m)%aerosol(i)%type)
1057  CASE (var_ocphilic)
1058  atm(m)%aerosol(i)%type = organic_carbon_aerosol
1059  DO k=1,n_layers
1060  atm(m)%aerosol(i)%effective_radius(k)=&
1061  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1062  &rh(k,m))
1063  ENDDO
1064 
1065  CASE (var_du001)
1066  atm(m)%aerosol(i)%type = dust_aerosol
1067  atm(m)%aerosol(i)%effective_radius(:)=dust_radii(1)
1068  CASE (var_du002)
1069  atm(m)%aerosol(i)%type = dust_aerosol
1070  atm(m)%aerosol(i)%effective_radius(:)=dust_radii(2)
1071  CASE (var_du003)
1072  atm(m)%aerosol(i)%type = dust_aerosol
1073  atm(m)%aerosol(i)%effective_radius(:)=dust_radii(3)
1074  CASE (var_du004)
1075  atm(m)%aerosol(i)%type = dust_aerosol
1076  atm(m)%aerosol(i)%effective_radius(:)=dust_radii(4)
1077  CASE (var_du005)
1078  atm(m)%aerosol(i)%type = dust_aerosol
1079  atm(m)%aerosol(i)%effective_radius(:)=dust_radii(5)
1080 
1081  CASE (var_ss001)
1082  atm(m)%aerosol(i)%type = seas_types(1)
1083  DO k=1,n_layers
1084  atm(m)%aerosol(i)%effective_radius(k)=&
1085  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1086  &rh(k,m))
1087  ENDDO
1088  CASE (var_ss002)
1089  atm(m)%aerosol(i)%type = seas_types(2)
1090  DO k=1,n_layers
1091  atm(m)%aerosol(i)%effective_radius(k)=&
1092  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1093  &rh(k,m))
1094  ENDDO
1095  CASE (var_ss003)
1096  atm(m)%aerosol(i)%type = seas_types(3)
1097  DO k=1,n_layers
1098  atm(m)%aerosol(i)%effective_radius(k)=&
1099  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1100  &rh(k,m))
1101  ENDDO
1102  CASE (var_ss004)
1103  atm(m)%aerosol(i)%type = seas_types(4)
1104  DO k=1,n_layers
1105  atm(m)%aerosol(i)%effective_radius(k)=&
1106  &gocart_aerosol_size(atm(m)%aerosol(i)%type, &
1107  &rh(k,m))
1108  ENDDO
1109  END SELECT
1110 
1111  ENDDO
1112 
1113  ENDDO
1114 
1115 
1116  END SUBROUTINE assign_gocart_default
1117 
1119 
1120  message = 'this aerosol not implemented in the CRTM - check later'
1121  CALL display_message( aerosol_option, message, failure )
1122  stop
1123 
1124  END SUBROUTINE assign_gocart_merra_2
1125 
1126  SUBROUTINE assign_other
1127 
1128  message = 'this aerosol not implemented - check later'
1129  CALL display_message( aerosol_option, message, failure )
1130  stop
1131 
1132  END SUBROUTINE assign_other
1133 
1134  END SUBROUTINE load_aerosol_data
1135 
1136  SUBROUTINE assign_aerosol_names(aerosol_option,var_aerosols)
1137 
1138  CHARACTER(*), INTENT(in) :: aerosol_option
1139  CHARACTER(len=MAXVARLEN), ALLOCATABLE, INTENT(out) :: var_aerosols(:)
1140 
1141  CHARACTER(max_string) :: err_msg
1142 
1143  IF (aerosol_option == "aerosols_gocart_default") THEN
1144  ALLOCATE(var_aerosols(SIZE(var_aerosols_gocart_default)))
1145  var_aerosols=var_aerosols_gocart_default
1146  ELSEIF (aerosol_option == "aerosols_gocart_merra_2") THEN
1147  ALLOCATE(var_aerosols(SIZE(var_aerosols_gocart_merra_2)))
1148  var_aerosols=var_aerosols_gocart_merra_2
1149  ELSEIF (aerosol_option == "var_aerosols_other") THEN
1150  ALLOCATE(var_aerosols(SIZE(var_aerosols_other)))
1151  var_aerosols=var_aerosols_other
1152  ELSE
1153  WRITE(err_msg,*) 'assign_aerosol_names: aerosol_option not implemented '//trim(aerosol_option)
1154  call abor1_ftn(err_msg)
1155  END IF
1156 
1157  END SUBROUTINE assign_aerosol_names
1158 
1159  SUBROUTINE calculate_aero_layer_factor_atm_profile(atm,layer_factors)
1160 
1161  TYPE(crtm_atmosphere_type), INTENT(in) :: atm
1162  REAL(kind_real), INTENT(out) :: layer_factors(:)
1163 
1164  INTEGER :: k
1165 
1166  DO k=1,SIZE(layer_factors)
1167 !correct for mixing ratio factor layer_factors
1168 !being calculated from dry pressure, cotton eq. (2.4)
1169 !p_dry=p_total/(1+1.61*mixing_ratio)
1170  layer_factors(k)=1e-9_kind_real*(atm%Level_Pressure(k)-&
1171  &atm%Level_Pressure(k-1))*100_kind_real/grav/&
1172  &(1_kind_real+rv_rd*atm%Absorber(k,1)*1e-3_kind_real)
1173  ENDDO
1174 
1176 
1177  SUBROUTINE calculate_aero_layer_factor_atm(atm,layer_factors)
1178 
1179  TYPE(crtm_atmosphere_type), INTENT(in) :: atm(:)
1180  REAL(kind_real), INTENT(out) :: layer_factors(:,:)
1181 
1182  INTEGER :: k,m
1183 
1184  DO k=1,SIZE(layer_factors,1)
1185  DO m=1,SIZE(layer_factors,2)
1186 !correct for mixing ratio factor layer_factors
1187 !being calculated from dry pressure, cotton eq. (2.4)
1188 !p_dry=p_total/(1+1.61*mixing_ratio)
1189  layer_factors(k,m)=1e-9_kind_real*(atm(m)%Level_Pressure(k)-&
1190  &atm(m)%Level_Pressure(k-1))*100_kind_real/grav/&
1191  &(1_kind_real+rv_rd*atm(m)%Absorber(k,1)*1.e-3_kind_real)
1192  ENDDO
1193  ENDDO
1194 
1195  END SUBROUTINE calculate_aero_layer_factor_atm
1196 
1197  FUNCTION gocart_aerosol_size( itype, rh ) & ! rh input in 0-1
1198  &result(r_eff) ! in micrometer
1199 
1200  USE crtm_aerosolcoeff, ONLY: aeroc
1201  IMPLICIT NONE
1202 
1203 !
1204 ! modified from a function provided by quanhua liu
1205 !
1206  INTEGER ,INTENT(in) :: itype
1207  REAL(kind_real) ,INTENT(in) :: rh
1208 
1209  INTEGER :: j1,j2,m
1210  REAL(kind_real) :: h1
1211  REAL(kind_real) :: r_eff
1212 
1213  j2 = 0
1214  j1 = 1
1215  IF ( rh <= aeroc%rh(1) ) THEN
1216  j1 = 1
1217  ELSE IF ( rh >= aeroc%rh(aeroc%n_rh) ) THEN
1218  j1 = aeroc%n_rh
1219  ELSE
1220  DO m = 1, aeroc%n_rh-1
1221  IF ( rh < aeroc%rh(m+1) .AND. rh > aeroc%rh(m) ) THEN
1222  j1 = m
1223  j2 = m+1
1224  h1 = (rh-aeroc%rh(m))/(aeroc%rh(m+1)-aeroc%rh(m))
1225  EXIT
1226  ENDIF
1227  ENDDO
1228  ENDIF
1229 
1230  IF ( j2 == 0 ) THEN
1231  r_eff = aeroc%reff(j1,itype )
1232  ELSE
1233  r_eff = (1_kind_real-h1)*aeroc%reff(j1,itype ) + h1*aeroc%reff(j2,itype )
1234  ENDIF
1235 
1236  END FUNCTION gocart_aerosol_size
1237 
1238 
1239  FUNCTION upper2lower(str) RESULT(string)
1240 
1241  IMPLICIT NONE
1242 
1243  CHARACTER(*), INTENT(in) :: str
1244  CHARACTER(LEN(str)) :: string
1245 
1246  INTEGER :: ic, i
1247 
1248  CHARACTER(26), PARAMETER :: upper = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
1249  CHARACTER(26), PARAMETER :: lower = 'abcdefghijklmnopqrstuvwxyz'
1250 
1251 ! lowcase each letter if it is lowecase
1252  string = str
1253  DO i = 1, len_trim(str)
1254  ic = index(upper, str(i:i))
1255  IF (ic > 0) string(i:i) = lower(ic:ic)
1256  END DO
1257 
1258  END FUNCTION upper2lower
1259 
1260  INTEGER FUNCTION getindex(names,usrname)
1261  IMPLICIT NONE
1262  CHARACTER(len=*),INTENT(in) :: names(:)
1263  CHARACTER(len=*),INTENT(in) :: usrname
1264  INTEGER i
1265  getindex=-1
1266  DO i=1,SIZE(names)
1267  IF(cmp_strings(usrname, names(i))) THEN
1268  getindex=i
1269  EXIT
1270  ENDIF
1271  ENDDO
1272  END FUNCTION getindex
1273 
1274 !from fv3
1275 
1276  SUBROUTINE qsmith_atm(atm,rh)
1277 
1278  TYPE(crtm_atmosphere_type), INTENT(in) :: atm(:)
1279  REAL(kind_real), INTENT(out),DIMENSION(:,:):: rh
1280 
1281  REAL, ALLOCATABLE :: table(:),des(:)
1282 
1283  REAL es, qs, q
1284  REAL ap1, eps10
1285  REAL Tmin
1286  INTEGER i, k, it, n_layers, n_profiles
1287 
1288  n_layers=SIZE(rh,1)
1289  n_profiles=SIZE(rh,2)
1290 
1291  tmin = tice-160.
1292  eps10 = 10.*esl
1293 
1294  IF( .NOT. ALLOCATED(table) ) CALL qsmith_init(table,des)
1295 
1296  DO k=1,n_layers
1297  DO i=1,n_profiles
1298  ap1 = 10.*dim(atm(i)%Temperature(k), tmin) + 1.
1299  ap1 = min(2621., ap1)
1300  it = ap1
1301  es = table(it) + (ap1-it)*des(it)
1302  q=atm(i)%Absorber(k,1)*1.e-3/(1.+atm(i)%Absorber(k,1)*1.e-3)
1303  qs = esl*es*(1.+zvir*q)/(atm(i)%Pressure(k)*100.)
1304  rh(k,i) = q/qs
1305  ENDDO
1306  ENDDO
1307 
1308  END SUBROUTINE qsmith_atm
1309 
1310  SUBROUTINE qsmith_profiles(t,sphum,p,rh)
1311 
1312  REAL(kind_real), DIMENSION(:,:), INTENT(in) :: t,sphum,p
1313  REAL(kind_real), DIMENSION(:,:), INTENT(out) :: rh
1314 
1315  REAL, ALLOCATABLE :: table(:),des(:)
1316 
1317  REAL es, qs, q
1318  REAL ap1, eps10
1319  REAL Tmin
1320  INTEGER i, k, it, n_layers, n_profiles
1321 
1322  n_layers=SIZE(t,1)
1323  n_profiles=SIZE(t,2)
1324 
1325  tmin = tice-160.
1326  eps10 = 10.*esl
1327 
1328  IF ( .NOT. ALLOCATED(table) ) CALL qsmith_init(table,des)
1329 
1330  DO k=1,n_layers
1331  DO i=1,n_profiles
1332  ap1 = 10.*dim(t(k,i), tmin) + 1.
1333  ap1 = min(2621., ap1)
1334  it = ap1
1335  es = table(it) + (ap1-it)*des(it)
1336  q=sphum(k,i)
1337  qs = esl*es*(1.+zvir*q)/p(k,i)
1338  rh(k,i) = q/qs
1339  ENDDO
1340  ENDDO
1341 
1342  END SUBROUTINE qsmith_profiles
1343 
1344  SUBROUTINE qsmith_init(table,des)
1345 
1346  REAL, ALLOCATABLE, INTENT(out) :: table(:),des(:)
1347  INTEGER, PARAMETER:: length=2621
1348  INTEGER i
1349 
1350  IF( .NOT. ALLOCATED(table) ) THEN
1351 ! Generate es table (dT = 0.1 deg. C)
1352 
1353  ALLOCATE ( table(length) )
1354  ALLOCATE ( des(length) )
1355 
1356  CALL qs_table(length, table)
1357 
1358  DO i=1,length-1
1359  des(i) = table(i+1) - table(i)
1360  ENDDO
1361  des(length) = des(length-1)
1362  ENDIF
1363 
1364  END SUBROUTINE qsmith_init
1365 
1366  SUBROUTINE qs_table(n,table)
1367  INTEGER, INTENT(in):: n
1368  REAL table (n)
1369  REAL :: dt=0.1
1370  REAL esbasw, tbasw, tbasi, Tmin, tem, aa, b, c, d, e
1371  INTEGER i
1372 ! Constants
1373  esbasw = 1013246.0
1374  tbasw = 373.16
1375  tbasi = 273.16
1376  tmin = tbasi - 160.
1377 ! Compute es over water
1378 ! see smithsonian meteorological tables page 350.
1379  DO i=1,n
1380  tem = tmin+dt*real(i-1)
1381  aa = -7.90298*(tbasw/tem-1)
1382  b = 5.02808*alog10(tbasw/tem)
1383  c = -1.3816e-07*(10**((1-tem/tbasw)*11.344)-1)
1384  d = 8.1328e-03*(10**((tbasw/tem-1)*(-3.49149))-1)
1385  e = alog10(esbasw)
1386  table(i) = 0.1*10**(aa+b+c+d+e)
1387  ENDDO
1388 
1389  END SUBROUTINE qs_table
1390 
1391 END MODULE ufo_crtm_utils_mod
1392 
real(kind_real), parameter, public pi
real(kind_real), parameter, public one
real(kind_real), parameter, public zero
real(kind_real), parameter, public two
real(kind_real), parameter, public rad2deg
Fortran module to provide code shared between nonlinear and tlm/adm radiance calculations.
integer, dimension(n_valid_cloud_categories), parameter crtm_cloud_id
subroutine qsmith_profiles(t, sphum, p, rh)
character(len=maxvarlen), dimension(4), parameter ufo_surfaces
subroutine, public crtm_conf_setup(conf, f_confOpts, f_confOper)
real(kind_real), parameter zvir
character(len=maxvarlen), dimension(n_valid_absorber_ids), parameter crtm_absorbers
character(len=maxvarlen), dimension(n_valid_cloud_categories, 2), parameter ufo_clouds
subroutine qs_table(n, table)
real(kind_real), parameter, public grav
integer, parameter, public max_string
integer, dimension(3), parameter crtm_absorber_units
real(kind_real), parameter, public aerosol_concentration_minvalue_layer
subroutine, public get_var_name(n, varname)
character(len=maxvarlen), dimension(2), parameter validsurfacewindgeovars
character(len=maxvarlen), dimension(3), parameter ufo_absorbers
integer function getindex(names, usrname)
character(len=maxvarlen), dimension(n_valid_cloud_categories), parameter crtm_clouds
character(len(str)) function, public upper2lower(str)
real(kind_real), parameter tice
subroutine, public load_aerosol_data(n_profiles, n_layers, geovals, aerosol_option, atm)
real(kind_real), parameter ozone_default_value
subroutine, public load_geom_data(obss, geo, geo_hf)
subroutine calculate_aero_layer_factor_atm_profile(atm, layer_factors)
real(kind_real), parameter, public aerosol_concentration_minvalue
subroutine, public crtm_comm_stat_check(stat, PROGRAM_NAME, message, f_comm)
subroutine, public load_sfc_data(n_Profiles, n_Channels, channels, geovals, sfc, chinfo, obss, conf)
subroutine, public ufo_crtm_skip_profiles(n_Profiles, n_Channels, channels, obss, Skip_Profiles)
subroutine, public assign_aerosol_names(aerosol_option, var_aerosols)
real(kind_real), parameter, public rv_rd
real(kind_real), parameter esl
real(kind_real) function gocart_aerosol_size(itype, rh)
real(kind=kind_real) function uv_to_wdir(u, v)
Determines the wind direction from U and V components.
subroutine qsmith_atm(atm, rh)
subroutine calculate_aero_layer_factor_atm(atm, layer_factors)
subroutine, public crtm_conf_delete(conf)
integer, dimension(n_valid_absorber_ids), parameter crtm_absorber_id
subroutine qsmith_init(table, des)
character(len=maxvarlen), dimension(4), parameter crtm_surfaces
subroutine, public load_atm_data(n_Profiles, n_Layers, geovals, atm, conf)
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module with various useful routines.
logical function, public cmp_strings(str1, str2)
character(len=maxvarlen), parameter, public var_co2
character(len=maxvarlen), parameter, public var_prsi
character(len=maxvarlen), dimension(n_aerosols_other), parameter, public var_aerosols_other
character(len=maxvarlen), parameter, public var_clrefr
character(len=maxvarlen), parameter, public var_clsefr
character(len=maxvarlen), parameter, public var_sfc_ifrac
integer function, public ufo_vars_getindex(vars, varname)
character(len=maxvarlen), parameter, public var_cldfrac
character(len=maxvarlen), parameter, public var_oz
character(len=maxvarlen), parameter, public var_clgefr
character(len=maxvarlen), parameter, public var_clw
character(len=maxvarlen), parameter, public var_sfc_lfrac
character(len=maxvarlen), parameter, public var_cls
character(len=maxvarlen), parameter, public var_sfc_wtmp
character(len=maxvarlen), parameter, public var_prs
character(len=maxvarlen), parameter, public var_rh
character(len=maxvarlen), parameter, public var_du004
character(len=maxvarlen), parameter, public var_sfc_wfrac
character(len=maxvarlen), parameter, public var_sfc_sfrac
character(len=maxvarlen), parameter, public var_sfc_itmp
character(len=maxvarlen), parameter, public var_sfc_wdir
character(len=maxvarlen), parameter, public var_bcphilic
character(len=maxvarlen), parameter, public var_sfc_soilm
integer, parameter, public n_aerosols_gocart_default
character(len=maxvarlen), parameter, public var_clhefr
character(len=maxvarlen), parameter, public var_du005
integer, parameter, public maxvarlen
character(len=maxvarlen), parameter, public var_ocphobic
character(len=maxvarlen), parameter, public var_sfc_u
character(len=maxvarlen), parameter, public var_sfc_sdepth
character(len=maxvarlen), parameter, public var_du003
character(len=maxvarlen), parameter, public var_sfc_landtyp
character(len=maxvarlen), parameter, public var_mixr
character(len=maxvarlen), parameter, public var_clh
character(len=maxvarlen), parameter, public var_du002
character(len=maxvarlen), dimension(n_aerosols_gocart_merra_2), parameter, public var_aerosols_gocart_merra_2
character(len=maxvarlen), parameter, public var_sulfate
character(len=maxvarlen), parameter, public var_bcphobic
character(len=maxvarlen), parameter, public var_sfc_stmp
character(len=maxvarlen), parameter, public var_cliefr
character(len=maxvarlen), parameter, public var_sfc_sss
character(len=maxvarlen), parameter, public var_ts
character(len=maxvarlen), parameter, public var_sfc_lai
character(len=maxvarlen), parameter, public var_ocphilic
character(len=maxvarlen), parameter, public var_clwefr
character(len=maxvarlen), parameter, public var_du001
character(len=maxvarlen), parameter, public var_sfc_v
character(len=maxvarlen), parameter, public var_sfc_vegtyp
character(len=maxvarlen), parameter, public var_sfc_soiltyp
character(len=maxvarlen), parameter, public var_cli
character(len=maxvarlen), dimension(n_aerosols_gocart_default), parameter, public var_aerosols_gocart_default
character(len=maxvarlen), parameter, public var_ss002
character(len=maxvarlen), parameter, public var_sfc_vegfrac
character(len=maxvarlen), parameter, public var_clr
character(len=maxvarlen), parameter, public var_sfc_ltmp
character(len=maxvarlen), parameter, public var_ss004
character(len=maxvarlen), parameter, public var_sfc_soilt
character(len=maxvarlen), parameter, public var_sfc_wspeed
character(len=maxvarlen), parameter, public var_ss003
character(len=maxvarlen), parameter, public var_ss001
character(len=maxvarlen), parameter, public var_clg
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators
subroutine assign_other
subroutine assign_gocart_merra_2
subroutine assign_gocart_default