UFO
ProfileCheckValidator.cc
Go to the documentation of this file.
1 /*
2  * (C) Crown copyright 2020, Met Office
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 
8 #include <algorithm>
9 #include <map>
10 #include <memory>
11 #include <string>
12 
13 #include "eckit/exception/Exceptions.h"
14 
15 #include "oops/util/Logger.h"
16 
19 
20 #include "ufo/utils/StringUtils.h"
21 
22 namespace ufo {
25  : options_(options)
26  {
27  // Set offsets due to C++ and Fortran array index starting values
34 
35  // List of checks performed
36  std::vector <std::string> checks = options_.Checks.value();
37 
38  // Loop over each check and populate lists of integer and float values to compare
39  for (const auto& check : checks) {
40  if (check == "Basic") {
41  } else if (check == "SamePDiffT") {
42  valuesToCompare_int_.insert({
46  } else if (check == "Sign") {
47  valuesToCompare_int_.insert({
51  } else if (check == "UnstableLayer") {
52  valuesToCompare_int_.insert({
56  valuesToCompare_float_.insert({
58  } else if (check == "Interpolation") {
59  valuesToCompare_int_.insert({
71  valuesToCompare_float_.insert({
74  } else if (check == "Hydrostatic") {
75  valuesToCompare_int_.insert({
85  valuesToCompare_float_.insert({
88  } else if (check == "UInterp" || check == "UInterpAlternative") {
89  valuesToCompare_int_.insert({
99  valuesToCompare_float_.insert({
103  } else if (check == "RH") {
104  valuesToCompare_int_.insert({
113  valuesToCompare_float_.insert({
120  } else if (check == "Time") {
121  valuesToCompare_int_.insert({
124  } else if (check == "PermanentReject") {
125  valuesToCompare_int_.insert({
131  } else if (check.find("Background") != std::string::npos) {
132  valuesToCompare_int_.insert({
138  valuesToCompare_float_.insert({
148  } else if (check == "Pressure") {
149  valuesToCompare_int_.insert({
151  valuesToCompare_float_.insert({
153  } else if (check == "AveragePressure") {
154  valuesToCompare_float_.insert({
161  } else if (check == "AverageTemperature") {
162  valuesToCompare_int_.insert({
165  valuesToCompare_float_.insert({
168  } else if (check == "AverageWindSpeed") {
169  valuesToCompare_int_.insert({
174  valuesToCompare_float_.insert({
177  } else if (check == "AverageRelativeHumidity") {
178  valuesToCompare_int_.insert({
181  valuesToCompare_float_.insert({
183  }
184  }
185  }
186 
187  /// Comparison of single values
188  template <typename T>
189  void ProfileCheckValidator::compareOutput(const std::string &desc,
190  const T val1,
191  const T val2,
192  const int offset,
193  const float tol,
194  int &n)
195  {
196  if (!differenceWithinTol(val1, val2 + offset, tol)) {
197  oops::Log::debug() << "Mismatch for " << desc << " (OPS, this code): "
198  << val1 << ", " << val2 + offset << std::endl;
199  n++;
200  }
201  }
202 
203  /// Comparison of vectors of values
204  template <typename T>
205  void ProfileCheckValidator::compareOutput(const std::string &desc,
206  const std::vector <T> &vec1,
207  const std::vector <T> &vec2,
208  const int offset,
209  const float tol,
210  int &n)
211  {
212  // Do not compare vectors if at least one is empty
213  if (oops::anyVectorEmpty(vec1, vec2))
214  {
215  if (vec1.empty())
216  oops::Log::debug() << "Vector of " << desc << " in OPS output is empty" << std::endl;
217  if (vec2.empty())
218  oops::Log::debug() << "Vector of " << desc << " in this code is empty" << std::endl;
219  return;
220  }
221  // Compare vector elements up to the smaller of the two sizes.
222  const size_t vecsize = std::min(vec1.size(), vec2.size());
223  for (size_t jvec = 0; jvec < vecsize; ++jvec) {
224  if (!differenceWithinTol(vec1[jvec], vec2[jvec] + offset, tol)) {
225  oops::Log::debug() << "Mismatch for " << desc << "[" << jvec << "] "
226  << "(OPS, this code): " << vec1[jvec] << ", "
227  << vec2[jvec] + offset << std::endl;
228  n++;
229  }
230  }
231  }
232 
234  size_t commSize)
235  {
236  oops::Log::debug() << " Comparing values against OPS equivalents..." << std::endl;
237 
238  // Reset number of mismatches for this profile
239  nMismatches_ = 0;
240 
241  float tol = options_.Comparison_Tol.value(); // Comparison tolerance
242 
243  // Compare integer values obtained in this code and OPS
244  for (const auto& valueToCompare_int : valuesToCompare_int_) {
245  std::string varname;
246  std::string groupname;
247  ufo::splitVarGroup(valueToCompare_int, varname, groupname);
248  std::string varname_OPS = "OPS_" + valueToCompare_int;
249  if (groupname == "Counters") {
250  /// Special case: OPS counters have one value per profile level,
251  /// and are in the MetaData rather than the Counters group.
252  /// This avoids the (default) treatment which assumes
253  /// that variables in the Counters group have one value per profile.
254  varname_OPS = "OPS_" + varname + "@MetaData";
255  }
256 
257  // Obtain values for comparison
258  const std::vector <int> &values_thiscode =
259  profileDataHandler.get<int>(valueToCompare_int);
260  const std::vector <int> &values_OPS =
261  profileDataHandler.get<int>(varname_OPS);
262 
263  // Account for potential offset between values in this code and OPS
264  int offset = 0;
265 
266  // Offsets due to C++ and Fortran array indices
267  auto comparison_offsets_it = comparison_offsets_.find(valueToCompare_int);
268  if (comparison_offsets_it != comparison_offsets_.end())
269  offset = comparison_offsets_it->second;
270 
271  // Offsets due to particular counters being accumulated over profiles in OPS
272  // (and not in this code). NumAnyErrors is not included.
273  if (groupname == "Counters" && varname != "NumAnyErrors")
274  offset = cumulativeCounters_[valueToCompare_int];
275 
276  // Only the first element of each counter is compared;
277  // in all other cases tne entire vectors are compared.
278  if (groupname == "Counters") {
279  // The counter comparison is only performed if there is one processor.
280  // With some refactoring it would be possible to use eckit::allGatherv
281  // to sum the results over multiple processors but, at present,
282  // if two processors have a different number of profiles then the allGatherv
283  // routine on the processor with more profiles will hang indefinitely.
284  if (commSize == 1) {
285  if (!oops::anyVectorEmpty(values_OPS, values_thiscode))
286  compareOutput(valueToCompare_int, values_OPS[0], values_thiscode[0],
287  offset, tol, nMismatches_);
288  } else {
289  oops::Log::debug() << "The counter comparison was not performed "
290  << "because multiple processors are in use." << std::endl;
291  }
292  } else {
293  compareOutput(valueToCompare_int, values_OPS, values_thiscode,
294  offset, tol, nMismatches_);
295  }
296 
297  // Increment cumulative counters. NumAnyErrors is not included.
298  if (groupname == "Counters" && varname != "NumAnyErrors")
299  cumulativeCounters_[valueToCompare_int] += values_thiscode[0];
300  }
301 
302  // Compare float values obtained in this code and OPS
303  for (const auto& valueToCompare_float : valuesToCompare_float_) {
304  const std::vector <float> &values_thiscode =
305  profileDataHandler.get<float>(valueToCompare_float);
306  const std::vector <float> &values_OPS =
307  profileDataHandler.get<float>("OPS_" + valueToCompare_float);
308  compareOutput(valueToCompare_float, values_OPS, values_thiscode,
309  0, tol, nMismatches_);
310  }
311 
312  oops::Log::debug() << " ... all comparisons done ("
313  << nMismatches_ << " mismatches)" << std::endl;
314  }
315 } // namespace ufo
316 
317 
Options controlling the operation of the ConventionalProfileProcessing filter.
oops::Parameter< float > Comparison_Tol
Tolerance for absolute difference comparisions.
oops::Parameter< std::vector< std::string > > Checks
List of checks to perform.
std::map< std::string, int > comparison_offsets_
std::set< std::string > valuesToCompare_int_
Integer values to compare.
const ConventionalProfileProcessingParameters & options_
Configurable parameters.
ProfileCheckValidator(const ConventionalProfileProcessingParameters &options)
int nMismatches_
Number of mismatches between this code and OPS (separate for each profile).
std::map< std::string, int > cumulativeCounters_
Counters that are accumulated across profiles.
std::set< std::string > valuesToCompare_float_
Float values to compare.
void validate(ProfileDataHandler &profileDataHandler, size_t commSize)
Validate check results against OPS values.
bool differenceWithinTol(const T A, const T B, const float tol=1e-10) const
Determine difference between two values within a certain tolerance.
void compareOutput(const std::string &desc, const T val1, const T val2, const int offset, const float tol, int &n)
Compare values with specified offset and tolerance.
Retrieve and store data for individual profiles. To do this, first the vector of values in the entire...
std::vector< T > & get(const std::string &fullname)
subroutine check(action, status)
Definition: RunCRTM.h:27
void splitVarGroup(const std::string &vargrp, std::string &var, std::string &grp)
Definition: StringUtils.cc:27
static constexpr const char *const rhbk
static constexpr const char *const ETol
static constexpr const char *const qcflags_observation_report
Definition: VariableNames.h:98
static constexpr const char *const qcflags_eastward_wind
static constexpr const char *const counter_TotHFlags
static constexpr const char *const pge_geopotential_height
Definition: VariableNames.h:67
static constexpr const char *const modellevels_ExnerP_derived
static constexpr const char *const tInterp
static constexpr const char *const counter_TotLFlags
static constexpr const char *const LogP
static constexpr const char *const counter_NumSamePErrObs
static constexpr const char *const counter_NumGapsU
static constexpr const char *const counter_NumGapsUWP
static constexpr const char *const counter_NumInterpErrObs
static constexpr const char *const bigPgaps_derived
static constexpr const char *const FlagH
static constexpr const char *const LevErrors
static constexpr const char *const counter_NumInterpErrors
static constexpr const char *const modellevels_average_eastward_wind_derived
static constexpr const char *const counter_NumAnyErrors
static constexpr const char *const counter_NumHydErrObs
static constexpr const char *const pge_relative_humidity
Definition: VariableNames.h:63
static constexpr const char *const HydError
static constexpr const char *const qcflags_northward_wind
static constexpr const char *const PBottom
static constexpr const char *const modellevels_logP_derived
static constexpr const char *const counter_NumSuperadiabat
static constexpr const char *const Temp
static constexpr const char *const qcflags_relative_humidity
static constexpr const char *const modellevels_average_eastward_wind_qcflags
static constexpr const char *const rh
static constexpr const char *const pge_northward_wind
Definition: VariableNames.h:66
static constexpr const char *const modellevels_average_northward_wind_qcflags
static constexpr const char *const uInterp
static constexpr const char *const NumSig
static constexpr const char *const modellevels_average_relative_humidity_derived
static constexpr const char *const counter_TotCFlags
static constexpr const char *const LogP_derived
static constexpr const char *const pgebd_northward_wind
Definition: VariableNames.h:78
static constexpr const char *const DC
static constexpr const char *const td
static constexpr const char *const NumStd
static constexpr const char *const Indx
static constexpr const char *const modellevels_logP_rho_derived
static constexpr const char *const Press
static constexpr const char *const pge_air_temperature
Definition: VariableNames.h:62
static constexpr const char *const SigBelow
static constexpr const char *const counter_NumStdMiss
static constexpr const char *const modellevels_ExnerP_rho_derived
static constexpr const char *const pgebd_air_temperature
Definition: VariableNames.h:72
static constexpr const char *const counter_NumSignChange
static constexpr const char *const StdLev
static constexpr const char *const vInterp
static constexpr const char *const modellevels_air_temperature_derived
static constexpr const char *const modellevels_average_relative_humidity_qcflags
static constexpr const char *const counter_NumGapsT
static constexpr const char *const counter_Num100Miss
static constexpr const char *const obs_air_pressure
Definition: VariableNames.h:18
static constexpr const char *const qcflags_air_temperature
Definition: VariableNames.h:99
static constexpr const char *const modellevels_average_air_temperature_derived
static constexpr const char *const pgebd_eastward_wind
Definition: VariableNames.h:76
static constexpr const char *const modellevels_average_northward_wind_derived
static constexpr const char *const SigAbove
static constexpr const char *const pge_eastward_wind
Definition: VariableNames.h:65
static constexpr const char *const counter_Num925Miss
static constexpr const char *const counter_NumIntHydErrors
static constexpr const char *const modellevels_average_air_temperature_qcflags
static constexpr const char *const counter_TotHProfs
static constexpr const char *const qcflags_geopotential_height
static constexpr const char *const counter_NumGapsRH
static constexpr const char *const IndStd
static constexpr const char *const tbk
static constexpr const char *const counter_TotCProfs
static constexpr const char *const pgebd_relative_humidity
Definition: VariableNames.h:74