16 #include "eckit/config/Configuration.h"
17 #include "ioda/ObsDataVector.h"
18 #include "ioda/ObsSpace.h"
19 #include "oops/base/Variables.h"
20 #include "oops/util/DateTime.h"
21 #include "oops/util/Duration.h"
22 #include "oops/util/Logger.h"
23 #include "oops/util/missingValues.h"
44 :
FilterBase(obsdb, params, flags, obserr), options_(params)
53 std::vector<std::vector<bool>> & flagged)
const {
62 metOfficeSort(validObsIds.begin(), validObsIds.end(), [&lat] (
size_t id) { return lat[id]; });
65 std::vector<float> distancesToBinCenter(validObsIds.size(), 0.f);
71 splitter, distancesToBinCenter);
73 splitter, distancesToBinCenter);
75 splitter, distancesToBinCenter);
78 validObsIds, obsAccessor, splitter, distancesToBinCenter);
100 switch (distanceNorm) {
106 throw eckit::BadParameter(
"Unrecognized distance norm", Here());
112 const std::vector<size_t> &validObsIds,
116 std::vector<float> &distancesToBinCenter)
const {
118 if (binSelector == boost::none)
122 << binSelector->latitudeBinWidth() << std::endl;
124 << binSelector->totalNumBins() << std::endl;
133 for (
float &longitude : lon)
138 std::vector<int> latBins;
139 std::vector<int> lonBins;
140 latBins.reserve(validObsIds.size());
141 lonBins.reserve(validObsIds.size());
142 for (
size_t obsId : validObsIds) {
143 const size_t latBin = binSelector->latitudeBin(lat[obsId]);
144 latBins.push_back(latBin);
145 lonBins.push_back(binSelector->longitudeBin(latBin, lon[obsId]));
150 for (
size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) {
151 const size_t obsId = validObsIds[validObsIndex];
153 lat[obsId], lon[obsId],
154 binSelector->latitudeBinCenter(latBins[validObsIndex]),
155 binSelector->longitudeBinCenter(latBins[validObsIndex], lonBins[validObsIndex]),
156 binSelector->inverseLatitudeBinWidth(),
157 binSelector->inverseLongitudeBinWidth(latBins[validObsIndex]));
159 distancesToBinCenter[validObsIndex], component);
170 oops::Log::debug() <<
"Gaussian_Thinning: requested horizontal bin size (km) = "
173 bool roundHorizontalBinCountToNearest =
176 roundHorizontalBinCountToNearest =
true;
181 const float meridianLength = M_PI * earthRadius;
182 const float tentativeNumLatBins = meridianLength / options.
horizontalMesh;
190 const int equatorToMeridianLengthRatio = 2;
199 const std::vector<size_t> &validObsIds,
203 std::vector<float> &distancesToBinCenter)
const {
208 if (binSelector->numBins() != boost::none)
210 << *binSelector->numBins() << std::endl;
215 std::vector<int> bins;
216 bins.reserve(validObsIds.size());
217 for (
size_t obsId : validObsIds)
219 bins.push_back(binSelector->bin(vcoord[obsId]));
223 for (
size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) {
224 const size_t obsId = validObsIds[validObsIndex];
226 vcoord[obsId], binSelector->binCenter(bins[validObsIndex]),
227 binSelector->inverseBinWidth());
229 distancesToBinCenter[validObsIndex], component);
241 return std::make_unique<RoundingEquispacedBinSelector>(
244 const int numVerticalBins = std::max(
253 << numVerticalBins << std::endl;
254 return std::make_unique<TruncatingEquispacedBinSelector>(
255 options.
verticalMin, adjustedVerticalMax, numVerticalBins);
262 const std::vector<size_t> &validObsIds,
266 std::vector<float> &distancesToBinCenter)
const {
267 util::DateTime timeOffset;
268 std::unique_ptr<EquispacedBinSelectorBase> binSelector =
273 if (binSelector->numBins() != boost::none)
275 << *binSelector->numBins() << std::endl;
278 "MetaData",
"datetime");
280 std::vector<int> bins;
281 bins.reserve(validObsIds.size());
282 for (
size_t obsId : validObsIds)
284 bins.push_back(binSelector->bin((times[obsId] - timeOffset).toSeconds()));
288 for (
size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) {
289 const size_t obsId = validObsIds[validObsIndex];
291 (times[obsId] - timeOffset).toSeconds(),
292 binSelector->binCenter(bins[validObsIndex]),
293 binSelector->inverseBinWidth());
295 distancesToBinCenter[validObsIndex], component);
303 const util::DateTime &windowStart,
304 const util::DateTime &windowEnd,
305 util::DateTime &timeOffset) {
306 if (options.
timeMesh.value() == boost::none ||
307 options.
timeMin.value() == boost::none ||
308 options.
timeMax.value() == boost::none)
311 const util::Duration timeMesh = options.
timeMesh.value().get();
312 if (timeMesh.toSeconds() == 0)
315 const util::DateTime timeMin = options.
timeMin.value().get();
316 const util::DateTime timeMax = options.
timeMax.value().get();
319 << ((timeMax - timeMin).toSeconds()) << std::endl;
320 oops::Log::debug() <<
"timeMesh.toSeconds() = " << timeMesh.toSeconds() << std::endl;
322 timeOffset = timeMin;
326 const util::Duration windowLength = windowEnd - windowStart;
327 const int numFullBinsLeftOfWindowCenter = (windowLength / 2).toSeconds() / timeMesh.toSeconds();
328 const util::DateTime bin0Center = timeMin + numFullBinsLeftOfWindowCenter * timeMesh +
330 return std::make_unique<RoundingEquispacedBinSelector>(
331 timeMesh.toSeconds(), (bin0Center - timeOffset).toSeconds());
335 const int numTimeBins = std::max(
337 static_cast<int>(std::ceil((timeMax - timeMin).toSeconds() /
338 static_cast<float>(timeMesh.toSeconds()))));
340 return std::make_unique<TruncatingEquispacedBinSelector>(
341 0.0f, numTimeBins * timeMesh.toSeconds(), numTimeBins);
348 const std::vector<size_t> &validObsIds,
351 const std::vector<float> &distancesToBinCenter)
const {
354 validObsIds, distancesToBinCenter, obsAccessor);
358 std::vector<bool> isThinned(totalNumObs,
false);
360 const size_t bestValidObsIndex = *std::min_element(
361 std::begin(group), std::end(group), comparator);
363 for (
size_t validObsIndex : group)
364 if (validObsIndex != bestValidObsIndex)
365 isThinned[validObsIds[validObsIndex]] =
true;
373 const std::vector<size_t> &validObsIds,
374 const std::vector<float> &distancesToBinCenter,
378 return [&distancesToBinCenter](
size_t validObsIndexA,
size_t validObsIndexB) {
379 return distancesToBinCenter[validObsIndexA] < distancesToBinCenter[validObsIndexB];
389 return [priorities, &validObsIds, &distancesToBinCenter]
390 (
size_t validObsIndexA,
size_t validObsIndexB) {
392 return std::make_pair(-priorities[validObsIds[validObsIndexA]],
393 distancesToBinCenter[validObsIndexA]) <
394 std::make_pair(-priorities[validObsIds[validObsIndexB]],
395 distancesToBinCenter[validObsIndexB]);
402 os <<
"Gaussian_Thinning: config = " <<
options_ << std::endl;
Calculates distances between observations and centres of bins used during thinning.
virtual float nonspatialDistanceComponent(float obs, float binCenter, float inverseBinWidth) const =0
virtual float spatialDistanceComponent(float obsLatitude, float obsLongitude, float latitudeBinCenter, float longitudeBinCenter, float inverseLatitudeBinWidth, float inverseLongitudeBinWidth) const =0
virtual float combineDistanceComponents(float componentA, float componentB) const =0
Base class for UFO QC filters.
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
GaussianThinningParameters options_
static boost::optional< SpatialBinSelector > makeSpatialBinSelector(const GaussianThinningParameters &options)
void print(std::ostream &) const override
ObsAccessor createObsAccessor() const
void groupObservationsByTime(const std::vector< size_t > &validObsIds, const DistanceCalculator &distanceCalculator, const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector< float > &distancesToBinCenter) const
void groupObservationsBySpatialLocation(const std::vector< size_t > &validObsIds, const DistanceCalculator &distanceCalculator, const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector< float > &distancesToBinCenter) const
std::vector< bool > identifyThinnedObservations(const std::vector< size_t > &validObsIds, const ObsAccessor &obsAccessor, const RecursiveSplitter &splitter, const std::vector< float > &distancesToBinCenter) const
Gaussian_Thinning(ioda::ObsSpace &obsdb, const GaussianThinningParameters ¶ms, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
static std::unique_ptr< EquispacedBinSelectorBase > makeTimeBinSelector(const GaussianThinningParameters &options, const util::DateTime &windowStart, const util::DateTime &windowEnd, util::DateTime &timeOffset)
void groupObservationsByVerticalCoordinate(const std::vector< size_t > &validObsIds, const DistanceCalculator &distanceCalculator, const ObsAccessor &obsAccessor, RecursiveSplitter &splitter, std::vector< float > &distancesToBinCenter) const
std::function< bool(size_t, size_t)> makeObservationComparator(const std::vector< size_t > &validObsIds, const std::vector< float > &distancesToBinCenter, const ObsAccessor &obsAccessor) const
static std::unique_ptr< EquispacedBinSelectorBase > makeVerticalBinSelector(const GaussianThinningParameters &options)
static std::unique_ptr< DistanceCalculator > makeDistanceCalculator(const GaussianThinningParameters &options)
Options controlling the operation of the Gaussian_Thinning filter.
oops::Parameter< bool > opsCompatibilityMode
oops::OptionalParameter< DistanceNorm > distanceNorm
oops::Parameter< float > verticalMesh
oops::Parameter< float > horizontalMesh
oops::Parameter< std::string > verticalCoord
Observation vertical coordinate.
oops::OptionalParameter< Variable > priorityVariable
oops::OptionalParameter< util::DateTime > timeMin
oops::Parameter< bool > useReducedHorizontalGrid
oops::Parameter< bool > thinIfAnyFilterVariablesAreValid
oops::Parameter< float > verticalMin
Lower bound of the vertical coordinate interval split into cells of size vertical_mesh.
oops::Parameter< float > verticalMax
oops::OptionalParameter< Variable > categoryVariable
oops::OptionalParameter< util::Duration > timeMesh
oops::OptionalParameter< bool > roundHorizontalBinCountToNearest
oops::OptionalParameter< util::DateTime > timeMax
This class provides access to observations that may be held on multiple MPI ranks.
RecursiveSplitter splitObservationsIntoIndependentGroups(const std::vector< size_t > &validObsIds, bool opsCompatibilityMode=false) const
std::vector< size_t > getValidObservationIds(const std::vector< bool > &apply, const ioda::ObsDataVector< int > &flags, const Variables &filtervars, bool validIfAnyFilterVariablePassedQC=true) const
Return the IDs of observation locations that should be treated as valid by a filter.
std::vector< int > getIntVariableFromObsSpace(const std::string &group, const std::string &variable) const
Return the values of the specified variable at successive observation locations.
static ObsAccessor toAllObservations(const ioda::ObsSpace &obsdb)
Create an accessor to observations from the observation space obsdb, assuming that the whole set of o...
static ObsAccessor toObservationsSplitIntoIndependentGroupsByVariable(const ioda::ObsSpace &obsdb, const Variable &variable)
Create an accessor to the collection of observations held in obsdb, assuming that observations with d...
std::vector< float > getFloatVariableFromObsSpace(const std::string &group, const std::string &variable) const
std::vector< util::DateTime > getDateTimeVariableFromObsSpace(const std::string &group, const std::string &variable) const
void flagRejectedObservations(const std::vector< bool > &isRejected, std::vector< std::vector< bool > > &flagged) const
Update flags of observations held on the current MPI rank.
size_t totalNumObservations() const
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Partitions an array into groups of elements equivalent according to certain criteria.
void groupBy(const std::vector< size_t > &categories)
Split existing equivalence classes according to a new criterion.
MultiElementGroupRange multiElementGroups() const
Return the range of equivalence classes consisting of more than one element.
Represents a partition of a sphere into a number of subsets (bins).
static IndexType roundNumBins(float idealNumBins, SpatialBinCountRoundingMode roundingMode)
Return idealNumBins rounded to a positive integer according to the rounding strategy roundingMode.
const std::string & variable() const
const std::string & group() const
void metOfficeSort(RandomIt first, RandomIt last, const UnaryOperation &key)
Sort the range [first, last) in the order of ascending keys using the same algorithm as the Ops_Integ...
SpatialBinCountRoundingMode
static constexpr double mean_earth_rad