UFO
ImpactHeightCheck.cc
Go to the documentation of this file.
1 /*
2  * (C) British Crown Copyright 2021 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 
9 
10 #include <Eigen/Core>
11 #include <iomanip>
12 #include <iostream>
13 #include <limits>
14 #include <vector>
15 
16 
17 #include "ioda/ObsDataVector.h"
18 #include "ioda/ObsSpace.h"
19 
20 #include "oops/util/Logger.h"
21 #include "ufo/filters/QCflags.h"
22 
23 namespace ufo {
24 
25 // -----------------------------------------------------------------------------
26 /// ImpactHeightCheck: Calculate the impact height for model profiles, and reject
27 /// any observations which are outside the range of model impact heights. Check
28 /// for any sharp refractivity gradients, and reject any observations below them.
29 
31  ioda::ObsSpace & obsdb,
32  const Parameters_ & parameters,
33  std::shared_ptr<ioda::ObsDataVector<int> > flags,
34  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
35  : FilterBase(obsdb, parameters, flags, obserr), parameters_(parameters)
36 {
37  oops::Log::trace() << "ImpactHeightCheck constructor" << std::endl;
38  allvars_ += Variable("refractivity@ObsDiag");
39  allvars_ += Variable("model_heights@ObsDiag");
40  allvars_ += Variable("impact_parameter@MetaData");
41  allvars_ += Variable("earth_radius_of_curvature@MetaData");
42 }
43 
44 // -----------------------------------------------------------------------------
45 
47  oops::Log::trace() << "ImpactHeightCheck destructed" << std::endl;
48 }
49 
50 // -----------------------------------------------------------------------------
51 /// Apply the filter: remove observations which are outside the range of
52 /// impact heights
53 
54 void ImpactHeightCheck::applyFilter(const std::vector<bool> & apply,
55  const Variables & filtervars,
56  std::vector<std::vector<bool>> & flagged) const {
57  oops::Log::trace() << "ImpactHeightCheck post-filter" << std::endl;
58  const oops::Variables observed = obsdb_.obsvariables();
59  const float missingFloat = util::missingValue(missingFloat);
60 
61  // Get the refractivity from the obs diagnostics, including the number of
62  // vertical levels on which the refractivity has been calculated (nRefLevels)
63  Variable refractivityVariable = Variable("refractivity@ObsDiag");
64  oops::Log::debug() << data_.nlevs(refractivityVariable) << std::endl;
65  const size_t nRefLevels = data_.nlevs(refractivityVariable);
66  std::vector<std::vector<float>> refractivity;
67 
68  for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) {
69  std::vector<float> inputData;
70  data_.get(refractivityVariable, static_cast<int>(iLevel), inputData);
71  refractivity.push_back(inputData);
72  }
73 
74  // For the benefits of debugging, output the refractivity for the first
75  // observation
76  oops::Log::debug() << "Refractivity(first ob) ";
77  for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) {
78  oops::Log::debug() << refractivity[iLevel][0] << " ";
79  }
80  oops::Log::debug() << std::endl;
81 
82  // Get the height of the levels on which the refractivity has been calculated.
83  // Must be the same length as the array defining the refractivity.
84  Variable modelHeightsVariable = Variable("model_heights@ObsDiag");
85  oops::Log::debug() << data_.nlevs(modelHeightsVariable) << std::endl;
86  if (data_.nlevs(modelHeightsVariable) != nRefLevels) {
87  throw eckit::BadValue("Model heights and refractivity must have the same number of levels",
88  Here());
89  }
90  std::vector<std::vector<float>> modelHeights;
91 
92  // Read the heights of the refractivity levels
93  for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) {
94  std::vector<float> inputData;
95  data_.get(modelHeightsVariable, static_cast<int>(iLevel), inputData);
96  modelHeights.push_back(inputData);
97  }
98 
99  // For debugging, output the heights of the refractivity levels for the first
100  // observation.
101  oops::Log::debug() << "Model heights (first ob) ";
102  for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel) {
103  oops::Log::debug() << modelHeights[iLevel][0] << " ";
104  }
105  oops::Log::debug() << std::endl;
106 
107  // Read in the observation impact parameter for each observation
108  Variable impactVariable = Variable("impact_parameter@MetaData");
109  std::vector<float> impactParameter;
110  data_.get(impactVariable, impactParameter);
111 
112  // Read in the earth's radius of curvature for each observation
113  Variable radiusCurvatureParameter = Variable("earth_radius_of_curvature@MetaData");
114  std::vector<float> radiusCurvature;
115  data_.get(radiusCurvatureParameter, radiusCurvature);
116 
117  // For each variable, perform the filter
118  for (size_t iFilterVar = 0; iFilterVar < filtervars.nvars(); ++iFilterVar) {
119  const size_t iVar = observed.find(filtervars.variable(iFilterVar).variable());
120 
121  // Loop over the observations
122  for (size_t iObs = 0; iObs < obsdb_.nlocs(); ++iObs) {
123  if (apply[iObs] && (*flags_)[iVar][iObs] == QCflags::pass) {
124  // Load the refractivity profile and the associated model heights for this observation,
125  // cleansing any missing data
126  std::vector<float> refracProfile;
127  std::vector<float> heightProfile;
128  for (size_t iLevel = 0; iLevel < nRefLevels; ++iLevel)
129  if (refractivity[iLevel][iObs] != missingFloat &&
130  modelHeights[iLevel][iObs] != missingFloat) {
131  refracProfile.push_back(refractivity[iLevel][iObs]);
132  heightProfile.push_back(modelHeights[iLevel][iObs]);
133  }
134 
135  if (refracProfile.size() < 2) {
136  oops::Log::error() << "Should have at least two valid points in every profile:" <<
137  std::endl << "size = " << refracProfile.size() << " " <<
138  "iObs = " << iObs << std::endl;
139  flagged[iFilterVar][iObs] = true;
140  continue;
141  }
142 
143  oops::Log::debug() << "Min and max refrac for profile " << iObs <<
144  " is " << refracProfile[0] << " to " << refracProfile.back() <<
145  std::endl;
146  oops::Log::debug() << "Impact height " << impactParameter[iObs] - radiusCurvature[iObs] <<
147  std::endl;
148 
149  // Output the calculated refractivity gradient for the first observation
150  const std::vector<float> & gradient = calcVerticalGradient(refracProfile, heightProfile);
151  if (iObs == 0) {
152  oops::Log::debug() << "Gradient found to be" << std::endl;
153  for (float grad : gradient)
154  oops::Log::debug() << grad << " ";
155  oops::Log::debug() << std::endl;
156  }
157 
158  float sharpGradientImpact = std::numeric_limits<float>::lowest();
159  // Search for sharp gradients (super-refraction) starting at the top of the profile
160  for (int iLevel = static_cast<int>(nRefLevels)-2; iLevel >= 0; --iLevel) {
161  size_t thisLevel = static_cast<size_t>(iLevel);
162  if (gradient[thisLevel] != missingFloat &&
163  gradient[thisLevel] < parameters_.gradientThreshold.value()) {
164  sharpGradientImpact = calcImpactHeight(refracProfile[thisLevel],
165  heightProfile[thisLevel],
166  radiusCurvature[iObs]);
167  oops::Log::info() << "Sharp refractivity gradient of " << gradient[thisLevel] <<
168  " found at " << thisLevel << " " << sharpGradientImpact <<
169  std::endl;
170  oops::Log::debug() << thisLevel << " " << refracProfile[thisLevel] << " " <<
171  heightProfile[thisLevel] << " " <<
172  radiusCurvature[thisLevel] << std::endl;
173  break;
174  }
175  }
176 
177  // Reject observation if it is below the minimum (either surface or sharp gradient)
178  const float obsImpactHeight = impactParameter[iObs] - radiusCurvature[iObs];
179  oops::Log::debug() << "Checking minimum height " << obsImpactHeight << " " <<
180  sharpGradientImpact + parameters_.sharpGradientOffset.value() <<
181  " " << calcImpactHeight(refracProfile[0], heightProfile[0],
182  radiusCurvature[iObs]) +
183  parameters_.surfaceOffset.value() << std::endl;
184  if (obsImpactHeight < sharpGradientImpact + parameters_.sharpGradientOffset.value() ||
185  obsImpactHeight < calcImpactHeight(refracProfile[0], heightProfile[0],
186  radiusCurvature[iObs]) +
187  parameters_.surfaceOffset.value())
188  flagged[iFilterVar][iObs] = true;
189 
190  // Reject observation if it is above the maximum
191  oops::Log::debug() << "Checking maximum height " << obsImpactHeight << " " <<
192  calcImpactHeight(refracProfile.back(), heightProfile.back(),
193  radiusCurvature[iObs]) << std::endl;
194  if (obsImpactHeight > calcImpactHeight(refracProfile.back(), heightProfile.back(),
195  radiusCurvature[iObs]))
196  flagged[iFilterVar][iObs] = true;
197  }
198  }
199  }
200 }
201 
202 // -----------------------------------------------------------------------------
203 /// Calculate the vertical gradient of refractivity, assuming that any missing
204 /// data has already been removed
206  const std::vector<float> & refrac,
207  const std::vector<float> & height) const {
208  std::vector<float> gradient;
209  for (size_t iLevel = 0; iLevel < refrac.size()-1; ++iLevel) {
210  gradient.push_back((refrac[iLevel+1] - refrac[iLevel]) / (height[iLevel+1] - height[iLevel]));
211  }
212  return gradient;
213 }
214 
215 // -----------------------------------------------------------------------------
216 /// Calculate the impact parameter from the refractivity, impact height and
217 /// radius of curvature
218 float ImpactHeightCheck::calcImpactHeight(float refractivity,
219  float modelHeight,
220  float radiusCurv) const {
221  return 1.0E-6f * refractivity * (radiusCurv + modelHeight) + modelHeight;
222 }
223 
224 
225 // -----------------------------------------------------------------------------
226 void ImpactHeightCheck::print(std::ostream & os) const {
227  os << "ImpactHeightCheck: config = " << parameters_ << std::endl;
228 }
229 
230 // -----------------------------------------------------------------------------
231 
232 } // namespace ufo
Base class for UFO QC filters.
Definition: FilterBase.h:45
float calcImpactHeight(float, float, float) const
std::vector< float > calcVerticalGradient(const std::vector< float > &, const std::vector< float > &) const
ImpactHeightCheck(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
void print(std::ostream &) const override
Parameters controlling the operation of the ImpactHeightCheck filter.
oops::Parameter< float > sharpGradientOffset
oops::Parameter< float > surfaceOffset
Reject data within this height (in m) of the surface.
oops::Parameter< float > gradientThreshold
size_t nlevs(const Variable &) const
Returns the number of levels in the specified variable.
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
ufo::Variables allvars_
ioda::ObsSpace & obsdb_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
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
constexpr int pass
Definition: QCflags.h:14
Definition: RunCRTM.h:27