UFO
ObsErrorFactorSfcPressure.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020 UCAR
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 
9 
10 #include <float.h>
11 
12 #include <algorithm>
13 #include <cmath>
14 
15 #include "eckit/exception/Exceptions.h"
16 #include "ioda/ObsDataVector.h"
17 #include "oops/util/Logger.h"
18 #include "oops/util/missingValues.h"
19 #include "ufo/utils/Constants.h"
20 
21 namespace ufo {
22 
23 static ObsFunctionMaker<ObsErrorFactorSfcPressure> makerSteps_("ObsErrorFactorSfcPressure");
24 
25 // -----------------------------------------------------------------------------
26 
27 ObsErrorFactorSfcPressure::ObsErrorFactorSfcPressure(const eckit::Configuration &config)
28  : invars_() {
29  oops::Log::debug() << "ObsErrorFactorSfcPressure: config = " << config << std::endl;
30  const float tiny_float = FLT_MIN;
31  const float huge_float = FLT_MAX;
32  const float missing = util::missingValue(missing);
33  // Initialize options
34  options_.reset(new ObsErrorFactorSfcPressureParameters());
35  options_->deserialize(config);
36 
37  // Initialize error_min, max from options. Make sure they are sane.
38  const float error_min = options_->error_min.value();
39  const float error_max = options_->error_max.value();
40  ASSERT(error_min < error_max);
41 
42  // The starting (un-inflated) value of obserror. If running in sequence of filters,
43  // then it is probably found in ObsErrorData, otherwise, it is probably ObsError.
44  const std::string errgrp = options_->original_obserr.value();
45  invars_ += Variable("surface_pressure@"+errgrp);
46 
47  // Include list of required data from ObsValue
48  invars_ += Variable("surface_pressure@ObsValue");
49  invars_ += Variable("air_temperature@ObsValue");
50 
51  // Include list of required data from MetaData
52  invars_ += Variable("station_elevation@MetaData");
53 
54  // Include list of required data from GeoVaLs
55  invars_ += Variable("surface_pressure@GeoVaLs");
56  invars_ += Variable("air_pressure@GeoVaLs");
57  const std::string geovar_temp = options_->geovar_temp.value();
58  invars_ += Variable(geovar_temp + "@GeoVaLs");
59  const std::string geovar_sfc_geomz = options_->geovar_sfc_geomz.value();
60  invars_ += Variable(geovar_sfc_geomz + "@GeoVaLs");
61 }
62 
63 // -----------------------------------------------------------------------------
64 
65 ObsErrorFactorSfcPressure::~ObsErrorFactorSfcPressure() {}
66 
67 // -----------------------------------------------------------------------------
68 
69 void ObsErrorFactorSfcPressure::compute(const ObsFilterData & data,
70  ioda::ObsDataVector<float> & obserr) const {
71  const float missing = util::missingValue(missing);
72  static constexpr float g_over_rd = 1.0f*Constants::grav/Constants::rd;
73  const float lapse_rate = 1.0f*Constants::Lclr;
74 
75  // Ensure that only one output variable is expected.
76  ASSERT(obserr.nvars() == 1);
77 
78  // Get dimensions
79  size_t nlocs = data.nlocs();
80  size_t nlevs = data.nlevs(Variable("air_pressure@GeoVaLs"));
81 
82  // Get min, max error values
83  const float error_min = options_->error_min.value();
84  const float error_max = options_->error_max.value();
85 
86  // Get MetaData of station elevation
87  std::vector<float> ob_elevation(nlocs);
88  data.get(Variable("station_elevation@MetaData"), ob_elevation);
89 
90  // Get ObsValue of surface pressure and temperature (possibly missing).
91  std::vector<float> ob_pressure_sfc(nlocs);
92  data.get(Variable("surface_pressure@ObsValue"), ob_pressure_sfc);
93  std::vector<float> ob_temperature_sfc(nlocs);
94  data.get(Variable("air_temperature@ObsValue"), ob_temperature_sfc);
95 
96  // Get original ObsError of surface pressure
97  std::vector<float> currentObserr(nlocs);
98  const std::string errgrp = options_->original_obserr.value();
99  data.get(Variable("surface_pressure@"+errgrp), currentObserr);
100 
101  // Get GeoVals of surface altitude, pressure, and temperature.
102  std::vector<float> model_elevation(nlocs);
103  const std::string geovar_sfc_geomz = options_->geovar_sfc_geomz.value();
104  data.get(Variable(geovar_sfc_geomz + "@GeoVaLs"), model_elevation);
105  std::vector<float> model_pres_sfc(nlocs);
106  data.get(Variable("surface_pressure@GeoVaLs"), model_pres_sfc);
107 
108  // Get GeoVals of air pressure [Pa] and temperature in vertical column.
109  std::vector<std::vector<float>> prsl(nlevs, std::vector<float>(nlocs));
110  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
111  const size_t level = nlevs - ilev - 1;
112  data.get(Variable("air_pressure@GeoVaLs"), level, prsl[ilev]);
113  }
114  std::vector<std::vector<float>> tair(nlevs, std::vector<float>(nlocs));
115  const std::string geovar_temp = options_->geovar_temp.value();
116  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
117  const size_t level = nlevs - ilev - 1;
118  data.get(Variable(geovar_temp + "@GeoVaLs"), level, tair[ilev]);
119  }
120 
121  // TODO(gthompsn): model temp is virtual_temp whereas obs is sensible temp.
122  // Also, for now, assigning model lowest level temp as surface temp.
123  // Lastly, need to make positive that assumption of 0th level is nearest sfc.
124  std::vector<float> model_temp_sfc(nlocs);
125  model_temp_sfc = tair[0];
126 
127  float obserror, new_error, error_factor;
128  float rdelz, rdp, drdp, ddiff, tges, pges, tges2, pges2, psges, pgesorig, drbx;
129  int iv = 0;
130 
131  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
132  // If missing station_elevation, set error factor to something very high (for rejection).
133  if (ob_elevation[iloc] == missing) {
134  obserr[iv][iloc] = 99.9f;
135  } else {
136  rdelz = ob_elevation[iloc]-model_elevation[iloc];
137 
138  // If more than a km between model and real elevation, set error factor linearly higher.
139  if (std::abs(rdelz) > 5000.0f) {
140  obserr[iv][iloc] = 50.0f;
141  } else if (std::abs(rdelz) > 1000.0f) {
142  obserr[iv][iloc] = 3.0f + 47.0f*(std::abs(rdelz)-1000.0f)/4000.0f;
143  } else {
144  pgesorig = model_pres_sfc[iloc]*0.001; // Converting Pascals to cb
145  psges = log(pgesorig);
146 
147  // Calculating drbx with observed temperature.
148  // If ob temperature missing, then check if model ground is above or below actual ground.
149  if (ob_temperature_sfc[iloc] != missing) {
150  drbx = 0.5f*std::abs(model_temp_sfc[iloc]-ob_temperature_sfc[iloc])
151  +0.2f+0.005f*std::abs(rdelz);
152  tges = 0.5f*(model_temp_sfc[iloc]+ob_temperature_sfc[iloc]);
153  } else {
154  // TODO(gthompsn): If model terrain below real world, grab nearest Temp,Pres from a
155  // vertical profile. Shortcut for now is assume lapse_rate and hydrostatic assumption
156  // over rdelz. The 2.5 addition is **arbitrary** and lapse rate assumption is 5C/km.
157  if (std::abs(rdelz) < 5.0) {
158  tges = model_temp_sfc[iloc];
159  drbx = 0.1;
160  } else if (rdelz > 0.0) {
161  tges2 = model_temp_sfc[iloc] - lapse_rate*rdelz;
162  drbx = 0.5f*std::abs(model_temp_sfc[iloc]-tges2)+2.5f+0.005f*std::abs(rdelz);
163  tges = 0.5f*(model_temp_sfc[iloc]+tges2);
164  } else {
165  tges = model_temp_sfc[iloc] - 0.5f*lapse_rate*rdelz;
166  tges2 = tges - lapse_rate*rdelz;
167  drbx = 0.5f*std::abs(model_temp_sfc[iloc]-tges2)+2.5f+0.005f*std::abs(rdelz);
168  drbx = drbx - 0.5f*lapse_rate*rdelz;
169  }
170  }
171 
172  rdp = g_over_rd*rdelz/tges;
173  pges = exp(log(pgesorig) - rdp);
174  drdp = pges*(g_over_rd*std::abs(rdelz)*drbx/(tges*tges));
175  ddiff = ob_pressure_sfc[iloc]*0.001f - pges; // innovation in cb
176 
177  // make adjustment to observational error (also convert to cb)
178  obserror = currentObserr[iloc]*0.001f;
179  // TODO(gthompsn): Maybe reduce obserror by 0.7 for data near sea-level and small delta-Z.
180  // if (ob_elevation[iloc] < 10.0f && rdelz < 5.0f) {
181  // obserror = obserror*0.7;
182  // }
183  new_error = obserror + drdp;
184  new_error = std::max(error_min*0.001f, std::min(new_error, error_max*0.001f));
185  error_factor = std::max(0.7f, new_error/obserror);
186 
187  obserr[iv][iloc] = error_factor;
188  }
189  }
190  }
191 }
192 
193 // -----------------------------------------------------------------------------
194 
195 const ufo::Variables & ObsErrorFactorSfcPressure::requiredVariables() const {
196  return invars_;
197 }
198 
199 // -----------------------------------------------------------------------------
200 
201 } // namespace ufo
Options controlling ObsErrorFactorSfcPressure ObsFunction.
constexpr int missing
Definition: QCflags.h:20
real(kind_real), parameter, public rd
real(kind_real), parameter, public grav
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27
static ObsFunctionMaker< ObsErrorFactorConventional > makerSteps_("ObsErrorFactorConventional")
util::Duration abs(const util::Duration &duration)