UFO
ufo_atmsfcinterp_mod.F90
Go to the documentation of this file.
1 ! (C) Copyright 2017-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 for atmsfcinterp observation operator
7 
9 
10  use oops_variables_mod
11  use ufo_vars_mod
12  use kinds
13 
14  implicit none
15  private
16 
17  !> Fortran derived type for the observation type
18  type, public :: ufo_atmsfcinterp
19  private
20  type(oops_variables), public :: obsvars ! Variables to be simulated
21  integer, allocatable, public :: obsvarindices(:) ! Indices of obsvars in the list of all
22  ! simulated variables in the ObsSpace
23  type(oops_variables), public :: geovars
24  logical :: use_fact10
25  real(kind_real) :: magl
26  contains
27  procedure :: setup => atmsfcinterp_setup_
28  procedure :: simobs => atmsfcinterp_simobs_
29  end type ufo_atmsfcinterp
30 
31 contains
32 
33 ! ------------------------------------------------------------------------------
34 subroutine atmsfcinterp_setup_(self, f_conf)
35  use fckit_configuration_module, only: fckit_configuration
36  implicit none
37  class(ufo_atmsfcinterp), intent(inout) :: self
38  type(fckit_configuration), intent(in) :: f_conf
39  integer :: nvars, ivar, fact10tmp
40 
41  ! check for if we need to look for wind reduction factor
42  self%use_fact10 = .false.
43  fact10tmp = 0
44  if (f_conf%has("use_fact10")) call f_conf%get_or_die("use_fact10",fact10tmp)
45  if (fact10tmp /= 0) then
46  self%use_fact10 = .true.
47  end if
48 
49  nvars = self%obsvars%nvars()
50  do ivar = 1, nvars
51  call self%geovars%push_back(self%obsvars%variable(ivar))
52  enddo
53 
54  !> add geopotential height
55  call self%geovars%push_back(var_z)
56  !> need skin temperature for near-surface interpolations
57  call self%geovars%push_back(var_sfc_t)
58  !> need surface geopotential height to get difference from phi
59  call self%geovars%push_back(var_sfc_z)
60  !> need surface roughness
61  call self%geovars%push_back(var_sfc_rough)
62  !> need surface and atmospheric pressure for potential temperature
63  call self%geovars%push_back(var_ps)
64  call self%geovars%push_back(var_prs)
65  call self%geovars%push_back(var_prsi)
66  call self%geovars%push_back(var_ts)
67  call self%geovars%push_back(var_tv)
68  call self%geovars%push_back(var_q)
69  call self%geovars%push_back(var_u)
70  call self%geovars%push_back(var_v)
71  call self%geovars%push_back(var_sfc_lfrac)
72  if (self%use_fact10) call self%geovars%push_back(var_sfc_fact10)
73 
74 end subroutine atmsfcinterp_setup_
75 
76 ! ------------------------------------------------------------------------------
77 
78 subroutine atmsfcinterp_simobs_(self, geovals_in, obss, nvars, nlocs, hofx)
84  use ufo_utils_mod, only: cmp_strings
85  use obsspace_mod
86  use iso_c_binding
87  use fckit_log_module, only: fckit_log
88  implicit none
89  class(ufo_atmsfcinterp), intent(in) :: self
90  integer, intent(in) :: nvars, nlocs
91  type(ufo_geovals), intent(in) :: geovals_in
92  real(c_double), intent(inout) :: hofx(nvars, nlocs)
93  type(c_ptr), value, intent(in) :: obss
94  type(ufo_geoval), pointer :: phi, hgt, tsfc, roughlen, psfc, prs, prsi, &
95  tsen, tv, q, u, v, landmask, &
96  profile, rad10
97  type(ufo_geovals):: geovals
98  integer :: ivar, iobs, iobsvar
99  real(kind_real), allocatable :: obselev(:), obshgt(:)
100  real(kind_real), parameter :: minroughlen = 1.0e-4_kind_real
101  character(len=MAXVARLEN) :: geovar
102  character(len=MAXVARLEN) :: var_zdir
103  real(kind_real) :: thv1, thv2, th1, thg, thvg, rib, V2, agl, zbot
104  real(kind_real) :: gzsoz0, gzzoz0
105  real(kind_real) :: redfac, psim, psimz, psih, psihz
106  real(kind_real) :: ttmp1, ttmpg, eg, qg
107  real(kind_real) :: z0, zq0, tvsfc
108  real(kind_real), parameter :: fv = rv/rd - 1.0_kind_real
109  real(kind_real) :: psit, psitz, ust, psiq, psiqz
110  real(kind_real), parameter :: zint0 = 0.01_kind_real ! default roughness over land
111  real(kind_real), parameter :: ka = 2.4e-5_kind_real
112  character(1024) :: debug_msg
113 
114  ! Quickly exit if nlocs is less than one, which happens on many CPUs and
115  ! no observations sent to one of them.
116  if (nlocs .lt. 1) return
117 
118  ! Notes:
119  ! (1) Set desired vertical coordinate direction (top2bottom or bottom2top) based
120  ! on vertical coodinate variable and reload geovals according to the set
121  ! direction
122  ! (2) This is done because this observation operator assumes pressure levels
123  ! are from bottom to top (bottom2top) with model pressure(1) for surface and
124  ! model pressure(nsig+1) for model top
125 
126  call ufo_geovals_copy(geovals_in, geovals) ! dont want to change geovals_in
127  var_zdir = var_prsi ! vertical coordinate variable
128  call ufo_geovals_reorderzdir(geovals, var_zdir, "bottom2top")
129 
130  ! to compute the value near the surface we are going to use
131  ! similarity theory which requires a number of near surface parameters
132  ! that need to be grabbed from GeoVaLs regardless of the observation type
133 
134  ! for low altitudes, we can assume: phi = z
135  ! grabbing geopotential height profile and surface elevation,
136  ! surface temperature, surface roughness length
137  ! surface and profile air pressure
138  ! profiles of tsen, q, u, v, and surface land fraction
139  call ufo_geovals_get_var(geovals, var_z, phi)
140  call ufo_geovals_get_var(geovals, var_sfc_z, hgt)
141  call ufo_geovals_get_var(geovals, var_sfc_t, tsfc)
142  call ufo_geovals_get_var(geovals, var_sfc_rough, roughlen)
143  call ufo_geovals_get_var(geovals, var_ps, psfc)
144  call ufo_geovals_get_var(geovals, var_prs, prs)
145  call ufo_geovals_get_var(geovals, var_prsi, prsi)
146  call ufo_geovals_get_var(geovals, var_ts, tsen)
147  call ufo_geovals_get_var(geovals, var_tv, tv)
148  call ufo_geovals_get_var(geovals, var_q, q)
149  call ufo_geovals_get_var(geovals, var_u, u)
150  call ufo_geovals_get_var(geovals, var_v, v)
151  call ufo_geovals_get_var(geovals, var_sfc_lfrac, landmask)
152  if (self%use_fact10) call ufo_geovals_get_var(geovals, var_sfc_fact10, rad10)
153 
154  ! get station elevation from obs
155  allocate(obselev(nlocs))
156  call obsspace_get_db(obss, "MetaData", "station_elevation", obselev)
157 
158  ! get observation height (above sea level)
159  allocate(obshgt(nlocs))
160  call obsspace_get_db(obss, "MetaData", "height", obshgt)
161 
162  do iobs = 1, nlocs
163  ! minimum roughness length
164  z0 = roughlen%vals(1,iobs)
165  if (z0 < minroughlen) z0 = minroughlen
166  ! roughness length for over water
167  zq0 = zint0
168  if (landmask%vals(1,iobs) < 0.01) zq0 = z0
169 
170  ! get virtual temperature of the ground assuming saturation
171  call gsi_tp_to_qs(tsfc%vals(1,iobs), psfc%vals(1,iobs), eg, qg)
172  tvsfc = tsfc%vals(1,iobs) * (1.0_kind_real + fv * qg)
173 
174  ! get potential temperatures for calculating psi
175  call calc_theta(tv%vals(1,iobs), prs%vals(1,iobs), thv1)
176  call calc_theta(tv%vals(2,iobs), prs%vals(2,iobs), thv2)
177  call calc_theta(tsen%vals(1,iobs), prs%vals(1,iobs), th1)
178  call calc_theta(tsfc%vals(1,iobs), psfc%vals(1,iobs), thg)
179  call calc_theta(tvsfc, psfc%vals(1,iobs), thvg)
180 
181  ! calculate convective velocity
182  call calc_conv_vel_gsi(u%vals(1,iobs), v%vals(1,iobs), thvg, thv1, v2)
183 
184  ! although there could be first height below sea level, it causes floating-pt-except
185  zbot = max(0.1,phi%vals(1,iobs)) ! bottom model level in meters
186  agl = max(1.0, (obshgt(iobs)-obselev(iobs))) ! obs height above ground in meters
187 
188  ! calculate bulk richardson number
189  rib = (grav * zbot / th1) * (thv1 - thvg) / v2
190 
191  gzsoz0 = log(zbot/z0)
192  gzzoz0 = log(agl/z0)
193 
194  ! calculate parameters regardless of variable
195  call calc_psi_vars_gsi(rib, gzsoz0, gzzoz0, thv1, thv2, v2, th1, &
196  thg, zbot, agl, psim, psih, psimz, psihz)
197 
198  do iobsvar = 1, size(self%obsvarindices)
199  ! Get the index of the row of hofx to fill
200  ivar = self%obsvarindices(iobsvar)
201 
202  ! Get the name of input variable in geovals
203  geovar = self%obsvars%variable(iobsvar)
204  ! Get profile for this variable from geovals
205  call ufo_geovals_get_var(geovals, geovar, profile)
206 
207  select case(trim(geovar))
208  case("air_temperature", "virtual_temperature")
209  psit = gzsoz0 - psih
210  psitz = gzzoz0 - psihz
211  if (cmp_strings(geovar, "air_temperature")) then
212  ttmp1 = th1
213  ttmpg = thg
214  else
215  ttmp1 = thv1
216  ttmpg = thvg
217  end if
218  hofx(ivar,iobs) = (ttmpg + (ttmp1 - ttmpg)*psitz/psit)*(psfc%vals(1,iobs)/1.0e5_kind_real)**rd_over_cp
219  case("eastward_wind", "northward_wind")
220  if (self%use_fact10) then ! use provided fact10 from model
221  hofx(ivar,iobs) = profile%vals(1,iobs) * rad10%vals(1,iobs)
222  else ! compute wind reduction factor
223  call sfc_wind_fact_gsi(u%vals(1,iobs), v%vals(1,iobs), tsen%vals(1,iobs), q%vals(1,iobs),&
224  psfc%vals(1,iobs), prsi%vals(1,iobs), prsi%vals(2,iobs),&
225  tsfc%vals(1,iobs), z0, landmask%vals(1,iobs), redfac)
226  hofx(ivar,iobs) = profile%vals(1,iobs) * redfac
227  end if
228  case("specific_humidity")
229  ust = max(0.01_kind_real, von_karman * sqrt(v2) / (gzsoz0 - psim))
230  psiq = log(von_karman*ust*zbot/ka + zbot/zq0) - psih
231  psiqz = log(von_karman*ust*agl/ka + agl/zq0) - psihz
232  hofx(ivar,iobs) = qg + (q%vals(1,iobs) - qg)*psiqz/psiq
233  end select
234  end do
235  end do
236 
237  deallocate(obshgt,obselev)
238 
239 end subroutine atmsfcinterp_simobs_
240 
241 ! ------------------------------------------------------------------------------
242 
243 end module ufo_atmsfcinterp_mod
subroutine calc_conv_vel_gsi(u1, v1, thvg, thv1, V2)
subroutine sfc_wind_fact_gsi(u, v, tsen, q, psfc, prsi1, prsi2, skint, z0, lsmask, f10m)
subroutine calc_psi_vars_gsi(rib, gzsoz0, gzzoz0, thv1, thv2, V2, th1, thg, phi1, obshgt, psim, psih, psimz, psihz)
Fortran module for thermodynamic computations and conversions for use in UFO.
Definition: thermo_utils.F90:8
subroutine gsi_tp_to_qs(t, p, es, qs)
subroutine calc_theta(t_in, p_in, t_out)
Fortran module for atmsfcinterp observation operator.
subroutine atmsfcinterp_setup_(self, f_conf)
subroutine atmsfcinterp_simobs_(self, geovals_in, obss, nvars, nlocs, hofx)
real(kind_real), parameter, public grav
real(kind_real), parameter, public von_karman
real(kind_real), parameter, public rd
real(kind_real), parameter, public rd_over_cp
real(kind_real), parameter, public rv
subroutine, public ufo_geovals_reorderzdir(self, varname, zdir)
subroutine, public ufo_geovals_copy(self, other)
Copy one GeoVaLs object into another.
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_sfc_rough
character(len=maxvarlen), parameter, public var_prsi
character(len=maxvarlen), parameter, public var_sfc_lfrac
character(len=maxvarlen), parameter, public var_prs
character(len=maxvarlen), parameter, public var_q
character(len=maxvarlen), parameter, public var_v
character(len=maxvarlen), parameter, public var_z
character(len=maxvarlen), parameter, public var_ts
character(len=maxvarlen), parameter, public var_u
character(len=maxvarlen), parameter, public var_ps
character(len=maxvarlen), parameter, public var_tv
character(len=maxvarlen), parameter, public var_sfc_z
character(len=maxvarlen), parameter, public var_sfc_t
character(len=maxvarlen), parameter, public var_sfc_fact10
Fortran derived type for the observation type.
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators