UFO
PoissonDiskThinning.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2019 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 <algorithm>
11 #include <cmath>
12 #include <limits>
13 #include <string>
14 #include <utility>
15 #include <vector>
16 
17 #include "eckit/config/Configuration.h"
18 #include "eckit/container/KDTree.h"
19 #include "ioda/ObsDataVector.h"
20 #include "ioda/ObsSpace.h"
21 #include "oops/base/Variables.h"
22 #include "oops/util/DateTime.h"
23 #include "oops/util/Duration.h"
24 #include "oops/util/IsAnyPointInVolumeInterior.h"
25 #include "oops/util/Logger.h"
28 #include "ufo/utils/Constants.h"
30 
31 namespace ufo {
32 
33 namespace {
34 
35 /// \brief Abstract interface of a container storing point sets and able to answer spatial queries
36 /// needed by PoissonDiskThinning ("does any point lie in the interior of an axis-aligned
37 /// ellipsoid/cylinder?").
38 template <int numDims_>
39 class PointIndex {
40  public:
41  typedef float CoordType;
42  static const int numDims = numDims_;
43 
44  typedef std::array<CoordType, numDims> Point;
45  typedef std::array<CoordType, numDims> Extent;
46 
47  virtual ~PointIndex() {}
48 
49  virtual void insert(const Point &point) = 0;
50 
51  virtual bool isAnyPointInCylinderInterior(const Point &center,
52  const Extent &semiAxes,
53  int numSpatialDims) const = 0;
54 
55  virtual bool isAnyPointInEllipsoidInterior(const Point &center,
56  const Extent &semiAxes) const = 0;
57 };
58 
59 /// \brief An implementation of PointIndex storing the point set in a kd-tree.
60 template <int numDims_>
61 class KDTree : public PointIndex<numDims_> {
62  public:
64 
65  typedef typename Base::CoordType CoordType;
66  typedef typename Base::Point Point;
67  typedef typename Base::Extent Extent;
68 
69  static const int numDims = Base::numDims;
70 
71  void insert(const Point &point) override;
72 
73  bool isAnyPointInCylinderInterior(const Point &center,
74  const Extent &semiAxes,
75  int numSpatialDims) const override;
76 
77  bool isAnyPointInEllipsoidInterior(const Point &center,
78  const Extent &semiAxes) const override;
79 
80  private:
81  struct EmptyPayload {};
82 
83  struct TreeTraits {
84  typedef eckit::geometry::KPoint<numDims> Point;
86  };
87 
88  typedef eckit::KDTreeMemory<TreeTraits> KDTreeImpl;
89  typedef typename KDTreeImpl::Alloc Alloc;
90  typedef typename KDTreeImpl::Node Node;
91  typedef typename KDTreeImpl::Point KPoint;
92  typedef typename KDTreeImpl::Value Value;
93 
95 };
96 
97 template <int numDims_>
99  const Point &point) {
100  tree_.insert(Value(KPoint(point), EmptyPayload()));
101 }
102 
103 template <int numDims_>
105  const Point &center, const Extent &semiAxes) const {
106  KPoint lbound, ubound;
107  for (int d = 0; d < numDims; ++d) {
108  lbound.data()[d] = center[d] - semiAxes[d];
109  ubound.data()[d] = center[d] + semiAxes[d];
110  }
111  return util::isAnyPointInEllipsoidInterior(tree_, lbound, ubound);
112 }
113 
114 template <int numDims_>
116  const Point &center, const Extent &semiAxes, int numSpatialDims) const {
117  KPoint lbound, ubound;
118  for (int d = 0; d < numDims; ++d) {
119  lbound.data()[d] = center[d] - semiAxes[d];
120  ubound.data()[d] = center[d] + semiAxes[d];
121  }
122  return util::isAnyPointInCylinderInterior(tree_, lbound, ubound, numSpatialDims);
123 }
124 
125 } // namespace
126 
128 {
129  boost::optional<util::ScalarOrMap<int, float>> minHorizontalSpacings;
130  boost::optional<std::vector<float>> latitudes;
131  boost::optional<std::vector<float>> longitudes;
132 
133  boost::optional<util::ScalarOrMap<int, float>> minVerticalSpacings;
134  boost::optional<std::vector<float>> pressures;
135 
136  boost::optional<util::ScalarOrMap<int, util::Duration>> minTimeSpacings;
137  boost::optional<std::vector<util::DateTime>> times;
138 
139  boost::optional<std::vector<int>> priorities;
140 };
141 
143  const eckit::Configuration & config,
144  std::shared_ptr<ioda::ObsDataVector<int> > flags,
145  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
146  : FilterBase(obsdb, config, flags, obserr)
147 {
148  oops::Log::debug() << "PoissonDiskThinning: config = " << config_ << std::endl;
149 
151  options_->deserialize(config);
152 }
153 
154 // Required for the correct destruction of options_.
156 {}
157 
158 void PoissonDiskThinning::applyFilter(const std::vector<bool> & apply,
159  const Variables & filtervars,
160  std::vector<std::vector<bool>> & flagged) const {
161  const std::vector<size_t> validObsIds = getValidObservationIds(apply);
162 
163  if (validObsIds.empty()) {
164  return;
165  }
166 
167  int numSpatialDims, numNonspatialDims;
168  ObsData obsData = getObsData(numSpatialDims, numNonspatialDims);
169 
170  std::vector<bool> isThinned(apply.size(), false);
171 
172  // Thin points from each category separately.
173  RecursiveSplitter categorySplitter(validObsIds.size());
174  groupObservationsByCategory(validObsIds, categorySplitter);
175  for (auto categoryGroup : categorySplitter.multiElementGroups()) {
176  std::vector<size_t> obsIdsInCategory;
177  for (size_t validObsIndex : categoryGroup) {
178  obsIdsInCategory.push_back(validObsIds[validObsIndex]);
179  }
180 
181  // Within each category, sort points by descending priority and then (if requested)
182  // randomly shuffle points of equal priority.
183  RecursiveSplitter prioritySplitter(obsIdsInCategory.size());
184  groupObservationsByPriority(obsIdsInCategory, prioritySplitter);
185  if (options_->shuffle) {
186  if (options_->randomSeed.value() != boost::none)
187  prioritySplitter.shuffleGroups(*options_->randomSeed.value());
188  else
189  prioritySplitter.shuffleGroups();
190  }
191 
192  // Select points to retain within the category.
193  thinCategory(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, numNonspatialDims,
194  isThinned);
195  }
196 
197  flagThinnedObservations(isThinned, flagged);
198 
199  if (filtervars.size() != 0) {
200  oops::Log::trace() << "PoissonDiskThinning: flagged? = " << flagged[0] << std::endl;
201  }
202 }
203 
205  int &numNonspatialDims) const
206 {
207  ObsData obsData;
208 
209  numSpatialDims = 0;
210  numNonspatialDims = 0;
211 
212  {
213  obsData.minHorizontalSpacings = options_->minHorizontalSpacing.value();
214  if (obsData.minHorizontalSpacings != boost::none) {
215  validateSpacings(*obsData.minHorizontalSpacings, "min_horizontal_spacing");
216  obsData.latitudes.emplace(obsdb_.nlocs());
217  obsData.longitudes.emplace(obsdb_.nlocs());
218  obsdb_.get_db("MetaData", "latitude", *obsData.latitudes);
219  obsdb_.get_db("MetaData", "longitude", *obsData.longitudes);
220  numSpatialDims = 3;
221  }
222  }
223 
224  {
225  obsData.minVerticalSpacings = options_->minVerticalSpacing.value();
226  if (obsData.minVerticalSpacings != boost::none) {
227  validateSpacings(*obsData.minVerticalSpacings, "min_vertical_spacing");
228  obsData.pressures.emplace(obsdb_.nlocs());
229  obsdb_.get_db("MetaData", "air_pressure", *obsData.pressures);
230  ++numNonspatialDims;
231  }
232  }
233 
234  {
235  obsData.minTimeSpacings = options_->minTimeSpacing.value();
236  if (obsData.minTimeSpacings != boost::none) {
237  validateSpacings(*obsData.minTimeSpacings, "min_time_spacing");
238  obsData.times.emplace(obsdb_.nlocs());
239  obsdb_.get_db("MetaData", "datetime", *obsData.times);
240  ++numNonspatialDims;
241  }
242  }
243 
244  {
245  const boost::optional<Variable> priorityVariable = options_->priorityVariable;
246  if (priorityVariable != boost::none) {
247  obsData.priorities.emplace(obsdb_.nlocs());
248  obsdb_.get_db(priorityVariable.get().group(), priorityVariable.get().variable(),
249  *obsData.priorities);
250  }
251  }
252 
253  return obsData;
254 }
255 
256 template <typename ValueType>
258  const util::ScalarOrMap<int, ValueType> &spacingsByPriority,
259  const std::string &parameterName) const {
260  if (spacingsByPriority.isScalar())
261  return;
262 
263  if (spacingsByPriority.begin() == spacingsByPriority.end())
264  throw eckit::BadParameter(parameterName + " must be a scalar or a non-empty map");
265 
266  // The map is ordered by increasing priority, so the spacing of every item must be
267  // no larger than that of the previous item
268  ValueType prevSpacing = spacingsByPriority.begin()->second;
269  for (const auto &priorityAndSpacing : spacingsByPriority) {
270  const ValueType &spacing = priorityAndSpacing.second;
271  if (spacing > prevSpacing)
272  throw eckit::BadParameter(parameterName +
273  ": exclusion volumes of lower-priority observations must be "
274  "at least as large as those of higher-priority ones.");
275  }
276 }
277 
279  const std::vector<bool> & apply) const {
280  std::vector<size_t> validObsIds;
281  for (size_t obsId = 0; obsId < apply.size(); ++obsId)
282  if (apply[obsId] && (*flags_)[0][obsId] == QCflags::pass)
283  validObsIds.push_back(obsId);
284  return validObsIds;
285 }
286 
288  const std::vector<size_t> &validObsIds,
289  RecursiveSplitter &splitter) const {
290  boost::optional<Variable> categoryVariable = options_->categoryVariable;
291  if (categoryVariable == boost::none)
292  return;
293 
294  ioda::ObsDataVector<int> obsDataVector(obsdb_, categoryVariable.get().variable(),
295  categoryVariable.get().group());
296  const ioda::ObsDataRow<int> &category = obsDataVector[0];
297 
298  std::vector<int> validObsCategories(validObsIds.size());
299  for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex)
300  validObsCategories[validObsIndex] = category[validObsIds[validObsIndex]];
301  splitter.groupBy(validObsCategories);
302 }
303 
305  const std::vector<size_t> &validObsIds,
306  RecursiveSplitter &splitter) const {
307  boost::optional<Variable> priorityVariable = options_->priorityVariable;
308  if (priorityVariable == boost::none)
309  return;
310 
311  ioda::ObsDataVector<int> obsDataVector(obsdb_, priorityVariable.get().variable(),
312  priorityVariable.get().group());
313  const ioda::ObsDataRow<int> &priority = obsDataVector[0];
314 
315  auto reverse = [](int i) {
316  return -i - std::numeric_limits<int>::lowest() + std::numeric_limits<int>::max();
317  };
318 
319  std::vector<int> validObsPriorities(validObsIds.size());
320  for (size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex)
321  // reversing because we want to start with the highest-priority items
322  validObsPriorities[validObsIndex] = reverse(priority[validObsIds[validObsIndex]]);
323  splitter.groupBy(validObsPriorities);
324 }
325 
327  const std::vector<size_t> &obsIdsInCategory,
328  const RecursiveSplitter &prioritySplitter,
329  int numSpatialDims,
330  int numNonspatialDims,
331  std::vector<bool> &isThinned) const {
332  switch (numSpatialDims + numNonspatialDims) {
333  case 0:
334  return; // nothing to do
335  case 1:
336  return thinCategory<1>(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, isThinned);
337  case 2:
338  return thinCategory<2>(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, isThinned);
339  case 3:
340  return thinCategory<3>(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, isThinned);
341  case 4:
342  return thinCategory<4>(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, isThinned);
343  case 5:
344  return thinCategory<5>(obsData, obsIdsInCategory, prioritySplitter, numSpatialDims, isThinned);
345  }
346 
347  ABORT("Unexpected number of thinning dimensions");
348 }
349 
350 template <int numDims>
352  const std::vector<size_t> &obsIdsInCategory,
353  const RecursiveSplitter &prioritySplitter,
354  int numSpatialDims,
355  std::vector<bool> &isThinned) const {
356  KDTree<numDims> pointIndex;
357 
358  for (auto priorityGroup : prioritySplitter.groups()) {
359  for (size_t obsIndex : priorityGroup) {
360  const size_t obsId = obsIdsInCategory[obsIndex];
361  std::array<float, numDims> point = getObservationPosition<numDims>(obsId, obsData);
362  std::array<float, numDims> semiAxes = getExclusionVolumeSemiAxes<numDims>(obsId, obsData);
363  if ((options_->exclusionVolumeShape == ExclusionVolumeShape::CYLINDER &&
364  pointIndex.isAnyPointInCylinderInterior(point, semiAxes, numSpatialDims)) ||
365  (options_->exclusionVolumeShape == ExclusionVolumeShape::ELLIPSOID &&
366  pointIndex.isAnyPointInEllipsoidInterior(point, semiAxes))) {
367  isThinned[obsId] = true;
368  } else {
369  pointIndex.insert(point);
370  }
371  }
372  }
373 }
374 
375 template <int numDims>
377  size_t obsId, const ObsData &obsData) const {
378  std::array<float, numDims> position;
379 
380  unsigned int dim = 0;
381 
382  if (obsData.latitudes && obsData.longitudes) {
383  const float deg2rad = static_cast<float>(M_PI / 180.0);
384  const float earthRadius = Constants::mean_earth_rad;
385 
386  const float lon = deg2rad * (*obsData.longitudes)[obsId];
387  const float lat = deg2rad * (*obsData.latitudes)[obsId];
388  const float sinLat = std::sin(lat);
389  const float cosLat = std::cos(lat);
390  const float sinLon = std::sin(lon);
391  const float cosLon = std::cos(lon);
392 
393  position[dim++] = earthRadius * cosLat * cosLon;
394  position[dim++] = earthRadius * cosLat * sinLon;
395  position[dim++] = earthRadius * sinLat;
396  }
397 
398  if (obsData.pressures) {
399  position[dim++] = (*obsData.pressures)[obsId];
400  }
401 
402  if (obsData.times) {
403  // We choose obsData.times[0] as the reference time when converting datetimes to floats.
404  // Maybe there's a better way.
405  position[dim++] = ((*obsData.times)[obsId] - (*obsData.times)[0]).toSeconds();
406  }
407 
408  return position;
409 }
410 
411 template <int numDims>
413  size_t obsId, const ObsData &obsData) const {
414 
415  std::array<float, numDims> semiAxes;
416 
417  const int priority = obsData.priorities == boost::none ? 0 : (*obsData.priorities)[obsId];
418 
419  unsigned int dim = 0;
420 
421  if (obsData.minHorizontalSpacings) {
422  const float earthDiameter = 2 * Constants::mean_earth_rad;
423  const float invEarthDiameter = 1 / earthDiameter;
424 
425  const float minGeodesicDistance = obsData.minHorizontalSpacings->at(priority);
426  const float minEuclideanDistance =
427  earthDiameter * std::sin(minGeodesicDistance * invEarthDiameter);
428 
429  semiAxes[dim++] = minEuclideanDistance;
430  semiAxes[dim++] = minEuclideanDistance;
431  semiAxes[dim++] = minEuclideanDistance;
432  }
433 
434  if (obsData.minVerticalSpacings) {
435  semiAxes[dim++] = obsData.minVerticalSpacings->at(priority);
436  }
437 
438  if (obsData.minTimeSpacings) {
439  semiAxes[dim++] = obsData.minTimeSpacings->at(priority).toSeconds();
440  }
441 
442  return semiAxes;
443 }
444 
446  const std::vector<bool> & isThinned,
447  std::vector<std::vector<bool>> & flagged) const {
448  for (std::vector<bool> & variableFlagged : flagged)
449  for (size_t obsId = 0; obsId < isThinned.size(); ++obsId)
450  if (isThinned[obsId])
451  variableFlagged[obsId] = true;
452 }
453 
454 void PoissonDiskThinning::print(std::ostream & os) const {
455  os << "PoissonDiskThinning: config = " << config_ << std::endl;
456 }
457 
458 // -----------------------------------------------------------------------------
459 
460 } // namespace ufo
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Extent
Base::Extent Extent
Definition: PoissonDiskThinning.cc:67
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::CoordType
Base::CoordType CoordType
Definition: PoissonDiskThinning.cc:65
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::~PointIndex
virtual ~PointIndex()
Definition: PoissonDiskThinning.cc:47
ufo::PoissonDiskThinning::flagThinnedObservations
void flagThinnedObservations(const std::vector< bool > &isThinned, std::vector< std::vector< bool > > &flagged) const
Definition: PoissonDiskThinning.cc:445
ufo::PoissonDiskThinning::getExclusionVolumeSemiAxes
std::array< float, numDims > getExclusionVolumeSemiAxes(size_t obsId, const ObsData &obsData) const
Definition: PoissonDiskThinning.cc:412
ufo::QCflags::pass
constexpr int pass
Definition: QCflags.h:14
ufo::PoissonDiskThinning::ObsData::minVerticalSpacings
boost::optional< util::ScalarOrMap< int, float > > minVerticalSpacings
Definition: PoissonDiskThinning.cc:133
ufo::Variables
Definition: src/ufo/filters/Variables.h:24
ufo::PoissonDiskThinning::ObsData
Definition: PoissonDiskThinning.cc:128
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Base
PointIndex< numDims_ > Base
Definition: PoissonDiskThinning.cc:63
ufo::PoissonDiskThinning::groupObservationsByCategory
void groupObservationsByCategory(const std::vector< size_t > &validObsIds, RecursiveSplitter &splitter) const
Definition: PoissonDiskThinning.cc:287
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::KDTreeImpl
eckit::KDTreeMemory< TreeTraits > KDTreeImpl
Definition: PoissonDiskThinning.cc:88
ufo::RecursiveSplitter::multiElementGroups
MultiElementGroupRange multiElementGroups() const
Return the range of equivalence classes consisting of more than one element.
Definition: src/ufo/utils/RecursiveSplitter.h:293
ufo_radiancerttov_utils_mod::debug
logical, public debug
Definition: ufo_radiancerttov_utils_mod.F90:100
ufo::FilterBase::obsdb_
ioda::ObsSpace & obsdb_
Definition: FilterBase.h:59
ufo::PoissonDiskThinning::ObsData::longitudes
boost::optional< std::vector< float > > longitudes
Definition: PoissonDiskThinning.cc:131
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::TreeTraits::Point
eckit::geometry::KPoint< numDims > Point
Definition: PoissonDiskThinning.cc:84
ufo::PoissonDiskThinning::getValidObservationIds
std::vector< size_t > getValidObservationIds(const std::vector< bool > &apply) const
Definition: PoissonDiskThinning.cc:278
ufo::ExclusionVolumeShape::ELLIPSOID
@ ELLIPSOID
getScalarOrFilterData.h
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::isAnyPointInEllipsoidInterior
virtual bool isAnyPointInEllipsoidInterior(const Point &center, const Extent &semiAxes) const =0
ufo::FilterBase
FilterBase: Base class for UFO QC filters.
Definition: FilterBase.h:42
ufo::RecursiveSplitter
Partitions an array into groups of elements equivalent according to certain criteria.
Definition: src/ufo/utils/RecursiveSplitter.h:63
PoissonDiskThinningParameters.h
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Alloc
KDTreeImpl::Alloc Alloc
Definition: PoissonDiskThinning.cc:89
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Node
KDTreeImpl::Node Node
Definition: PoissonDiskThinning.cc:90
ufo
Definition: RunCRTM.h:27
ufo::PoissonDiskThinning::ObsData::minHorizontalSpacings
boost::optional< util::ScalarOrMap< int, float > > minHorizontalSpacings
Definition: PoissonDiskThinning.cc:129
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex
Abstract interface of a container storing point sets and able to answer spatial queries needed by Poi...
Definition: PoissonDiskThinning.cc:39
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::insert
virtual void insert(const Point &point)=0
ufo::PoissonDiskThinning::~PoissonDiskThinning
~PoissonDiskThinning() override
Definition: PoissonDiskThinning.cc:155
ufo::PoissonDiskThinning::thinCategory
void thinCategory(const ObsData &obsData, const std::vector< size_t > &obsIdsInCategory, const RecursiveSplitter &prioritySplitter, int numSpatialDims, int numNonspatialDims, std::vector< bool > &isThinned) const
Definition: PoissonDiskThinning.cc:326
ufo::Constants::mean_earth_rad
static constexpr double mean_earth_rad
Definition: Constants.h:39
ufo::PoissonDiskThinning::applyFilter
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
Definition: PoissonDiskThinning.cc:158
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::isAnyPointInCylinderInterior
virtual bool isAnyPointInCylinderInterior(const Point &center, const Extent &semiAxes, int numSpatialDims) const =0
ufo::Variables::size
size_t size() const
Definition: Variables.cc:92
ufo::RecursiveSplitter::groupBy
void groupBy(const std::vector< size_t > &categories)
Split existing equivalence classes according to a new criterion.
Definition: src/ufo/utils/RecursiveSplitter.h:275
ufo::TrackCheckUtils::Point
std::array< float, 3 > Point
Definition: TrackCheckUtils.h:38
ufo::PoissonDiskThinning::PoissonDiskThinning
PoissonDiskThinning(ioda::ObsSpace &obsdb, const eckit::Configuration &config, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
Definition: PoissonDiskThinning.cc:142
ufo::FilterBase::flags_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Definition: FilterBase.h:61
util::ScalarOrMap
Definition: src/ufo/filters/PoissonDiskThinning.h:35
ufo::RecursiveSplitter::groups
GroupRange groups() const
Return the range of equivalence classes.
Definition: src/ufo/utils/RecursiveSplitter.h:290
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::EmptyPayload
Definition: PoissonDiskThinning.cc:81
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree
An implementation of PointIndex storing the point set in a kd-tree.
Definition: PoissonDiskThinning.cc:61
ufo::PoissonDiskThinning::ObsData::latitudes
boost::optional< std::vector< float > > latitudes
Definition: PoissonDiskThinning.cc:130
ufo::PoissonDiskThinning::ObsData::times
boost::optional< std::vector< util::DateTime > > times
Definition: PoissonDiskThinning.cc:137
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::Extent
std::array< CoordType, numDims > Extent
Definition: PoissonDiskThinning.cc:45
ufo::PoissonDiskThinning::options_
std::unique_ptr< PoissonDiskThinningParameters > options_
Definition: src/ufo/filters/PoissonDiskThinning.h:118
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::TreeTraits
Definition: PoissonDiskThinning.cc:83
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Point
Base::Point Point
Definition: PoissonDiskThinning.cc:66
ufo::test::reverse
ObsPair reverse(const ObsPair &pair)
Definition: test/ufo/MetOfficeBuddyPairFinder.h:286
ufo::PoissonDiskThinning::validateSpacings
void validateSpacings(const util::ScalarOrMap< int, ValueType > &spacingsByPriority, const std::string &parameterName) const
Definition: PoissonDiskThinning.cc:257
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::CoordType
float CoordType
Definition: PoissonDiskThinning.cc:41
ufo::PoissonDiskThinningParameters
Options controlling the operation of the PoissonDiskThinning filter.
Definition: PoissonDiskThinningParameters.h:59
ioda::ObsDataVector< int >
PoissonDiskThinning.h
ufo::PoissonDiskThinning::groupObservationsByPriority
void groupObservationsByPriority(const std::vector< size_t > &validObsIds, RecursiveSplitter &splitter) const
Definition: PoissonDiskThinning.cc:304
ufo::FilterBase::config_
const eckit::LocalConfiguration config_
Definition: FilterBase.h:60
ufo_constants_mod::deg2rad
real(kind_real), parameter, public deg2rad
Definition: ufo_constants_mod.F90:50
ufo::PoissonDiskThinning::ObsData::minTimeSpacings
boost::optional< util::ScalarOrMap< int, util::Duration > > minTimeSpacings
Definition: PoissonDiskThinning.cc:136
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::insert
void insert(const Point &point) override
Definition: PoissonDiskThinning.cc:98
ufo::PoissonDiskThinning::ObsData::priorities
boost::optional< std::vector< int > > priorities
Definition: PoissonDiskThinning.cc:139
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::Value
KDTreeImpl::Value Value
Definition: PoissonDiskThinning.cc:92
ufo::anonymous_namespace{PoissonDiskThinning.cc}::PointIndex::Point
std::array< CoordType, numDims > Point
Definition: PoissonDiskThinning.cc:44
Constants.h
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::TreeTraits::Payload
EmptyPayload Payload
Definition: PoissonDiskThinning.cc:85
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::tree_
KDTreeImpl tree_
Definition: PoissonDiskThinning.cc:94
ufo::PoissonDiskThinning::print
void print(std::ostream &) const override
Definition: PoissonDiskThinning.cc:454
ufo::anonymous_namespace{PoissonDiskThinning.cc}::KDTree::KPoint
KDTreeImpl::Point KPoint
Definition: PoissonDiskThinning.cc:91
ufo::PoissonDiskThinning::getObservationPosition
std::array< float, numDims > getObservationPosition(size_t obsId, const ObsData &obsData) const
Definition: PoissonDiskThinning.cc:376
ufo::PoissonDiskThinning::ObsData::pressures
boost::optional< std::vector< float > > pressures
Definition: PoissonDiskThinning.cc:134
ufo::PoissonDiskThinning::getObsData
ObsData getObsData(int &numSpatialDims, int &numNonspatialDims) const
Collect all observation data components used for thinning.
Definition: PoissonDiskThinning.cc:204
ufo::RecursiveSplitter::shuffleGroups
void shuffleGroups(unsigned int seed)
Randomly shuffle the elements of each equivalence class.
Definition: RecursiveSplitter.cc:77
RecursiveSplitter.h
ufo::ExclusionVolumeShape::CYLINDER
@ CYLINDER