UFO
ProbabilityGrossErrorWholeReport.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 Met Office UK
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 <cmath>
11 #include <vector>
12 
13 #include "eckit/config/Configuration.h"
14 
15 #include "ioda/ObsDataVector.h"
16 #include "ioda/ObsSpace.h"
17 
18 #include "oops/util/FloatCompare.h"
19 #include "oops/util/Logger.h"
20 #include "ufo/filters/QCflags.h"
23 
24 namespace ufo {
25 
26 // -----------------------------------------------------------------------------
28  const Parameters_ & parameters,
29  std::shared_ptr<ioda::ObsDataVector<int> > flags,
30  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
31  : FilterBase(obsdb, parameters, flags, obserr),
32  parameters_(parameters)
33 {
34  oops::Log::trace() << "ProbabilityGrossErrorWholeReport contructor starting" << std::endl;
35 }
36 
37 // -----------------------------------------------------------------------------
38 
40  oops::Log::trace() << "ProbabilityGrossErrorWholeReport destructed" << std::endl;
41 }
42 
43 // -----------------------------------------------------------------------------
44 
45 void ProbabilityGrossErrorWholeReport::applyFilter(const std::vector<bool> & apply,
46  const Variables & filtervars,
47  std::vector<std::vector<bool>> & flagged) const {
48  oops::Log::trace() << "ProbabilityGrossErrorWholeReport preProcess" << std::endl;
49  // Missing value indicator
50  const float missingValueFloat = util::missingValue(missingValueFloat);
51  // Dimensions
52  const size_t nlocs = obsdb_.nlocs();
53  const size_t nvars = filtervars.nvars();
54  // Missing data indicator for stored PGEs.
55  const float PGEMDI = 1.111f;
56  // PGE multiplication factor used to store PGE values for later use.
57  const float PGEMult = 1000.0;
58  // PGE rejection limit.
59  const float PGECrit = parameters_.PGEParameters.PGE_PGECrit.value();
60 
61  // Initial Probability of Gross error in whole report
62  std::vector<float> PBadRep(nlocs);
63  // Overall Probability density for whole report
64  std::vector<float> PdReport(nlocs, 1.0f);
65  // Probability density of bad observations for each variable
66  std::vector<float> PdBad(nvars);
67  // Probability density of bad synop observations for each variable
68  std::vector<float> PdBad_synop(nvars);
69  // Probability density of bad bogus observations for each variable
70  std::vector<float> PdBad_bogus(nvars);
71  // Probabilty density of bad observations used for each observation
72  std::vector<std::vector<float>> PdBad_used(nvars, std::vector<float>(nlocs));
73  // Is this variable used in the whole report error calculation?
74  std::vector<bool> notUsed(nvars);
75  // Vector holding filter variable names
76  std::vector<std::string> varname(nvars);
77  // Vector holding probability of gross error for input to Buddy check
78  std::vector<std::vector<float>> varPGEBd(nvars, std::vector<float>(nlocs));
79  // Vector holding combined probability densities
80  std::vector<std::vector<float>> varPGE(nvars, std::vector<float>(nlocs));
81  // Vector holding packed PGEs
82  std::vector<std::vector<float>> varPGEFinal(nvars, std::vector<float>(nlocs));
83  // Vector holding OPS style QCflags for each variable
84  std::vector<std::vector<int>> varQCflags(nvars, std::vector<int>(nlocs));
85 
86  for (size_t ivar = 0; ivar < filtervars.nvars(); ++ivar) {
87  varname[ivar] = filtervars.variable(ivar).variable();
88  // Get Gross Error Probability values and Met Office QCFlags from ObsSpace
89  if (obsdb_.has("GrossErrorProbability", varname[ivar]) &&
90  obsdb_.has("GrossErrorProbabilityTotal", varname[ivar]) &&
91  obsdb_.has("GrossErrorProbabilityFinal", varname[ivar]) &&
92  obsdb_.has("QCFlags", varname[ivar])) {
93  obsdb_.get_db("GrossErrorProbability", varname[ivar], varPGEBd[ivar]);
94  obsdb_.get_db("GrossErrorProbabilityTotal", varname[ivar], varPGE[ivar]);
95  obsdb_.get_db("GrossErrorProbabilityFinal", varname[ivar], varPGEFinal[ivar]);
96  obsdb_.get_db("QCFlags", varname[ivar], varQCflags[ivar]);
97  } else {
98  std::stringstream errormessage;
99  errormessage << "GrossErrorProbability/" + varname[ivar] + ", "
100  << "GrossErrorProbabilityTotal/" + varname[ivar] + ", "
101  << "GrossErrorProbabilityFinal/" + varname[ivar] + ", and "
102  << "QCFlags/" + varname[ivar] + " must all be present"
103  << std::endl;
104  throw eckit::BadValue(errormessage.str(), Here());
105  }
106  // Get variable options
107  PdBad[ivar] = filtervars[ivar].options().getFloat("probability_density_bad", 0.1f);
108  if (filtervars[ivar].options().has("synop_probability_density_bad")) {
109  PdBad_synop[ivar] = filtervars[ivar].options().getFloat("synop_probability_density_bad");
110  } else {
111  PdBad_synop[ivar] = PdBad[ivar];
112  }
113  if (filtervars[ivar].options().has("bogus_probability_density_bad")) {
114  PdBad_bogus[ivar] = filtervars[ivar].options().getFloat("bogus_probability_density_bad");
115  } else {
116  PdBad_bogus[ivar] = PdBad[ivar];
117  }
118  //
119  notUsed[ivar] = filtervars[ivar].options().getBool("not_used_in_whole_report", false);
120  }
121  // Get input probability of gross error affecting whole report
122  std::vector<float> ReportPGE(nlocs);
123  if (obsdb_.has("MetaData", "GrossErrorProbabilityReport")) {
124  obsdb_.get_db("MetaData", "GrossErrorProbabilityReport", ReportPGE);
125  } else {
126  throw eckit::BadValue("MetaData/GrossErrorProbabilityReport not present", Here());
127  }
128  // Get ObsType
129  std::vector<int> ObsType(nlocs);
130  if (obsdb_.has("MetaData", "ObsType")) {
131  obsdb_.get_db("MetaData", "ObsType", ObsType);
132  } else {
133  throw eckit::BadValue("MetaData/ObsType not present", Here());
134  }
135 
136  // Calculate probability that whole report is affected by gross error
137  for (size_t jobs = 0; jobs < nlocs; ++jobs) {
138  if (apply[jobs] && (ReportPGE[jobs] > 0)) {
139  // Probability density given Gross error in whole report
140  float PdBadRep = 1.0f;
141  for (size_t ivar = 0; ivar < filtervars.nvars(); ++ivar) {
142  if (!notUsed[ivar]) {
143  PdReport[jobs] *= varPGE[ivar][jobs];
144  if (ObsType[jobs] == MetOfficeObsIDs::Bogus::Bogus) {
145  PdBad_used[ivar][jobs] = PdBad_bogus[ivar];
146  } else if (ObsType[jobs] == MetOfficeObsIDs::Surface::SynopManual ||
147  ObsType[jobs] == MetOfficeObsIDs::Surface::SynopAuto ||
148  ObsType[jobs] == MetOfficeObsIDs::Surface::MetarManual ||
149  ObsType[jobs] == MetOfficeObsIDs::Surface::MetarAuto ||
150  ObsType[jobs] == MetOfficeObsIDs::Surface::SynopMob ||
151  ObsType[jobs] == MetOfficeObsIDs::Surface::SynopBufr ||
152  ObsType[jobs] == MetOfficeObsIDs::Surface::WOW) {
153  PdBad_used[ivar][jobs] = PdBad_synop[ivar];
154  } else {
155  PdBad_used[ivar][jobs] = PdBad[ivar];
156  }
157  PdBadRep *= PdBad_used[ivar][jobs];
158  }
159  }
160  PBadRep[jobs] = ReportPGE[jobs];
161  PdBadRep *= PBadRep[jobs];
162  PdReport[jobs] = PdBadRep + ((1.0f - PBadRep[jobs]) * PdReport[jobs]);
163  if (PdReport[jobs] > 0.0f) {
164  ReportPGE[jobs] = PdBadRep / PdReport[jobs];
165  }
166  }
167  }
168 
169  // Calculate updated probability that individual observation OR
170  // whole report is affected by gross error
171  bool secondComponentOfTwo = false;
172  for (size_t ivar = 0; ivar < filtervars.nvars(); ++ivar) {
173  secondComponentOfTwo = filtervars[ivar].options().getBool("second_component_of_two", false);
174  if (secondComponentOfTwo) {
175  varPGEBd[ivar] = varPGEBd[ivar - 1];
176  varPGEFinal[ivar] = varPGEFinal[ivar - 1];
177  varQCflags[ivar] = varQCflags[ivar - 1];
178  flagged[ivar] = flagged[ivar - 1];
179  } else {
180  for (size_t jobs = 0; jobs < nlocs; ++jobs) {
181  if (apply[jobs] && (ReportPGE[jobs] > 0) && (PdReport[jobs] > 0)) {
182  if (varPGEFinal[ivar][jobs] < PGEMult) {
183  float intpart;
184  // Initial Probability of Gross error in element
185  float const PGE0 = std::modf(varPGEFinal[ivar][jobs], &intpart);
186  float PdProduct = 1.0f;
187  for (size_t jvar = 0; jvar < filtervars.nvars(); ++jvar) {
188  if ((jvar != ivar) && !notUsed[jvar]) {
189  PdProduct *= varPGE[jvar][jobs];
190  }
191  }
192  // PGE in element or whole report
193  float PGE1;
194  if (notUsed[ivar]) {
195  PGE1 = ReportPGE[jobs] + varPGEBd[ivar][jobs] -
196  ReportPGE[jobs] * varPGEBd[ivar][jobs];
197  } else {
198  PGE1 = ReportPGE[jobs] + (PdBad_used[ivar][jobs] * PdProduct) * PGE0 *
199  (1.0f - PBadRep[jobs])/PdReport[jobs];
200  }
201  varPGEFinal[ivar][jobs] = floor(PGE1 * PGEMult) + PGE0; // PGE packing
202  if (PGE1 >= PGECrit) {
203  varQCflags[ivar][jobs] |= ufo::MetOfficeQCFlags::Elem::BackRejectFlag;
204  flagged[ivar][jobs] = true;
205  }
206  if (std::abs(varPGEBd[ivar][jobs] - PGEMDI) > 0.001f) {
207  varPGEBd[ivar][jobs] = PGE1;
208  }
209  }
210  }
211  }
212  }
213  // Save updated gross error probabilities and QCFlags to ObsSpace
214  obsdb_.put_db("GrossErrorProbability", varname[ivar], varPGEBd[ivar]);
215  obsdb_.put_db("GrossErrorProbabilityFinal", varname[ivar], varPGEFinal[ivar]);
216  obsdb_.put_db("QCFlags", varname[ivar], varQCflags[ivar]);
217  }
218  obsdb_.put_db("MetaData", "GrossErrorProbabilityReport", ReportPGE);
219 }
220 
221 // -----------------------------------------------------------------------------
222 
223 void ProbabilityGrossErrorWholeReport::print(std::ostream & os) const {
224  os << "ProbabilityGrossErrorWholeReport: config = " << parameters_ << std::endl;
225 }
226 
227 // -----------------------------------------------------------------------------
228 
229 } // namespace ufo
Base class for UFO QC filters.
Definition: FilterBase.h:45
ioda::ObsSpace & obsdb_
ProbabilityGrossErrorWholeReport(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr< ioda::ObsDataVector< int >>, std::shared_ptr< ioda::ObsDataVector< float >>)
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
Parameters controlling the operation of the ProbabilityGrossErrorWholeReport filter.
oops::Parameter< float > PGE_PGECrit
PGE rejection limit.
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
@ MetarManual
non-automatic metar
@ SynopManual
non-automatic synop
@ BackRejectFlag
PGE>0.5 after backgr check.
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27
util::Duration abs(const util::Duration &duration)