UFO
QCmanager.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 <numeric>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 #include "eckit/config/Configuration.h"
16 
17 #include "ioda/distribution/Accumulator.h"
18 #include "ioda/ObsDataVector.h"
19 #include "ioda/ObsSpace.h"
20 #include "ioda/ObsVector.h"
21 #include "oops/base/Variables.h"
22 #include "oops/util/Logger.h"
23 #include "oops/util/missingValues.h"
24 #include "ufo/filters/QCflags.h"
25 
26 namespace ufo {
27 
28 // Presets for QC filters could be performed in a function outside of any class.
29 // We keep them as a filter for now. The main reason for this is to be able to use
30 // the factory for models not in UFO/IODA.
31 
32 namespace {
33 
34 /// At each location, set the QC flag to QCflags::missing if the current QC flag is invalid
35 /// or if the ObsValue is missing.
36 void updateQCFlags(const std::vector<float> *obsValues, std::vector<int>& qcflags) {
37  const float rmiss = util::missingValue(rmiss);
38  const int imiss = util::missingValue(imiss);
39  for (size_t jobs = 0; jobs < qcflags.size(); ++jobs) {
40  if (qcflags[jobs] == imiss || !obsValues || (*obsValues)[jobs] == rmiss) {
41  qcflags[jobs] = QCflags::missing;
42  }
43  }
44 }
45 
46 } // namespace
47 
48 // -----------------------------------------------------------------------------
49 
50 QCmanager::QCmanager(ioda::ObsSpace & obsdb, const eckit::Configuration & /*config*/,
51  std::shared_ptr<ioda::ObsDataVector<int> > qcflags,
52  std::shared_ptr<ioda::ObsDataVector<float> > /*obserr*/)
53  : obsdb_(obsdb), nogeovals_(), nodiags_(), flags_(qcflags)
54 {
55  oops::Log::trace() << "QCmanager::QCmanager starting" << std::endl;
56 
57  ASSERT(qcflags);
58 
59  const oops::Variables &allSimulatedVars = obsdb.obsvariables();
60  const oops::Variables &initialSimulatedVars = obsdb.initial_obsvariables();
61  const oops::Variables &derivedSimulatedVars = obsdb.derived_obsvariables();
62 
63  ASSERT(allSimulatedVars.size() == initialSimulatedVars.size() + derivedSimulatedVars.size());
64  ASSERT(flags_->nvars() == allSimulatedVars.size());
65  ASSERT(flags_->nlocs() == obsdb_.nlocs());
66 
67  const float rmiss = util::missingValue(rmiss);
68  const int imiss = util::missingValue(imiss);
69 
70  const ioda::ObsDataVector<float> obs(obsdb, initialSimulatedVars, "ObsValue");
71 
72  // Iterate over initial simulated variables
73  for (size_t jv = 0; jv < initialSimulatedVars.size(); ++jv) {
74  const ioda::ObsDataRow<float> &currentObsValues = obs[jv];
75  ioda::ObsDataRow<int> &currentQCFlags = (*qcflags)[obs.varnames()[jv]];
76  updateQCFlags(&currentObsValues, currentQCFlags);
77  }
78 
79  // Iterate over derived simulated variables and if they don't exist yet, set their QC flags to
80  // 'missing'.
81  for (size_t jv = 0; jv < derivedSimulatedVars.size(); ++jv) {
82  ioda::ObsDataRow<int> &currentQCFlags = (*qcflags)[derivedSimulatedVars[jv]];
83  if (!obsdb.has("ObsValue", derivedSimulatedVars[jv])) {
84  updateQCFlags(nullptr, currentQCFlags);
85  } else {
86  std::vector<float> currentObsValues(obsdb_.nlocs());
87  obsdb_.get_db("ObsValue", derivedSimulatedVars[jv], currentObsValues);
88  updateQCFlags(&currentObsValues, currentQCFlags);
89  }
90  }
91 
92  oops::Log::trace() << "QCmanager::QCmanager done" << std::endl;
93 }
94 
95 // -----------------------------------------------------------------------------
96 
97 void QCmanager::postFilter(const ioda::ObsVector & hofx,
98  const ioda::ObsVector & /*bias*/,
99  const ObsDiagnostics & /*diags*/) {
100  oops::Log::trace() << "QCmanager postFilter" << std::endl;
101 
102  const double missing = util::missingValue(missing);
103  const oops::Variables &allSimulatedVars = obsdb_.obsvariables();
104 
105  for (size_t jv = 0; jv < allSimulatedVars.size(); ++jv) {
106  for (size_t jobs = 0; jobs < obsdb_.nlocs(); ++jobs) {
107  size_t iobs = allSimulatedVars.size() * jobs + jv;
108  if ((*flags_)[jv][jobs] == 0 && hofx[iobs] == missing) {
109  (*flags_)[jv][jobs] = QCflags::Hfailed;
110  }
111  }
112  }
113  oops::Log::trace() << "QCmanager postFilter done" << std::endl;
114 }
115 
116 // -----------------------------------------------------------------------------
117 
119  oops::Log::trace() << "QCmanager::~QCmanager starting" << std::endl;
120  oops::Log::info() << *this;
121  oops::Log::trace() << "QCmanager::~QCmanager done" << std::endl;
122 }
123 
124 // -----------------------------------------------------------------------------
125 
126 void QCmanager::print(std::ostream & os) const {
127  const std::vector<std::pair<int, const char*>> cases{
128  // Special cases reported using dedicated code
129  {QCflags::pass, nullptr},
130  {76, nullptr}, // } The numbers of observations with these two flags
131  {77, nullptr}, // } will be added up and reported together
132 
133  // "Normal" cases reported in a uniform way
134  {QCflags::passive, "passive observations"},
135  {QCflags::missing, "missing values"},
136  {QCflags::preQC, "rejected by pre QC"},
137  {QCflags::bounds, "out of bounds"},
138  {QCflags::domain, "out of domain of use"},
139  {QCflags::black, "black-listed"},
140  {QCflags::Hfailed, "H(x) failed"},
141  {QCflags::thinned, "removed by thinning"},
142  {QCflags::derivative, "dy/dx out of valid range"},
143  {QCflags::clw, "removed by cloud liquid water check"},
144  {QCflags::profile, "removed by profile consistency check"},
145  {QCflags::fguess, "rejected by first-guess check"},
146  {QCflags::diffref, "rejected by difference check"},
147  {QCflags::seaice, "removed by sea ice check"},
148  {QCflags::track, "removed by track check"},
149  {QCflags::buddy, "removed by buddy check"},
150  {QCflags::onedvar, "removed by 1D Var check"},
151  {QCflags::bayesianQC, "removed by Bayesian background check"},
152  {QCflags::modelobthresh, "removed by ModelOb threshold"}
153  };
154  const size_t numSpecialCases = 3;
155 
156  const size_t nlocs = obsdb_.nlocs();
157  const size_t gnlocs = obsdb_.globalNumLocs();
158 
159  const oops::Variables &allSimulatedVars = obsdb_.obsvariables();
160 
161  for (size_t jvar = 0; jvar < allSimulatedVars.size(); ++jvar) {
162  std::unique_ptr<ioda::Accumulator<std::vector<size_t>>> accumulator =
163  obsdb_.distribution()->createAccumulator<size_t>(cases.size());
164 
165  for (size_t jobs = 0; jobs < nlocs; ++jobs) {
166  const int actualFlag = (*flags_)[jvar][jobs];
167  for (size_t jcase = 0; jcase < cases.size(); ++jcase)
168  if (actualFlag == cases[jcase].first)
169  accumulator->addTerm(jobs, jcase, 1);
170  }
171  const std::vector<std::size_t> counts = accumulator->computeResult();
172 
173  if (obsdb_.comm().rank() == 0) {
174  const std::string info = "QC " + flags_->obstype() + " " + allSimulatedVars[jvar] + ": ";
175 
176  // Normal cases
177  for (size_t i = numSpecialCases; i < counts.size(); ++i)
178  if (counts[i] > 0)
179  os << info << counts[i] << " " << cases[i].second << "." << std::endl;
180 
181  // Special cases: the GNSSRO check...
182  const size_t nGNSSRO = counts[1] + counts[2];
183  if (nGNSSRO > 0)
184  os << info << nGNSSRO << " rejected by GNSSRO reality check." << std::endl;
185 
186  // ... the number of passed observations and the total number of observations.
187  const size_t npass = counts[0];
188  os << info << npass << " passed out of " << gnlocs << " observations." << std::endl;
189  }
190 
191  const size_t numRecognizedFlags = std::accumulate(counts.begin(), counts.end(), 0);
192  ASSERT(numRecognizedFlags == gnlocs);
193  }
194 }
195 
196 // -----------------------------------------------------------------------------
197 
198 } // namespace ufo
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Definition: QCmanager.h:57
void postFilter(const ioda::ObsVector &, const ioda::ObsVector &, const ObsDiagnostics &) override
Definition: QCmanager.cc:97
QCmanager(ioda::ObsSpace &, const eckit::Configuration &, std::shared_ptr< ioda::ObsDataVector< int > >, std::shared_ptr< ioda::ObsDataVector< float > >)
Definition: QCmanager.cc:50
void print(std::ostream &) const override
Definition: QCmanager.cc:126
ioda::ObsSpace & obsdb_
Definition: QCmanager.h:54
constexpr int bounds
Definition: QCflags.h:22
constexpr int Hfailed
Definition: QCflags.h:25
constexpr int black
Definition: QCflags.h:24
constexpr int preQC
Definition: QCflags.h:21
constexpr int seaice
Definition: QCflags.h:30
constexpr int clw
Definition: QCflags.h:28
constexpr int bayesianQC
Definition: QCflags.h:36
constexpr int thinned
Definition: QCflags.h:26
constexpr int pass
Definition: QCflags.h:14
constexpr int modelobthresh
Definition: QCflags.h:37
constexpr int onedvar
Definition: QCflags.h:35
constexpr int derivative
Definition: QCflags.h:33
constexpr int fguess
Definition: QCflags.h:29
constexpr int missing
Definition: QCflags.h:20
constexpr int domain
Definition: QCflags.h:23
constexpr int profile
Definition: QCflags.h:34
constexpr int buddy
Definition: QCflags.h:32
constexpr int track
Definition: QCflags.h:31
constexpr int passive
Definition: QCflags.h:15
constexpr int diffref
Definition: QCflags.h:27
void updateQCFlags(const std::vector< float > *obsValues, std::vector< int > &qcflags)
Definition: QCmanager.cc:36
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27