UFO
ObsErrorModelStepwiseLinear.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 
8 #include <memory>
9 #include <boost/optional/optional_io.hpp>
10 
12 
13 #include "eckit/exception/Exceptions.h"
14 
15 #include "ioda/ObsDataVector.h"
16 
17 #include "oops/util/CompareNVectors.h"
18 #include "oops/util/Logger.h"
19 #include "oops/util/missingValues.h"
20 #include "oops/util/PropertiesOfNVectors.h"
21 
22 namespace ufo {
23 
24 static ObsFunctionMaker<ObsErrorModelStepwiseLinear> makerSteps_("ObsErrorModelStepwiseLinear");
25 
26 // -----------------------------------------------------------------------------
27 
28 ObsErrorModelStepwiseLinear::ObsErrorModelStepwiseLinear(const eckit::LocalConfiguration config)
29  : invars_() {
30  // Initialize options
31  options_.deserialize(config);
32 
33  // Get piece-wise parameters from options.
34  const std::vector<float> &xvals = options_.xvals.value();
35  const std::vector<float> &errors = options_.errors.value();
36 
37  // Ensure same size vectors (xvals and errors); Also ensure more than one value in each.
38  if (!oops::allVectorsSameNonZeroSize(xvals, errors) ||
39  ((xvals.size() <= 1 || errors.size() <= 1))) {
40  std::ostringstream errString;
41  errString << "At least one vector is the wrong size - either unequal or less than 2."
42  << std::endl << "Vector sizes of xvals, errors: "
43  << oops::listOfVectorSizes(xvals, errors) << std::endl;
44  oops::Log::error() << errString.str();
45  throw eckit::BadValue(errString.str());
46  }
47 
48  // Initialize x-variable
49  const Variable &xvar = options_.xvar.value();
50  ASSERT(xvar.size() == 1);
51  invars_ += xvar;
52 
53  // In the case of using a multiply operator, load it into invars as well.
54  if (options_.scale_factor_var.value() != boost::none) {
55  multiplicative_ = true;
56  const boost::optional<Variable> &scale_factor_var = options_.scale_factor_var.value();
57  invars_ += *scale_factor_var;
58  oops::Log::debug() << " StepwiseLinear, scale_factor_var: " << *scale_factor_var << std::endl;
59  }
60 
61  // Check that all errors >= 0
62  for (size_t i = 0; i < errors.size(); ++i) {
63  ASSERT(errors[i] >= 0.0);
64  }
65 
66  // In order to check for beyond range of xvals, determine if ascending or descending.
67  // Also ensure that the entire vector is consistent throughout and no consecutive elements
68  // of xval are equal.
69  if (xvals.back() < xvals.front()) {
70  isAscending_ = false;
71  }
72  for (size_t kv = 1; kv < xvals.size(); ++kv) {
73  if ((xvals[kv] >= xvals[kv-1] && !isAscending_) || (xvals[kv] <= xvals[kv-1] && isAscending_)) {
74  std::ostringstream errString;
75  errString << "The xvals vector of elements is NOT internally consistent."
76  << " It must be entirely either ascending or descending order,"
77  << " or two consecutive values are the same." << std::endl;
78  oops::Log::error() << errString.str();
79  throw eckit::BadValue(errString.str());
80  }
81  }
82  oops::Log::debug() << "ObsErrorModelStepwiseLinear: config (constructor) = "
83  << config << std::endl;
84 }
85 
86 // -----------------------------------------------------------------------------
87 
89 
90 // -----------------------------------------------------------------------------
91 
93  ioda::ObsDataVector<float> & obserr) const {
94  const float missing = util::missingValue(missing);
95  // Linearly interpolate from y0 to y1 at xstar between x0 and x1 to arrive at error
96  float x0, x1, y0, y1;
97  float xstar, error;
98 
99  // Get the x-variable name and piece-wise parameters from options
100  const Variable &xvar = options_.xvar.value();
101  const std::vector<float> &xvals = options_.xvals.value();
102  const std::vector<float> &errors = options_.errors.value();
103  oops::Log::debug() << " ObsErrorModelStepwiseLinear, x-variable name: " << xvar.variable()
104  << " and group: " << xvar.group() << std::endl;
105 
106  // Populate the testdata array. xstar is just the 0..nloc-1 value of testvar[iv]
107  // At each nloc, find matching (x0,x1) and (y0,y1) pair for linear interp.
108  ioda::ObsDataVector<float> testdata(data.obsspace(), xvar.toOopsVariables());
109  data.get(xvar, testdata);
110 
111  // Declare a unique_ptr, but keep it empty for now (don’t assign anything to it).
112  std::unique_ptr<ioda::ObsDataVector<float>> obvalues;
113 
114  // If using the multiply operator, load up the variable (scale_factor_var) to multiply by.
115  if (multiplicative_) {
116  const boost::optional<Variable> &scale_factor_var = options_.scale_factor_var.value();
117  oops::Log::debug() << " ObsErrorModelStepwiseLinear, scale_factor_var name: "
118  << (*scale_factor_var).variable() << " and group: "
119  << (*scale_factor_var).group() << std::endl;
120  obvalues.reset(new ioda::ObsDataVector<float>(data.obsspace(),
121  (*scale_factor_var).toOopsVariables()));
122  data.get(*scale_factor_var, *obvalues);
123  }
124 
125  // The 1st index of data should have size 1 and 2nd index should be size nlocs.
126  int iv = 0;
127  if (testdata[iv].size() != obserr[iv].size()) {
128  std::ostringstream errString;
129  errString << "Something is wrong, testdata size not equal obserr size."
130  << " Sizes: " << testdata[iv].size() << " and " << obserr[iv].size() << std::endl;
131  oops::Log::error() << errString.str();
132  throw eckit::BadValue(errString.str());
133  }
134 
135  for (size_t jobs = 0; jobs < testdata[iv].size(); ++jobs) {
136  error = 0.0;
137  obserr[iv][jobs] = missing;
138  if (testdata[iv][jobs] == missing) {
139  continue;
140  }
141  xstar = testdata[iv][jobs];
142  if ((xstar <= xvals[0] && isAscending_) || (xstar >= xvals[0] && !isAscending_)) {
143  error = errors[0];
144  } else if ((xstar >= xvals.back() && isAscending_)
145  || (xstar <= xvals.back() && !isAscending_)) {
146  error = errors[errors.size()-1];
147  } else {
148  for (size_t kv = 1; kv < xvals.size(); ++kv) {
149  if ((isAscending_ && (xstar > xvals[kv-1]) && (xstar <= xvals[kv])) ||
150  (!isAscending_ && (xstar < xvals[kv-1]) && (xstar >= xvals[kv]))) {
151  x0 = xvals[kv-1];
152  x1 = xvals[kv];
153  y0 = errors[kv-1];
154  y1 = errors[kv];
155  error = y0 + (xstar-x0)*((y1-y0)/(x1-x0));
156  break;
157  }
158  }
159  }
160  // TODO(gthompsn): probably need this next line for when filtervariable is flagged missing
161  // if (!flagged_[jv][jobs]) obserr[jv][jobs] = error;
162  if (multiplicative_) {
163  obserr[iv][jobs] = error*(*obvalues)[iv][jobs];
164  } else {
165  obserr[iv][jobs] = error;
166  }
167  }
168 }
169 
170 // -----------------------------------------------------------------------------
171 
173  return invars_;
174 }
175 
176 // -----------------------------------------------------------------------------
177 
178 } // namespace ufo
const ufo::Variables & requiredVariables() const
geovals required to compute the function
void compute(const ObsFilterData &, ioda::ObsDataVector< float > &) const
compute the result of the function
ObsErrorModelStepwiseLinearParameters options_
ObsErrorModelStepwiseLinear(const eckit::LocalConfiguration)
oops::OptionalParameter< Variable > scale_factor_var
When final answer is a multiplication factor, we need to know which variable to multiply.
oops::RequiredParameter< Variable > xvar
the name of the xvar
oops::RequiredParameter< std::vector< float > > errors
vector of error values corresponding to vector of X-steps
oops::RequiredParameter< std::vector< float > > xvals
vector of X-steps
ObsFilterData provides access to all data related to an ObsFilter.
ioda::ObsSpace & obsspace() const
Returns reference to ObsSpace associated with ObsFilterData.
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
const std::string & variable() const
Definition: Variable.cc:99
const std::string & group() const
Definition: Variable.cc:116
oops::Variables toOopsVariables() const
Definition: Variable.cc:139
size_t size() const
Definition: Variable.cc:78
constexpr int missing
Definition: QCflags.h:20
Definition: RunCRTM.h:27
static ObsFunctionMaker< ObsErrorFactorConventional > makerSteps_("ObsErrorFactorConventional")