UFO
ProfileCheckHydrostatic.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 
10 
11 namespace ufo {
12 
14 
17  : ProfileCheckBase(options),
18  ProfileStandardLevels(options)
19  {}
20 
22  {
23  oops::Log::debug() << " Hydrostatic check" << std::endl;
24 
25  const int numProfileLevels = profileDataHandler.getNumProfileLevels();
26 
27  const std::vector <float> &pressures =
28  profileDataHandler.get<float>(ufo::VariableNames::obs_air_pressure);
29  const std::vector <float> &tObs =
30  profileDataHandler.get<float>(ufo::VariableNames::obs_air_temperature);
31  const std::vector <float> &tBkg =
32  profileDataHandler.get<float>(ufo::VariableNames::hofx_air_temperature);
33  const std::vector <float> &zObs =
34  profileDataHandler.get<float>(ufo::VariableNames::obs_geopotential_height);
35  const std::vector <float> &zBkg =
36  profileDataHandler.get<float>(ufo::VariableNames::hofx_geopotential_height);
37  std::vector <int> &tFlags =
38  profileDataHandler.get<int>(ufo::VariableNames::qcflags_air_temperature);
39  std::vector <int> &zFlags =
41  std::vector <int> &NumAnyErrors =
42  profileDataHandler.get<int>(ufo::VariableNames::counter_NumAnyErrors);
43  std::vector <int> &Num925Miss =
44  profileDataHandler.get<int>(ufo::VariableNames::counter_Num925Miss);
45  std::vector <int> &Num100Miss =
46  profileDataHandler.get<int>(ufo::VariableNames::counter_Num100Miss);
47  std::vector <int> &NumStdMiss =
48  profileDataHandler.get<int>(ufo::VariableNames::counter_NumStdMiss);
49  std::vector <int> &NumHydErrObs =
50  profileDataHandler.get<int>(ufo::VariableNames::counter_NumHydErrObs);
51  std::vector <int> &NumIntHydErrors =
52  profileDataHandler.get<int>(ufo::VariableNames::counter_NumIntHydErrors);
53  const std::vector <float> &tObsCorrection =
54  profileDataHandler.get<float>(ufo::VariableNames::obscorrection_air_temperature);
55  std::vector <float> &zObsCorrection =
57 
58  if (!oops::allVectorsSameNonZeroSize(pressures, tObs, tBkg, zObs, zBkg, tFlags, zFlags,
59  tObsCorrection, zObsCorrection)) {
60  oops::Log::warning() << "At least one vector is the wrong size. "
61  << "Check will not be performed." << std::endl;
62  oops::Log::warning() << "Vector sizes: "
63  << oops::listOfVectorSizes(pressures, tObs, tBkg, zObs, zBkg, tFlags,
64  zFlags, tObsCorrection, zObsCorrection)
65  << std::endl;
66  return;
67  }
68 
69  std::vector <float> tObsFinal;
70  correctVector(tObs, tObsCorrection, tObsFinal);
71 
72  calcStdLevels(numProfileLevels, pressures, tObsFinal, tFlags);
74 
75  HydDesc_ = options_.HydDesc.value();
76  DC_.assign(numProfileLevels, missingValueFloat);
77  ETol_.assign(numProfileLevels, missingValueFloat);
78  D_.assign(numProfileLevels, missingValueFloat);
79  E_.assign(numProfileLevels + 1, missingValueFloat);
80  HydError_.assign(numProfileLevels, 0);
81 
82  int NumErrors = 0;
83  // Find large thickness residuals
84  for (int jlevstd = 1; jlevstd < NumStd_; ++jlevstd) {
85  int jlev = StdLev_[jlevstd]; // Standard level
86  int jlevB = StdLev_[jlevstd - 1]; // Standard level below this one
87  if (zObs[jlev] == missingValueFloat ||
88  zObs[jlevB] == missingValueFloat) continue;
89  if (IndStd_[jlevstd - 1] == -1) { // Surface
90  if (std::fabs(pressures[jlevB] - pressures[jlev]) >
91  options_.HCheck_SurfacePThresh.value()) continue;
92  } else if (IndStd_[jlevstd] == Ind925_ + 1 &&
93  IndStd_[jlevstd - 1] == Ind925_ - 1) { // Missed 925 hPa
94  Num925Miss[0]++;
95  } else if (IndStd_[jlevstd] - IndStd_[jlevstd - 1] != 1) {
96  if (IndStd_[jlevstd - 1] < Ind925_ &&
97  IndStd_[jlevstd] > Ind925_) { // Allow for bigger gaps than two standard levels
98  Num925Miss[0]++;
99  } else if (IndStd_[jlevstd - 1] < Ind100_ &&
100  IndStd_[jlevstd] > Ind100_) { // Missed 100 hPa
101  Num100Miss[0]++;
102  } else { // Missed any other standard level
103  NumStdMiss[0]++;
104  }
105 
106  oops::Log::debug() << " Gap in standard levels" << std::endl;
107  oops::Log::debug() << " -> Level " << jlev << ": "
108  << "P = " << pressures[jlev] * 0.01 << "hPa, tObs = "
109  << tObsFinal[jlev] - ufo::Constants::t0c << "C, "
110  << "tBkg = " << tBkg[jlev] - ufo::Constants::t0c << "C" << std::endl;
111  oops::Log::debug() << " -> Level " << jlevB << ": "
112  << "P = " << pressures[jlevB] * 0.01 << "hPa, tObs = "
113  << tObsFinal[jlevB] - ufo::Constants::t0c << "C, "
114  << "tBkg = " << tBkg[jlevB] - ufo::Constants::t0c
115  << "C" << std::endl;
116  oops::Log::debug() << " -> IndStd[" << jlevstd << "] = "
117  << IndStd_[jlevstd] << ", "
118  << "IndStd[" << jlevstd - 1 << "] = "
119  << IndStd_[jlevstd - 1] << std::endl;
120  continue;
121  }
122 
123  DC_[jlevstd] = 0.5 * ufo::Constants::rd_over_g * (LogP_[jlevB] - LogP_[jlev]);
124  D_[jlevstd] = DC_[jlevstd] *
125  (tObsFinal[jlevB] + tObsFinal[jlev]); // Thickness calculated from temperature
126 
127  // For neutral stability T(jlev) = T(jlevB) * TRatio
128  float TRatio = std::pow(pressures[jlev] / pressures[jlevB], ufo::Constants::rd_over_cp);
129  float DB = DC_[jlevstd] * (1.0 + TRatio) *
130  tObsFinal[jlevB]; // Min thickness given T(jlevB)
131  float DA = DC_[jlevstd] * (1.0 / TRatio + 1.0) *
132  tObsFinal[jlev]; // Max thickness given T(jlev)
133  ETol_[jlevstd] = options_.HCheck_ETolMult.value() * (DA - DB);
134  float ETolMax = options_.HCheck_ETolMax.value();
135  if (pressures[jlevB] <= options_.HCheck_ETolMaxPThresh.value())
136  ETolMax = options_.HCheck_ETolMaxLarger.value();
137  float ETolMin = options_.HCheck_ETolMin.value(); // ETolMin = 20.0 m in GGDPS
138  ETol_[jlevstd] = std::max(std::min(ETol_[jlevstd], ETolMax), ETolMin);
139  E_[jlevstd] = zObs[jlev] - zObs[jlevB] - D_[jlevstd];
140  if (std::fabs(E_[jlevstd]) > ETol_[jlevstd]) {
141  NumErrors++;
142  NumAnyErrors[0]++;
143  HydError_[jlevstd] = 3; // T or Z error
144  } else {
145  HydError_[jlevstd] = 0; // Probably OK
146  if (std::fabs(E_[jlevstd]) <= options_.HCheck_EThresh.value() &&
147  std::fabs(E_[jlevstd - 1]) <= options_.HCheck_EThreshB.value() &&
150  oops::Log::debug() << " -> removed interpolation flag on level " << jlevB << std::endl;
151  }
152  }
153  }
154 
155  // Hydrostatic decision making algorithm
156  if (NumErrors > 0) {
157  NumHydErrObs[0]++;
158 
159  for (int jlevstd = 2; jlevstd < NumStd_; ++jlevstd) {
160  // Check for duplicate std levels
161  if (eckit::types::is_approximately_equal(DC_[jlevstd - 1], 0.0f) ||
162  eckit::types::is_approximately_equal(DC_[jlevstd], 0.0f)) continue;
163  int jlev = StdLev_[jlevstd]; // Standard level
164  int jlevB = StdLev_[jlevstd - 1]; // Standard level below
165  if (HydError_[jlevstd] == 3 || HydError_[jlevstd - 1] == 3) {
166  if (E_[jlevstd] == missingValueFloat) {
167  // Checked previous time as top level
168  continue;
169  }
170  if (E_[jlevstd - 1] == missingValueFloat) {
171  if (E_[jlevstd + 1] == missingValueFloat) {
176  oops::Log::debug() << " -> Isolated large residual on levels "
177  << jlev << " and " << jlevB << std::endl;
178  }
179  continue;
180  }
181  // Possible temperature corrections
182 
183  // These equations are expressed differently to their equivalents in the OPS code.
184  // This avoids a vectorisation problem in the clang compiler:
185  // previously, when E_ was divided by DC_, some (extraneous) machine registers
186  // were filled with zeros prior to the division occurring.
187  // A floating point exception was then thrown due to division by zero even though
188  // the registers in question played no further part in the calculation.
189  float EDC1 = E_[jlevstd - 1] * DC_[jlevstd];
190  float EDC2 = E_[jlevstd] * DC_[jlevstd - 1];
191  float MinAbsEDC = std::min(std::fabs(EDC1), std::fabs(EDC2));
192  float CorrMinThreshDC = options_.HCheck_CorrMinThresh.value()
193  * DC_[jlevstd] * DC_[jlevstd - 1];
194  float AbsEDCDiff = std::fabs(EDC1 - EDC2);
195  float CorrDiffThreshDC = options_.HCheck_CorrDiffThresh.value()
196  * DC_[jlevstd] * DC_[jlevstd - 1];
197 
198  float MinAbsE = std::min(std::fabs(E_[jlevstd - 1]), std::fabs(E_[jlevstd]));
199 
200  float ENext = missingValueFloat;
201  if (jlevstd < NumStd_ - 1) ENext = E_[jlevstd + 1];
202 
203  // Height error
204  if ((std::fabs(E_[jlevstd - 1] + E_[jlevstd]) <=
205  options_.HCheck_ESumThresh.value() &&
206  MinAbsE >= options_.HCheck_MinAbsEThresh.value()) ||
207  (std::fabs(E_[jlevstd - 1] + E_[jlevstd]) <=
209  MinAbsE >= options_.HCheck_MinAbsEThreshLarger.value())) {
211  oops::Log::debug() << " -> Failed hydrostatic check (height error) on level "
212  << jlevB << std::endl;
213  HydError_[jlevstd - 1] = 1;
214  HydError_[jlevstd] = 0;
215  float Corr = 0.5 * (E_[jlevstd] - E_[jlevstd - 1]); // Average of adjacent levels
216  float CorrApp = 100.0 * std::round(Corr / 100.0); // Round to nearest 100 m
217  if (std::fabs(Corr - CorrApp) > options_.HCheck_CorrThresh.value()) CorrApp = 0.0;
218  oops::Log::debug() << " -> P = " << pressures[jlevB] * 0.01
219  << "hPa, zObs = " << zObs[jlevB] << "m, "
220  << "Z Correction? " << Corr << "m"
221  << ", rounded = " << CorrApp << "m" << std::endl;
222  if (CorrApp != 0.0) {
224  if (options_.HCheck_CorrectZ.value()) {
225  zObsCorrection[jlevB] = CorrApp;
226  oops::Log::debug() << " -> Uncorrected zObs: " << zObs[jlevB] << "m" << std::endl;
227  oops::Log::debug() << " zObs correction: " << CorrApp << "m" << std::endl;
228  oops::Log::debug() << " Corrected zObs: "
229  << zObs[jlevB] + zObsCorrection[jlevB] << "m" << std::endl;
230  } else {
231  // Observation is rejected
233  }
234  }
235  // Height errors in two adjacent levels
236  } else if (ENext != missingValueFloat &&
237  std::fabs(E_[jlevstd - 1] + E_[jlevstd] + ENext) <=
239  MinAbsE >= options_.HCheck_MinAbsEThresh.value()) {
242  HydError_[jlevstd - 1] = 1;
243  HydError_[jlevstd] = 1;
244 
245  oops::Log::debug() << " -> Failed hydrostatic check (height error) on levels "
246  << jlevB << " and " << jlev << std::endl;
247 
248  float Corr = -E_[jlevstd - 1];
249  oops::Log::debug() << " -> P = " << pressures[jlevB] * 0.01 << "hPa, zObs = "
250  << zObs[jlevB] << "m, "
251  << "Z Correction? " << Corr << "m" << std::endl;
252  Corr = ENext;
253  oops::Log::debug() << " -> P = " << pressures[jlev] * 0.01 << "hPa, zObs = "
254  << zObs[jlev] << "m, "
255  << "Z Correction? " << Corr << "m" << std::endl;
256 
257  // Temperature error
258  } else if (MinAbsE >= options_.HCheck_MinAbsEThreshT.value() &&
259  AbsEDCDiff <= CorrDiffThreshDC &&
260  MinAbsEDC >= CorrMinThreshDC) {
262  HydError_[jlevstd - 1] = 2;
263  HydError_[jlevstd] = 0;
264 
265  oops::Log::debug() << " -> Failed hydrostatic check (temperature error) on level "
266  << jlevB << std::endl;
267 
268  // Potential T correction
269  float Corr1 = E_.at(jlevstd - 1) / DC_.at(jlevstd - 1);
270  float Corr2 = E_.at(jlevstd) / DC_.at(jlevstd);
271  float Corr = 0.5 * (Corr1 + Corr2);
272  oops::Log::debug() << " -> P = " << pressures[jlevB] * 0.01 << "hPa, tObs = "
273  << tObsFinal[jlevB] - ufo::Constants::t0c << "C, "
274  << "T Correction? " << Corr << "C, "
275  << " Corr1, Corr2 = "
276  << Corr1 << "C, " << Corr2 << "C , DC[" << jlevstd - 1
277  << "], DC[" << jlevstd << "] = "
278  << DC_[jlevstd - 1] << ", " << DC_[jlevstd]
279  << std::endl;
280 
282  int SigB = SigBelow_[jlevstd - 1];
283  int SigA = SigAbove_[jlevstd - 1];
284 
287 
288  NumIntHydErrors[0]++;
289  oops::Log::debug() << " -> Hyd: remove interpolation flags on levels "
290  << SigB << " " << SigA << std::endl;
291  }
292 
293  // Bottom level error in T or Z, usually jlevstd = 3
294  } else if (HydError_[jlevstd - 1] == 3 && HydError_[jlevstd] == 0) {
295  if (E_[jlevstd - 2] == missingValueFloat) {
296  int L1 = StdLev_[jlevstd - 2];
297 
300 
301  oops::Log::debug() << " -> Failed hydrostatic check "
302  << "(bottom level error in T or Z) on level " << L1 << std::endl;
303 
304  HydError_[jlevstd - 2] = 4;
305  HydError_[jlevstd - 1] = 0;
306 
308  oops::Log::debug() << " -> Baseline error for level " << L1
309  << "? P = " << pressures[L1] * 0.01 << "hPa, zObs = "
310  << zObs[L1] << "m, zBkg = " << zBkg[L1]
311  << ", zObs + E = "
312  << zObs[L1] + E_[jlevstd - 1] << "m" << std::endl;
313  }
314  } else {
315  // Error in all subsequent heights?
316  HydError_[jlevstd - 1] = 6;
318 
319  oops::Log::debug() << " -> Failed hydrostatic check "
320  << "(error in all subsequent heights) on level "
321  << jlevB << std::endl;
322  }
323  } else if (HydError_[jlevstd - 1] == 3) { // T and/or Z error
326 
327  oops::Log::debug() << " -> Failed hydrostatic check "
328  << "(T and/or Z error) on level " << jlevB << std::endl;
329  }
330 
331  // Top level error in T or Z
332  if (HydError_[jlevstd] == 3 && E_[jlevstd + 1] == missingValueFloat) {
335  HydError_[jlevstd] = 5;
336 
337  oops::Log::debug() << " -> Failed hydrostatic check "
338  << "(top level error in T or Z) on level " << jlev << std::endl;
339  }
340  }
341  }
342 
343  for (int jlevstd = 0; jlevstd < NumStd_; ++jlevstd) {
344  int HydType = HydError_[jlevstd];
345  int jlev = StdLev_[jlevstd]; // Standard level
346 
347  oops::Log::debug() << " -> Level " << jlev << ": "
348  << "P = " << pressures[jlev] * 0.01 << "hPa, tObs = "
349  << tObsFinal[jlev] - ufo::Constants::t0c << "C, "
350  << "tBkg = " << tBkg[jlev] - ufo::Constants::t0c << "C, "
351  << "zObs = " << zObs[jlev] << "m, zBkg = " << zBkg[jlev] << "m, "
352  << "D = " << D_[jlevstd] << ", E = " << E_[jlevstd]
353  << ", ETol = " << ETol_[jlevstd] << ", DC = " << DC_[jlevstd]
354  << ", HydDesc = " << HydDesc_[HydType] << " " << std::endl;
355  }
356  }
357  }
358 
360  {
361  profileDataHandler.set(ufo::VariableNames::DC, std::move(DC_));
362  profileDataHandler.set(ufo::VariableNames::ETol, std::move(ETol_));
363  profileDataHandler.set(ufo::VariableNames::D, std::move(D_));
364  profileDataHandler.set(ufo::VariableNames::E, std::move(E_));
365  profileDataHandler.set(ufo::VariableNames::HydError, std::move(HydError_));
366  }
367 } // namespace ufo
368 
Options controlling the operation of the ConventionalProfileProcessing filter.
oops::Parameter< bool > HCheck_CorrectZ
Correct zObs in the hydrostatic check?
oops::Parameter< float > HCheck_SurfacePThresh
Surface P threshold for hydrostatic check (Pa)
oops::Parameter< std::vector< std::string > > HydDesc
Hydrostatic error descriptions.
Profile QC checker base class.
const float missingValueFloat
Missing value (float)
const ConventionalProfileProcessingParameters & options_
Configurable parameters.
void correctVector(const std::vector< T > &v1, const std::vector< T > &v2, std::vector< T > &vout)
Apply correction to vector of values.
std::vector< std::string > HydDesc_
Hydrostatic error descriptions.
std::vector< float > ETol_
Thickness tolerance.
std::vector< float > DC_
Constant in thickness calculation.
ProfileCheckHydrostatic(const ConventionalProfileProcessingParameters &options)
void fillValidationData(ProfileDataHandler &profileDataHandler) override
Fill variables in validator.
std::vector< int > HydError_
Hydrostatic flag by level.
std::vector< float > E_
Thickness 'error'.
std::vector< float > D_
Thickness calculated from temepature.
void runCheck(ProfileDataHandler &profileDataHandler) override
Run check.
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)
void set(const std::string &fullname, std::vector< T > &&vec_in)
int getNumProfileLevels() const
Return number of levels to which QC checks should be applied.
Calculate standard levels.
int Ind925_
Standard level index closest to 925 hPa.
int Ind100_
Standard level index closest to 100 hPa.
std::vector< int > StdLev_
Index of standard levels.
void findHCheckStdLevs()
Compute indices of particular standard levels for the hydrostatic check.
void calcStdLevels(const int numProfileLevels, const std::vector< float > &pressures, const std::vector< float > &tObs, const std::vector< int > &tFlags)
Calculate standard levels.
std::vector< float > LogP_
Log(Pressure) - used for vertical interpolation.
int NumStd_
Number of standard levels.
std::vector< int > IndStd_
Indices of standard levels.
std::vector< int > SigBelow_
Significant level below standard level.
std::vector< int > SigAbove_
Significant level above standard level.
@ InterpolationFlag
Interpolation check flag.
@ HydrostaticFlag
Hydrostatic check flag.
@ SurfaceLevelFlag
Surface Level.
@ FinalRejectFlag
Final QC flag.
@ DataCorrectFlag
Eg sign correction.
Definition: RunCRTM.h:27
static ProfileCheckMaker< ProfileCheckHydrostatic > makerProfileCheckHydrostatic_("Hydrostatic")
static constexpr double rd_over_cp
Definition: Constants.h:35
static constexpr double rd_over_g
Definition: Constants.h:38
static constexpr double t0c
Definition: Constants.h:24
static constexpr const char *const ETol
static constexpr const char *const D
static constexpr const char *const hofx_air_temperature
Definition: VariableNames.h:38
static constexpr const char *const obs_air_temperature
Definition: VariableNames.h:19
static constexpr const char *const counter_NumAnyErrors
static constexpr const char *const counter_NumHydErrObs
static constexpr const char *const E
static constexpr const char *const HydError
static constexpr const char *const obs_geopotential_height
Definition: VariableNames.h:23
static constexpr const char *const DC
static constexpr const char *const counter_NumStdMiss
static constexpr const char *const hofx_geopotential_height
Definition: VariableNames.h:39
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 obscorrection_air_temperature
static constexpr const char *const counter_Num925Miss
static constexpr const char *const counter_NumIntHydErrors
static constexpr const char *const obscorrection_geopotential_height
static constexpr const char *const qcflags_geopotential_height