UFO
ObsBoundsCheck.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 <set>
12 #include <vector>
13 
14 #include "eckit/config/Configuration.h"
15 
16 #include "ioda/ObsDataVector.h"
17 #include "ioda/ObsSpace.h"
18 #include "oops/util/abor1_cpp.h"
19 #include "oops/util/IntSetParser.h"
20 #include "oops/util/Logger.h"
21 #include "oops/util/missingValues.h"
22 #include "ufo/filters/QCflags.h"
24 #include "ufo/utils/StringUtils.h"
25 
26 namespace ufo {
27 
28 namespace {
29 
30 // -----------------------------------------------------------------------------
31 
32 /// Set each element of \p flagged to true if the corresponding element of \p apply is true
33 /// and the corresponding element of \p testValues is a non-missing value lying outside the
34 /// closed interval [\p minValue, \p maxValue].
35 void flagWhereOutOfBounds(const std::vector<bool> & apply,
36  const std::vector<float> & testValues,
37  const float minValue,
38  const float maxValue,
39  const bool treatMissingAsOutOfBounds,
40  std::vector<bool> &flagged) {
41  const size_t nlocs = testValues.size();
42  ASSERT(apply.size() == nlocs);
43  ASSERT(flagged.size() == nlocs);
44 
45  const float missing = util::missingValue(missing);
46  for (size_t i = 0; i < nlocs; ++i) {
47  if (apply[i]) {
48  if (testValues[i] != missing) {
49  if (minValue != missing && testValues[i] < minValue)
50  flagged[i] = true;
51  if (maxValue != missing && testValues[i] > maxValue)
52  flagged[i] = true;
53  } else {
54  if (treatMissingAsOutOfBounds)
55  flagged[i] = true;
56  }
57  }
58  }
59 }
60 
61 } // namespace
62 
63 // -----------------------------------------------------------------------------
64 
65 ObsBoundsCheck::ObsBoundsCheck(ioda::ObsSpace & obsdb, const Parameters_ & parameters,
66  std::shared_ptr<ioda::ObsDataVector<int> > flags,
67  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
68  : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters)
69 {
70  if (parameters_.testVariables.value() != boost::none) {
71  for (const Variable & var : *parameters_.testVariables.value())
72  allvars_ += var;
73  }
74  oops::Log::debug() << "ObsBoundsCheck: config (constructor) = " << parameters_ << std::endl;
75 }
76 
77 // -----------------------------------------------------------------------------
78 
80 
81 // -----------------------------------------------------------------------------
82 
83 void ObsBoundsCheck::applyFilter(const std::vector<bool> & apply,
84  const Variables & filtervars,
85  std::vector<std::vector<bool>> & flagged) const {
86  // Find the variables that should be tested. Use the variables specified in the 'test variables'
87  // option if present, otherwise the filter variables.
88  ufo::Variables testvars;
89  if (parameters_.testVariables.value() != boost::none) {
90  for (const Variable & var : *parameters_.testVariables.value())
91  testvars += var;
92  } else {
93  testvars += ufo::Variables(filtervars, "ObsValue");
94  }
95  if (!testvars)
96  throw eckit::UserError("ObsBoundsCheck: The list of test variables is empty", Here());
97 
98  oops::Log::debug() << "ObsBoundsCheck: filtering " << filtervars << " with "
99  << testvars << std::endl;
100 
101  // Retrieve the bounds.
102  const float missing = util::missingValue(missing);
103  const float vmin = parameters_.minvalue.value().value_or(missing);
104  const float vmax = parameters_.maxvalue.value().value_or(missing);
105 
106  // Determine the mode of operation.
107  const bool flagAllFilterVarsIfAnyTestVarOutOfBounds =
108  parameters_.testVariables.value() != boost::none &&
110  testvars.nvars() == 1);
111  const bool onlyTestGoodFilterVarsForFlagAllFilterVars =
113  const bool treatMissingAsOutOfBounds = parameters_.treatMissingAsOutOfBounds;
114 
115  // Do the actual work.
116  if (flagAllFilterVarsIfAnyTestVarOutOfBounds) {
117  // If using test only good filter vars then the number of filter
118  // vars must equal the number of test vars
119  if (onlyTestGoodFilterVarsForFlagAllFilterVars && filtervars.nvars() != testvars.nvars())
120  throw eckit::UserError("The number of 'primitive' (single-channel) test variables must "
121  "match that of 'primitive' filter variables when using the "
122  "'test only filter variables with passed qc when flagging all "
123  "filter variables' option.");
124  ASSERT(filtervars.nvars() == flagged.size());
125  // Loop over all channels of all test variables and record all locations where any of these
126  // channels is out of bounds.
127  std::vector<bool> anyTestVarOutOfBounds(obsdb_.nlocs(), false);
128  size_t ifiltervar = 0;
129  for (PrimitiveVariable singleChannelTestVar : PrimitiveVariables(testvars, data_)) {
130  std::vector<bool> testAtLocations = apply;
131  if (onlyTestGoodFilterVarsForFlagAllFilterVars) {
132  for (size_t iloc=0; iloc < testAtLocations.size(); iloc++)
133  if ((*flags_)[ifiltervar][iloc] != QCflags::pass) testAtLocations[iloc] = false;
134  }
135  const std::vector<float> & testValues = singleChannelTestVar.values();
136  flagWhereOutOfBounds(testAtLocations, testValues, vmin, vmax, treatMissingAsOutOfBounds,
137  anyTestVarOutOfBounds);
138  ifiltervar++;
139  }
140  // Copy these flags to the flags of all filtered variables.
141  for (std::vector<bool> &f : flagged)
142  f = anyTestVarOutOfBounds;
143  } else {
144  if (filtervars.nvars() != testvars.nvars())
145  throw eckit::UserError("The number of 'primitive' (single-channel) test variables must match "
146  "that of 'primitive' filter variables unless the 'flag all filter "
147  "variables if any test variable is out of bounds' option is set");
148  // Loop over all channels of all test variables and for each locations where that channel is out
149  // of bounds, flag the corresponding filter variable channel.
150  ASSERT(filtervars.nvars() == flagged.size());
151  size_t ifiltervar = 0;
152  for (PrimitiveVariable singleChannelTestVar : PrimitiveVariables(testvars, data_)) {
153  const std::vector<float> & testValues = singleChannelTestVar.values();
154  flagWhereOutOfBounds(apply, testValues, vmin, vmax, treatMissingAsOutOfBounds,
155  flagged[ifiltervar++]);
156  }
157  }
158 }
159 
160 // -----------------------------------------------------------------------------
161 
162 void ObsBoundsCheck::print(std::ostream & os) const {
163  os << "ObsBoundsCheck: config = " << parameters_ << std::endl;
164 }
165 
166 // -----------------------------------------------------------------------------
167 
168 } // namespace ufo
Base class for UFO QC filters.
Definition: FilterBase.h:45
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
ObsBoundsCheck(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr< ioda::ObsDataVector< int > >, std::shared_ptr< ioda::ObsDataVector< float > >)
void print(std::ostream &) const override
Parameters_ parameters_
Parameters controlling the operation of the ObsBoundsCheck filter.
oops::Parameter< bool > onlyTestGoodFilterVarsForFlagAllFilterVars
oops::OptionalParameter< std::vector< Variable > > testVariables
oops::Parameter< bool > treatMissingAsOutOfBounds
oops::Parameter< bool > flagAllFilterVarsIfAnyTestVarOutOfBounds
oops::OptionalParameter< float > minvalue
Minimum allowed value of the tested variables.
oops::OptionalParameter< float > maxvalue
Maximum allowed value of the tested variables.
ufo::Variables allvars_
ioda::ObsSpace & obsdb_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
A range covering all "primitive" (single-channel) variables in a Variables object.
size_t nvars() const
Return the number of constituent "primitive" (single-channel) variables.
Definition: Variables.cc:104
logical, parameter f
constexpr int pass
Definition: QCflags.h:14
constexpr int missing
Definition: QCflags.h:20
void flagWhereOutOfBounds(const std::vector< bool > &apply, const std::vector< float > &testValues, const float minValue, const float maxValue, const bool treatMissingAsOutOfBounds, std::vector< bool > &flagged)
Value maxValue(const std::map< Key, Value > &map)
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27