UFO
ProfileBackgroundCheck.cc
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 
8 
9 #include <algorithm>
10 #include <cmath>
11 #include <iomanip>
12 #include <iostream>
13 #include <limits>
14 #include <set>
15 #include <string>
16 #include <vector>
17 
18 #include "ioda/ObsDataVector.h"
19 #include "ioda/ObsSpace.h"
20 
21 #include "oops/util/Logger.h"
22 
24 #include "ufo/filters/QCflags.h"
25 
26 namespace ufo {
27 
28 // -----------------------------------------------------------------------------
29 /// ProfileBackgroundCheck: check observation closeness to background over a profile
30 ///
31 /// This will use the record number in obsdb_ to identify which observations belong to a
32 /// given profile (all members of a profile must share the same record number). For
33 /// each profile the RMS of observed-HofX is calculated, and this is compared against
34 /// the given threshold. If "relative threshold" is used (rather than "absolute threshold")
35 /// then the RMS is normalised by observation error.
36 ///
37 /// The threshold can be a floating-point constant or the name of a variable indicating
38 /// the threshold to use at each location. In this case, the value of the threshold is
39 /// taken from the threshold given for the first observation in the profile.
40 ///
41 /// This is related to BackgroundCheck, which checks each observation against a threshold.
42 /// There is also a group of other profile checks (ConventionalProfileProcessing) which are
43 /// mostly aimed to processing radiosondes.
44 
46  ioda::ObsSpace & obsdb,
47  const Parameters_ & parameters,
48  std::shared_ptr<ioda::ObsDataVector<int> > flags,
49  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
50  : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters)
51 {
52  oops::Log::trace() << "ProfileBackgroundCheck constructor" << std::endl;
53 
54  ASSERT(parameters_.relativeThreshold.value() ||
56 }
57 
58 // -----------------------------------------------------------------------------
59 
61  oops::Log::trace() << "ProfileBackgroundCheck destructed" << std::endl;
62 }
63 
64 // -----------------------------------------------------------------------------
65 /// Apply the profile background check filter.
66 
67 void ProfileBackgroundCheck::applyFilter(const std::vector<bool> & apply,
68  const Variables & filtervars,
69  std::vector<std::vector<bool>> & flagged) const {
70  oops::Log::trace() << "ProfileBackgroundCheck postFilter" << std::endl;
71  const oops::Variables observed = obsdb_.obsvariables();
72 
73  oops::Log::debug() << "ProfileBackgroundCheck obserr: " << *obserr_;
74 
75  ioda::ObsDataVector<float> obs(obsdb_, filtervars.toOopsVariables(), "ObsValue");
76  ioda::ObsDataVector<float> bias(obsdb_, filtervars.toOopsVariables(), "ObsBias", false);
77 
78  // Get the record numbers from the observation data. These will be used to identify
79  // which observations belong to which profile.
80  const std::vector<size_t> &unique = obsdb_.recidx_all_recnums();
81  oops::Log::debug() << "Unique record numbers" << std::endl;
82  for (size_t iUnique : unique)
83  oops::Log::debug() << iUnique << ' ';
84  oops::Log::debug() << std::endl;
85 
86  // Threshold for all variables
87  std::vector<float> abs_thr(obsdb_.nlocs(), std::numeric_limits<float>::max());
88  std::vector<float> rel_thr(obsdb_.nlocs(), std::numeric_limits<float>::max());
89  if (parameters_.absoluteThreshold.value())
91  if (parameters_.relativeThreshold.value())
93 
94  Variables varhofx(filtervars_, "HofX");
95  for (size_t jv = 0; jv < filtervars.nvars(); ++jv) {
96  size_t iv = observed.find(filtervars.variable(jv).variable());
97  // H(x)
98  std::vector<float> hofx;
99  data_.get(varhofx.variable(jv), hofx);
100 
101  // Loop over the unique profiles
102  for (const auto& iprofile : unique) {
103  std::vector<size_t> obs_numbers = obsdb_.recidx_vector(iprofile);
104  // Initialise the accumulators for this profile
105  double total_diff = 0;
106  int total_nobs = 0;
107  bool using_rel_thresh = false;
108  bool first_ob = true;
109  float profile_threshold = 0;
110 
111  // First run through all the observations to calculate the O-B statistics
112  // (normalised by the observation error).
113  // Determine whether this profile uses threshold or abs_threshold based
114  // on the first value in the profile.
115  for (size_t jobs : obs_numbers) {
116  if (apply[jobs] && (*flags_)[iv][jobs] == QCflags::pass) {
117  ASSERT((*obserr_)[iv][jobs] != util::missingValue((*obserr_)[iv][jobs]));
118  ASSERT(obs[jv][jobs] != util::missingValue(obs[jv][jobs]));
119  ASSERT(hofx[jobs] != util::missingValue(hofx[jobs]));
120 
121  if (first_ob) {
122  if (rel_thr[jobs] == std::numeric_limits<float>::max()) {
123  using_rel_thresh = false;
124  profile_threshold = abs_thr[jobs];
125  } else {
126  using_rel_thresh = true;
127  profile_threshold = rel_thr[jobs];
128  }
129  first_ob = false;
130  ASSERT(profile_threshold < std::numeric_limits<float>::max() &&
131  profile_threshold > 0.0f);
132  }
133 
134  // Apply bias correction
135  const float yy = obs[jv][jobs] - bias[jv][jobs];
136 
137  // Accumulate distance from background
138  if ((*obserr_)[iv][jobs] > 0) {
139  total_nobs++;
140  if (using_rel_thresh) {
141  // Normalise by observation error
142  total_diff += std::pow((hofx[jobs] - yy) / (*obserr_)[iv][jobs], 2);
143  } else {
144  // Use the absolute differences
145  total_diff += std::pow(hofx[jobs] - yy, 2);
146  }
147  }
148  }
149  }
150 
151  // Calculate average difference over profile
152  if (total_nobs > 0) {
153  total_diff = std::pow(total_diff / total_nobs, 0.5);
154  }
155  oops::Log::debug() << "ProfileBackgroundCheck: For profile " << iprofile <<
156  " rms diff is " << total_diff << std::endl;
157 
158  // Second run through the observations to apply the threshold to reject profiles
159  for (size_t jobs : obs_numbers) {
160  if (apply[jobs] && (*flags_)[iv][jobs] == QCflags::pass) {
161  // If rejecting this profile, then flag all elements
162  if (total_diff > static_cast<double>(profile_threshold)) flagged[jv][jobs] = true;
163  }
164  }
165  }
166  }
167 }
168 
169 // -----------------------------------------------------------------------------
170 
171 void ProfileBackgroundCheck::print(std::ostream & os) const {
172  os << "ProfileBackgroundCheck: config = " << parameters_ << std::endl;
173 }
174 
175 // -----------------------------------------------------------------------------
176 
177 } // namespace ufo
Base class for UFO QC filters.
Definition: FilterBase.h:45
ufo::Variables filtervars_
Definition: FilterBase.h:60
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
std::shared_ptr< ioda::ObsDataVector< float > > obserr_
ioda::ObsSpace & obsdb_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
ProfileBackgroundCheck(ioda::ObsSpace &, const Parameters_ &, 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
Apply the profile background check filter.
Parameters controlling the operation of the ProfileBackgroundCheck filter.
oops::OptionalParameter< std::string > relativeThreshold
oops::OptionalParameter< std::string > absoluteThreshold
const std::string & variable() const
Definition: Variable.cc:99
size_t nvars() const
Return the number of constituent "primitive" (single-channel) variables.
Definition: Variables.cc:104
Variable variable(const size_t) const
Return a given constituent "primitive" (single-channel) variable.
Definition: Variables.cc:114
oops::Variables toOopsVariables() const
Definition: Variables.cc:156
constexpr int pass
Definition: QCflags.h:14
Definition: RunCRTM.h:27
std::vector< float > getScalarOrFilterData(const std::string &strfactor, const ObsFilterData &data)
Function to fill in a vector with either a scalar or data from ObsFilterData.