UFO
ufo_scatwind_neutralmetoffice_mod.F90
Go to the documentation of this file.
1 !-------------------------------------------------------------------------------
2 ! (C) British Crown Copyright 2020 Met Office
3 !
4 ! This software is licensed under the terms of the Apache Licence Version 2.0
5 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
6 !-------------------------------------------------------------------------------
7 
8 !> \brief Fortran module for Met Office scatwind neutral wind forward operator
9 !!
10 !! \details This code introduces the Met Office observation operator for scatterometer
11 !! wind data. We assimilate the data as a "neutral" 10m wind, i.e. where the effects of
12 !! atmospheric stability are neglected. For each observation we calculate the momentum
13 !! roughness length using the Charnock relation. We then calculate the Monin-Obukhov
14 !! stability function for momentum, integrated to the model's lowest wind level.
15 !! The calculations are dependant upon on whether we have stable or unstable conditions
16 !! according to the Obukhov Length. The neutral 10m wind components are then calculated
17 !! from the lowest model level winds.
18 !!
19 !! \author J.Cotton (Met Office)
20 !!
21 !! \date 22/12/2020: Created
22 !!
24 
25 use iso_c_binding
26 use kinds
27 use ufo_vars_mod
30 use ufo_basis_mod, only: ufo_basis
31 use obsspace_mod
32 use oops_variables_mod
33 use missing_values_mod
34 use fckit_log_module, only : fckit_log
35 
36 implicit none
37 private
38 
39  !> Fortran derived type for neutral wind
41  type(oops_variables), public :: geovars
42  type(oops_variables), public :: obsvars
43  contains
45  procedure :: simobs => ufo_scatwind_neutralmetoffice_simobs
47 
48 character(len=maxvarlen), dimension(7), parameter :: geovars_default = (/ &
49  var_u, &
50  var_v, &
51  var_zi, &
52  var_sfc_ifrac, &
53  var_sfc_geomz, &
56 
57 ! ------------------------------------------------------------------------------
58 contains
59 ! ------------------------------------------------------------------------------
60 
61 subroutine ufo_scatwind_neutralmetoffice_setup(self, f_conf)
62  use fckit_configuration_module, only: fckit_configuration
63  implicit none
64  class(ufo_scatwind_neutralmetoffice), intent(inout) :: self
65  type(fckit_configuration), intent(in) :: f_conf
66 
67  call self%geovars%push_back(geovars_default)
68 
70 
71 ! ------------------------------------------------------------------------------
72 !> Neutral wind forward operator for the Met Office system
73 !!
74 !! \author Met Office
75 !!
76 !! \date 22/12/2020: Created
77 !!
78 ! ------------------------------------------------------------------------------
79 subroutine ufo_scatwind_neutralmetoffice_simobs(self, geovals, obss, nvars, &
80  nlocs, hofx)
81  implicit none
82 
83  ! Arguments to this routine
84  class(ufo_scatwind_neutralmetoffice), intent(in) :: self !< The object in which this operator is contained
85  integer, intent(in) :: nvars, nlocs !< The number of variables and locations
86  type(ufo_geovals), intent(in) :: geovals !< The model values, interpolated to the obsevation locations
87  real(c_double), intent(inout) :: hofx(nvars, nlocs) !< The output model equivalent of the observations
88  type(c_ptr), value, intent(in) :: obss !< The observations, and meta-data for those observations
89 
90  character(len=*), parameter :: myname_ = "ufo_scatwind_neutralmetoffice_simobs"
91  integer, parameter :: max_string = 800
92 
93  character(max_string) :: err_msg ! Error message for output
94  character(max_string) :: message ! General message for output
95  integer :: nobs ! Number of observations
96  integer :: iobs ! Loop variable, observation number
97  type(ufo_geoval), pointer :: cx_u ! Model column of eastward wind
98  type(ufo_geoval), pointer :: cx_v ! Model column of northward wind
99  type(ufo_geoval), pointer :: cx_za ! Model heights of wind levels
100  type(ufo_geoval), pointer :: cx_friction_vel ! Model friction velocity
101  type(ufo_geoval), pointer :: cx_obukhov_length ! Model obukhov length
102  type(ufo_geoval), pointer :: cx_orog ! Model orography
103  type(ufo_geoval), pointer :: cx_seaice ! Model sea ice
104  real(kind_real), allocatable :: CDR10(:) ! 10m interpolation coefficients
105 
106  write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: begin"
107  call fckit_log%info(err_msg)
108 
109 ! check if nlocs is consistent in geovals & hofx
110  if (geovals%nlocs /= size(hofx(1,:))) then
111  write(err_msg,*) myname_, ' error: nlocs inconsistent!'
112  call abor1_ftn(err_msg)
113  endif
114 
115  ! check that hofx is the correct size for simulated variables
116  if (size(hofx(:,1)) /= 2) then
117  write(err_msg,*) myname_, ' error: hofx should have 2 variables - eastward_wind and northward_wind'
118  call abor1_ftn(err_msg)
119  endif
120 
121  write(message, *) myname_, ' Running Met Office neutral wind operator'
122  call fckit_log%info(message)
123 
124 ! get variables from geovals
125  call ufo_geovals_get_var(geovals, var_u, cx_u) ! Eastward wind
126  call ufo_geovals_get_var(geovals, var_v, cx_v) ! Northward wind
127  call ufo_geovals_get_var(geovals, var_zi, cx_za) ! Geopotential height of wind levels
128  call ufo_geovals_get_var(geovals, var_sfc_ifrac, cx_seaice) ! Sea ice
129  call ufo_geovals_get_var(geovals, var_sfc_geomz, cx_orog) ! Orography
130  call ufo_geovals_get_var(geovals, var_sea_fric_vel, cx_friction_vel) ! Friction velocity
131  call ufo_geovals_get_var(geovals, var_obk_length, cx_obukhov_length) ! Obukhov length
132 
133  ! Allocate arrays for interpolation weights and initialise to missing data
134  allocate(cdr10(nlocs))
135  cdr10(:) = missing_value(cdr10(1))
136 
137  write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: begin observation loop, nobs = ", nlocs
138  call fckit_log%info(err_msg)
139 
140  obs_loop: do iobs = 1, nlocs
141  call ops_scatwind_forwardmodel(cx_za % vals(:, iobs), &
142  cx_u % vals(:, iobs), &
143  cx_v % vals(:, iobs), &
144  cx_friction_vel % vals(1,iobs), &
145  cx_obukhov_length % vals(1,iobs), &
146  cx_seaice % vals(1,iobs), &
147  cx_orog % vals(1,iobs), &
148  hofx(:,iobs), &
149  cdr10(iobs))
150  end do obs_loop
151 
152  deallocate(cdr10)
153 
154  write(err_msg,*) "TRACE: ufo_scatwind_neutralmetoffice_simobs: completed"
155  call fckit_log%info(err_msg)
156 
158 
159 ! ------------------------------------------------------------------------------
160 !> \brief Scatterometer forward model
161 !!
162 !! \details The main steps are as follows:
163 !! * For each observation we calculate the momentum roughness length using the
164 !! Charnock relation (Charnock, 1955) modified to include low-wind conditions
165 !! (Smith, 1988):
166 !! \f[
167 !! z_{0m(sea)} = \frac{0.11\nu}{1.0 \times 10^{-5} + u_*} +
168 !! \alpha_{ch} \frac{u_*^2}{g}
169 !! \f]
170 !! where
171 !! \f$\nu = 14 \times 10^{-6}\f$ ms-1 is the dynamic viscosity of air,
172 !! \f$u_{*}\f$ is the friction velocity,
173 !! \f$\alpha_{ch} =0.018\f$ is the charnock parameter, and
174 !! \f$g\f$ is the acceleration due to gravity.
175 !! * Call a subroutine to calculate the Monin-Obukhov stability
176 !! function for momentum integrated to the lowest model level,
177 !! \f$\Phi_{m}\f$.
178 !! The calculations are dependant upon on whether we have stable or unstable
179 !! conditions according to the Obukhov Length, \f$L\f$.
180 !! * The neutral 10m wind components, \f$v_{10n}\f$ are then calculated from
181 !! the lowest model level winds, \f$v_1\f$, as
182 !! \f[
183 !! \textbf{v}_{10n}=\frac{\ln\left(\left(10+z_{0m}\right)/z_{0m}\right)}
184 !! {\Phi_m\left(L,z_1+z_{0m},z_{0m}\right)}
185 !! \textbf{v}_1
186 !! \f]
187 !!
188 !! References:
189 !!
190 !! Charnock, H. (1955). Wind stress on a water surface. Quart. J. Royal
191 !! Meteorol. Soc., 81, 639-640.
192 !! Smith, R. N. B. (1988). Coefficient for sea surface wind stress, heat flux
193 !! and wind profiles as a function of wind speed and temperature.
194 !! J. Geophys. Res., 93, 15467-15472.
195 !!
196 !! \author Met Office
197 !!
198 !! \date 22/12/2020: Created
199 !!
200 ! ------------------------------------------------------------------------------
202  u, &
203  v, &
204  ustr, &
205  oblen, &
206  seaice, &
207  orog, &
208  ycalc, &
209  cdr10)
210 
211 use ufo_constants_mod, only: &
212  grav ! Gravitational field strength
213 
214 real(kind_real), intent(in) :: za(:) !< heights of rho levs
215 real(kind_real), intent(in) :: u(:) !< Model eastward wind profile
216 real(kind_real), intent(in) :: v(:) !< Model northward wind profile
217 real(kind_real), intent(in) :: ustr !< Model friction velocity
218 real(kind_real), intent(in) :: oblen !< Model obukhov length
219 real(kind_real), intent(in) :: seaice !< Model sea ice fraction
220 real(kind_real), intent(in) :: orog !< Model orography
221 real(kind_real), intent(inout) :: ycalc(:) !< Model equivalent of the obs
222 real(kind_real), intent(inout) :: cdr10 !< 10m interpolation coefficients
223 !
224 ! Local parameters
225 !
226 integer, parameter :: max_string = 800 ! Length of strings
227 real, parameter :: scatt_height = 10.0 ! height of observation
228 real, parameter :: charnock = 0.018 ! Charnock parameter
229 character(len=*), parameter :: myname_ = "Ops_Scatwind_ForwardModel"
230 character(max_string) :: message ! General message for output
231 !
232 ! Local variables
233 !
234 real :: u1 ! eastward wind on lowest model level
235 real :: v1 ! northward wind on lowest model level
236 real :: oblen_1 ! ObLen checked for very small numbers
237 real :: recip_l_mo ! Reciprocal of ObLen
238 real :: z1_uv ! Height of lowest wind (rho) level
239 real :: z0m ! Roughness length for momentum
240 real :: phi_m ! Monin-Obukhov stability function
241  ! integrated to lowest level
242 real :: phi_m_10 ! Monin-Obukhov stability function
243  ! integrated to 10m
244 real :: phi_mn_10 ! Neutral form of stability
245  ! function integrated to 10m
246 character(max_string) :: err_msg ! Error message to be output
247 
248 if (u(1) == missing_value(u(1))) then ! u wind missing
249  write(message, *) myname_, "Missing value u1"
250  call abor1_ftn(message)
251 end if
252 
253 if (v(1) == missing_value(v(1))) then ! v wind missing
254  write(message, *) myname_, "Missing value v1"
255  call abor1_ftn(message)
256 end if
257 
258 if (za(1) == missing_value(za(1))) then ! height missing
259  write(message, *) myname_, "Missing value z1_uv"
260  call abor1_ftn(message)
261 end if
262 
263 if (oblen == missing_value(oblen)) then ! obukhov length missing
264  write(message, *) myname_, "Missing value obukhov length"
265  call abor1_ftn(message)
266 end if
267 
268 if (ustr == missing_value(ustr)) then ! friction vel missing
269  write(message, *) myname_, "Missing value friction velocity"
270  call abor1_ftn(message)
271 end if
272 
273 if (orog == missing_value(orog)) then ! orogoraphy missing
274  write(message, *) myname_, "Missing value orography"
275  call fckit_log % warning(message)
276 end if
277 
278 if (seaice == missing_value(seaice)) then ! sea ice missing
279  write(message, *) myname_, "Missing value sea ice"
280  call fckit_log % warning(message)
281 end if
282 
283 ! Get u,v wind components on lowest model level
284 u1 = u(1)
285 v1 = v(1)
286 
287 ! Height (m) of lowest wind (rho) level
288 z1_uv = za(1)
289 
290 ! Obukhov length (m) and its reciprocal
291 oblen_1 = sign( max(1.0e-6, abs(oblen)),oblen)
292 recip_l_mo = 1.0 / oblen_1
293 
294 if (orog == 0.0 .and. seaice == 0.0 .and. z1_uv > 0.0) then ! over sea only
295 
296  ! Calculate roughness height for momentum
297  ! Consistent with UMDP24 eqn 125
298  z0m = 1.54e-6 / (1.0e-5 + ustr) + (charnock / grav) * ustr * ustr
299 
300  ! check z0m > 0 before proceeding
301  if (z0m <= 0) then
302  write(message, *) myname_, "Invalid roughness height"
303  call abor1_ftn(message)
304  end if
305 
306  ! Calculate Monin-Obukhov stability function for momentum
307  ! integrated to the model's lowest wind level.
308  call ops_scatwind_phi_m_sea (recip_l_mo, & ! in
309  z1_uv, & ! in
310  z0m, & ! in
311  phi_m) ! out
312 
313  ! Calculate Monin-Obukhov stability function for momentum
314  ! integrated to 10m (in case this is different to level 1)
315  call ops_scatwind_phi_m_sea (recip_l_mo, & ! in
316  scatt_height, & ! in
317  z0m, & ! in
318  phi_m_10) ! out
319 
320  ! Calculate model 10m neutral wind components
321  phi_mn_10 = log((scatt_height + z0m) / z0m)
322 
323  if (phi_m > 0.0) then
324  ! avoid zero divide
325  ycalc(1) = (phi_mn_10 / phi_m) * u1
326  ycalc(2) = (phi_mn_10 / phi_m) * v1
327  end if
328 
329  ! Store 10m interpolation coefficients to go from 10m real
330  ! wind to 10m neutral wind (consistent with VAR using fixed 10m)
331  ! rather than lowest model level
332  if (phi_m_10 > 0.0) then
333  ! avoid zero divide
334  if (cdr10 == missing_value(cdr10)) then
335  ! only store coefficients the 1st time this routine is called
336  cdr10 = (phi_mn_10 / phi_m_10)
337  end if
338 
339  end if
340 
341 else
342  ! ob over land/seaice
343  ycalc(1) = missing_value(ycalc(1))
344  ycalc(2) = missing_value(ycalc(2))
345 
346 end if
347 
348 end subroutine ops_scatwind_forwardmodel
349 
350 ! ------------------------------------------------------------------------------
351 !> \brief Calculate the integrated froms of the Monin-Obukhov stability functions
352 !! for surface exchanges.
353 !!
354 !! \details The main steps are as follows:
355 !! * In neutral conditions we have the logarithmic profile:
356 !! \f[
357 !! \Phi_{mn} = \ln\left(\frac{z_1 + z_{0m}}{z_{0m}}\right)
358 !! \f]
359 !! * In stable conditions the stability functions of Beljaars and Holtslag (1991)
360 !! are used:
361 !! \f[
362 !! \Phi_{m} = \Phi_{mn} +
363 !! a\left(\zeta_1 - \zeta_{0m}\right) +
364 !! b\left(
365 !! \left(\zeta_1 - \frac{c}{d}\right)
366 !! \exp\left(-d\zeta_1\right) -
367 !! \left(\zeta_{0m} - \frac{c}{d}\right)
368 !! \exp\left(-d\zeta_{0m}\right)
369 !! \right)
370 !! \f]
371 !! with
372 !! \f$\zeta_1=(z_1+z_{0m})/L\f$,
373 !! \f$\zeta_{0m}=z_{0m}/L\f$, and
374 !! a=1, b=2/3, c=5, d=0.35.
375 !!
376 !! * In unstable conditions the Dyer and Hicks forms (Dyer, 1974) are used:
377 !! \f[
378 !! \Phi_{m} = \Phi_{mn} -
379 !! 2\ln\left(\frac{1+X_1}{1+X_0}\right) -
380 !! \ln\left(\frac{1+X_1^2}{1+X_0^2}\right) +
381 !! 2\left(\tan^{-1}(X_1) - \tan^{-1}(X_0)\right)
382 !! \f]
383 !! with
384 !! \f$X_1=(1-16\zeta_1)^{1/4}\f$ and
385 !! \f$X_0=(1-16\zeta_{0m})^{1/4}\f$ and
386 !!
387 !! References:
388 !!
389 !! Beljaars, A. C. M. and Holtslag, A. A. M. (1991). Flux parameterisation
390 !! over land surfaces for atmospheric models. J. Appl. Meteor., 30,
391 !! 327-341.
392 !!
393 !! Dyer, A. J., 1974: A review of flux-profile relationships. Bound. Layer
394 !! Meteor., 7, 363-372.
395 !!
396 !! \author Met Office
397 !!
398 !! \date 22/12/2020: Created
399 !!
400 ! ------------------------------------------------------------------------------
401 
402 subroutine ops_scatwind_phi_m_sea (recip_l_mo, &
403  z_uv, &
404  z0m, &
405  phi_m)
406 
407 implicit none
408 ! Subroutine arguments:
409 real, intent(in) :: recip_l_mo !< Reciprocal of Monin-Obukhov length (m^-1).
410 real, intent(in) :: z_uv !< Height of wind level above roughness height(m).
411 real, intent(in) :: z0m !< Roughness length for momentum (m).
412 real, intent(out) :: phi_m !< Stability function for momentum.
413 ! Local declarations:
414 character(len=*), parameter :: RoutineName = 'ops_scatwind_phi_m_sea'
415 real, parameter :: a = 1.0
416 real, parameter :: b = 2.0 / 3.0
417 real, parameter :: c = 5.0
418 real, parameter :: d = 0.35
419 real, parameter :: c_over_d = c / d
420 real :: phi_mn ! Neutral value of stability function for momentum
421 real :: zeta_uv
422 real :: zeta_0m
423 real :: x_uv_sq
424 real :: x_0m_sq
425 real :: x_uv
426 real :: x_0m
427 
428 ! Calculate neutral value of PHI_M
429 phi_mn = log((z_uv + z0m)/z0m)
430 
431 ! Calculate stability parameters
432 zeta_uv = (z_uv + z0m) * recip_l_mo
433 zeta_0m = z0m * recip_l_mo
434 
435 if (recip_l_mo >= 0.0) then
436  ! Calculate PHI_M for neutral and stable conditions.
437  ! Formulation of Beljaars and Holtslag (1991).
438  phi_m = phi_mn + &
439  a * (zeta_uv - zeta_0m) + &
440  b * ((zeta_uv - c_over_d) * exp(-d * zeta_uv) - &
441  (zeta_0m - c_over_d) * exp(-d * zeta_0m))
442 
443 else
444  ! Calculate PHI_M for unstable conditions.
445  x_uv_sq = sqrt(1.0 - 16.0 * zeta_uv)
446  x_0m_sq = sqrt(1.0 - 16.0 * zeta_0m)
447  x_uv = sqrt(x_uv_sq)
448  x_0m = sqrt(x_0m_sq)
449 
450  phi_m = phi_mn - 2.0 * log((1.0 + x_uv) / (1.0 + x_0m)) - &
451  log((1.0 + x_uv_sq) / (1.0 + x_0m_sq)) + &
452  2.0 * (atan(x_uv) - atan(x_0m))
453 
454 end if
455 
456 end subroutine ops_scatwind_phi_m_sea
457 
real(kind_real), parameter, public grav
type(registry_t), public ufo_geovals_registry
Linked list interface - defines registry_t type.
subroutine, public ufo_geovals_get_var(self, varname, geoval)
Fortran module for Met Office scatwind neutral wind forward operator.
subroutine ufo_scatwind_neutralmetoffice_simobs(self, geovals, obss, nvars, nlocs, hofx)
Neutral wind forward operator for the Met Office system.
subroutine ops_scatwind_phi_m_sea(recip_l_mo, z_uv, z0m, phi_m)
Calculate the integrated froms of the Monin-Obukhov stability functions for surface exchanges.
character(len=maxvarlen), dimension(7), parameter geovars_default
subroutine ops_scatwind_forwardmodel(za, u, v, ustr, oblen, seaice, orog, ycalc, cdr10)
Scatterometer forward model.
character(len=maxvarlen), parameter, public var_sfc_ifrac
character(len=maxvarlen), parameter, public var_obk_length
character(len=maxvarlen), parameter, public var_v
character(len=maxvarlen), parameter, public var_zi
character(len=maxvarlen), parameter, public var_sfc_geomz
character(len=maxvarlen), parameter, public var_u
character(len=maxvarlen), parameter, public var_sea_fric_vel
type to hold interpolated field for one variable, one observation
type to hold interpolated fields required by the obs operators