UFO
ObsDerivativeCheck.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2018-2019 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 <algorithm>
11 #include <cmath>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "eckit/config/Configuration.h"
17 #include "eckit/geometry/Point2.h"
18 #include "eckit/geometry/Sphere.h"
19 
20 #include "ioda/ObsDataVector.h"
21 #include "ioda/ObsSpace.h"
22 #include "oops/util/Duration.h"
23 #include "oops/util/Logger.h"
24 #include "oops/util/missingValues.h"
25 #include "ufo/utils/Constants.h"
26 
27 namespace ufo {
28 
29 // -----------------------------------------------------------------------------
30 
31 ObsDerivativeCheck::ObsDerivativeCheck(ioda::ObsSpace & obsdb, const eckit::Configuration & config,
32  std::shared_ptr<ioda::ObsDataVector<int> > flags,
33  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
34  : FilterBase(obsdb, config, flags, obserr)
35 {
36  oops::Log::debug() << "ObsDerivativeCheck: config = " << config_ << std::endl;
37 }
38 
39 // -----------------------------------------------------------------------------
40 
42 
43 // -----------------------------------------------------------------------------
44 
45 void get_locs(const std::vector<std::size_t> & rSort, const size_t & i1, const size_t & i2,
46  const size_t & ilocs, size_t & ii1, size_t & ii2)
47 // rSort - the vector of integers of indices of the sorted ObsDataVector
48 // used to get the size of the sorted record
49 // i1 and i2 are by default 0 but can be defined in YAML to assign fixed indices
50 // for the derivative computed for each point in the record
51 // if i1 and i2 are both zero then a local derivative will be computed and indices
52 // will be computed by this function
53 // ii1 and ii2 are the output indices from the function to use in computation of derivatives
54 // ii1=i1 and ii2=i2 for when i1 and i2 != 0
55 // ii1 and ii2 are computed below for all other cases
56 {
57  if ( rSort.size() == 1 ) {
58  // set both ii1 and ii2 to 0 which will be used to indicate derivative is zero
59  ii1 = 0;
60  ii2 = 0;
61  } else if ( rSort.size() == 2 ) {
62  // set ii1 to 0 and ii2 to 1 because there are only 2 points in this group
63  ii1 = 0;
64  ii2 = 1;
65  } else {
66  if ( (i1 == 0) && (i2 == 0) ) {
67  // this means local derivatives will be computed for each point
68  if ( ilocs == 0 ) {
69  // special case for boundary condition 1
70  ii1 = 0;
71  ii2 = 1;
72  } else if ( ilocs == rSort.size()-1 ) {
73  // special case for boundary condition 2
74  ii1 = ilocs-1;
75  ii2 = ilocs;
76  } else {
77  // define local derivative normally as
78  // (y(i+1) - y(i-1)) / (x(i+1) - x(i-1))
79  ii1 = ilocs-1;
80  ii2 = ilocs+1;
81  }
82  } else {
83  // compute derivative for each point to be from indices provided in YAML
84  // note this may need to be more complex later to compute the indices desired
85  // set index to the last value if -1 defined in YAML
86  (i1 == -1) ? ii1 = rSort.size()-1 : ii1 = i1;
87  (i2 == -1) ? ii2 = rSort.size()-1 : ii2 = i2;
88  // set index to the last value if YAML greater than indices
89  (i1 > rSort.size()-1) ? ii1 = rSort.size()-1 : ii1 = i1;
90  (i2 > rSort.size()-1) ? ii2 = rSort.size()-1 : ii2 = i2;
91  }
92  }
93 }
94 
95 // -----------------------------------------------------------------------------
96 
97 void ObsDerivativeCheck::applyFilter(const std::vector<bool> & apply,
98  const Variables & filtervars,
99  std::vector<std::vector<bool>> & flagged) const {
100  const float missing = util::missingValue(missing);
101  const double radiusEarth = Constants::mean_earth_rad*1000.0;
102 
103  // first we want to get the config of the two vars to use in computing the derivative
104  const std::string strInd_ = config_.getString("independent", "");
105  const std::string strDep_ = config_.getString("dependent", "");
106  size_t i1 = config_.getInt("i1", 0); // specified indices to use if not local derivatives
107  size_t i2 = config_.getInt("i2", 0);
108  size_t ii1 = 0; // indices that will be used to compute derivativs
109  size_t ii2 = 0;
110  // min/max value setup
111  float minddx = config_.getFloat("minvalue", missing);
112  float maxddx = config_.getFloat("maxvalue", missing);
113 
114  oops::Log::debug() << "ObsDerivativeCheck: Independent Var = " << strInd_ << std::endl;
115  oops::Log::debug() << "ObsDerivativeCheck: Dependent Var = " << strDep_ << std::endl;
116 
117  // now grab the vars and compute the derivative
118  const size_t nlocs_ = obsdb_.nlocs();
119  std::vector<float> dydx(nlocs_);
120 
121  // different options depending on the independent variable
122  // the generic case:
123  // dy/dx = y(ii2)-y(ii1) / x(ii2) - x(ii1)
124  // when x is datetime:
125  // time is computed in seconds and denominator is handled differently
126  // when y is distance:
127  // longitude and latitude are used to compute great circle distances
128  // for numerator
129  // when x is distance:
130  // longitude and latitude are used to compute great circle distances
131  // for denominator
132  if ( strInd_ == "datetime" ) { // special case for datetime
133  std::vector<util::DateTime> varIndT_(nlocs_);
134  obsdb_.get_db("MetaData", strInd_, varIndT_);
135  ioda::ObsSpace::RecIdxIter irec;
136  if ( strDep_ == "distance" ) { // special case for moving obs
137  ioda::ObsDataVector<float> varDepX_(obsdb_, "longitude", "MetaData");
138  ioda::ObsDataVector<float> varDepY_(obsdb_, "latitude", "MetaData");
139  for ( irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec ) {
140  std:: size_t rNum = obsdb_.recidx_recnum(irec);
141  std::vector<std::size_t> rSort = obsdb_.recidx_vector(irec);
142  for (size_t ilocs = 0; ilocs < rSort.size(); ++ilocs) {
143  get_locs(rSort, i1, i2, ilocs, ii1, ii2);
144  if ( ii1 == ii2 ) {
145  // no derivative if the indices are the same
146  dydx[rSort[ilocs]] = 0.0;
147  } else {
148  // compute distance for each lat lon pair
149  eckit::geometry::Point2 Ob1(varDepX_["longitude"][rSort[ii2]],
150  varDepY_["latitude"][rSort[ii2]]);
151  eckit::geometry::Point2 Ob2(varDepX_["longitude"][rSort[ii1]],
152  varDepY_["latitude"][rSort[ii1]]);
153  float dist_m = static_cast<float>(eckit::geometry::Sphere::distance(radiusEarth,
154  Ob1, Ob2));
155  // compute derivative
156  dydx[rSort[ilocs]] = dist_m /
157  (varIndT_[rSort[ii2]] - varIndT_[rSort[ii1]]).toSeconds();
158  }
159  }
160  }
161  } else {
162  ioda::ObsDataVector<float> varDep_(obsdb_, strDep_, "MetaData");
163  for ( irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec ) {
164  std:: size_t rNum = obsdb_.recidx_recnum(irec);
165  std::vector<std::size_t> rSort = obsdb_.recidx_vector(irec);
166  for (size_t ilocs = 0; ilocs < rSort.size(); ++ilocs) {
167  get_locs(rSort, i1, i2, ilocs, ii1, ii2);
168  if ( ii1 == ii2 ) {
169  // no derivative if the indices are the same
170  dydx[rSort[ilocs]] = 0.0;
171  } else {
172  // compute derivative
173  dydx[rSort[ilocs]] = (varDep_[strDep_][rSort[ii2]] -
174  varDep_[strDep_][rSort[ii1]]) /
175  (varIndT_[rSort[ii2]] -
176  varIndT_[rSort[ii1]]).toSeconds();
177  }
178  }
179  }
180  }
181  // end if strInd_ == "datetime"
182  } else if ( strInd_ == "distance" ) { // convert lat/lon change to distance
183  ioda::ObsDataVector<float> varDep_(obsdb_, strDep_, "MetaData");
184  ioda::ObsDataVector<float> varIndX_(obsdb_, "longitude", "MetaData");
185  ioda::ObsDataVector<float> varIndY_(obsdb_, "latitude", "MetaData");
186  ioda::ObsSpace::RecIdxIter irec;
187  for ( irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec ) {
188  std:: size_t rNum = obsdb_.recidx_recnum(irec);
189  std::vector<std::size_t> rSort = obsdb_.recidx_vector(irec);
190  for (size_t ilocs = 0; ilocs < rSort.size(); ++ilocs) {
191  get_locs(rSort, i1, i2, ilocs, ii1, ii2);
192  if ( ii1 == ii2 ) {
193  // no derivative if the indices are the same
194  dydx[rSort[ilocs]] = 0.0;
195  } else {
196  // compute distance for each lat lon pair
197  eckit::geometry::Point2 Ob1(varIndX_["longitude"][rSort[ii2]],
198  varIndY_["latitude"][rSort[ii2]]);
199  eckit::geometry::Point2 Ob2(varIndX_["longitude"][rSort[ii1]],
200  varIndY_["latitude"][rSort[ii1]]);
201  float dist_m = static_cast<float>(eckit::geometry::Sphere::distance(radiusEarth,
202  Ob1, Ob2));
203  dydx[rSort[ilocs]] = (varDep_[strDep_][rSort[ii2]] - varDep_[strDep_][rSort[ii1]]) /
204  dist_m;
205  }
206  }
207  }
208  // end if strInd_ == "distance"
209  } else { // standard case where independent var is not datetime or distance
210  ioda::ObsDataVector<float> varDep_(obsdb_, strDep_, "MetaData");
211  ioda::ObsDataVector<float> varInd_(obsdb_, strInd_, "MetaData");
212  ioda::ObsSpace::RecIdxIter irec;
213  for ( irec = obsdb_.recidx_begin(); irec != obsdb_.recidx_end(); ++irec ) {
214  std:: size_t rNum = obsdb_.recidx_recnum(irec);
215  std::vector<std::size_t> rSort = obsdb_.recidx_vector(irec);
216  for (size_t ilocs = 0; ilocs < rSort.size(); ++ilocs) {
217  get_locs(rSort, i1, i2, ilocs, ii1, ii2);
218  if ( ii1 == ii2 ) {
219  dydx[rSort[ilocs]] = 0.0;
220  } else {
221  dydx[rSort[ilocs]] = (varDep_[strDep_][rSort[ii2]] - varDep_[strDep_][rSort[ii1]]) /
222  (varInd_[strInd_][rSort[ii2]] - varInd_[strInd_][rSort[ii1]]);
223  }
224  }
225  }
226  }
227 
228  // determine if the derivative is outside the specified range
229  for (size_t jv = 0; jv < filtervars.nvars(); ++jv) {
230  for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) {
231  if (apply[jobs]) {
232  if (dydx[jobs] > maxddx && maxddx != missing) flagged[jv][jobs] = flagged[jv][jobs] = true;
233  if (dydx[jobs] < minddx && minddx != missing) flagged[jv][jobs] = flagged[jv][jobs] = true;
234  }
235  }
236  }
237 }
238 
239 // -----------------------------------------------------------------------------
240 
241 void ObsDerivativeCheck::print(std::ostream & os) const {
242  os << "ObsDerivativeCheck: config = " << config_ << std::endl;
243 }
244 
245 // -----------------------------------------------------------------------------
246 
247 } // namespace ufo
Base class for UFO QC filters.
Definition: FilterBase.h:45
const eckit::LocalConfiguration config_
Definition: FilterBase.h:59
ObsDerivativeCheck(ioda::ObsSpace &, const eckit::Configuration &, std::shared_ptr< ioda::ObsDataVector< int > >, std::shared_ptr< ioda::ObsDataVector< float > >)
void print(std::ostream &) const override
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
ioda::ObsSpace & obsdb_
size_t nvars() const
Return the number of constituent "primitive" (single-channel) variables.
Definition: Variables.cc:104
constexpr int missing
Definition: QCflags.h:20
float distance(const Point &a, const Point &b)
Returns the distance between the two cartesian-mapped Point arguments
Definition: RunCRTM.h:27
void get_locs(const std::vector< std::size_t > &rSort, const size_t &i1, const size_t &i2, const size_t &ilocs, size_t &ii1, size_t &ii2)
static constexpr double mean_earth_rad
Definition: Constants.h:40