IODA
DistributionMethods.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020 UCAR
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 #ifndef TEST_DISTRIBUTION_DISTRIBUTIONMETHODS_H_
9 #define TEST_DISTRIBUTION_DISTRIBUTIONMETHODS_H_
10 
11 #include <algorithm>
12 #include <limits>
13 #include <memory>
14 #include <string>
15 #include <vector>
16 
17 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
18 
19 #include "eckit/config/LocalConfiguration.h"
20 #include "eckit/mpi/Comm.h"
21 #include "eckit/testing/Test.h"
22 
23 #include "oops/mpi/mpi.h"
24 #include "oops/runs/Test.h"
25 #include "oops/test/TestEnvironment.h"
26 #include "oops/util/Logger.h"
27 
28 #include "ioda/distribution/Accumulator.h"
29 #include "ioda/distribution/Distribution.h"
30 #include "ioda/distribution/DistributionFactory.h"
31 
32 namespace ioda {
33 namespace test {
34 
35 // -----------------------------------------------------------------------------
36 
37 template <typename T>
38 void testAccumulateScalar(const Distribution &TestDist, const std::vector<size_t> &myRecords,
39  size_t expectedSum) {
40  auto accumulator = TestDist.createAccumulator<T>();
41  for (size_t loc = 0; loc < myRecords.size(); ++loc)
42  accumulator->addTerm(loc, myRecords[loc]);
43  const T sum = accumulator->computeResult();
44  EXPECT_EQUAL(sum, static_cast<T>(expectedSum));
45 }
46 
47 template <typename T>
48 void testAccumulateVector(const Distribution &TestDist, const std::vector<size_t> &myRecords,
49  size_t expectedSum) {
50  const size_t numSums = 3;
51  std::vector<T> expectedSums(numSums);
52  for (size_t i = 0; i < numSums; ++i)
53  expectedSums[i] = (i + 1) * expectedSum;
54 
55  // Part 1: two-argument addTerm overload
56  {
57  auto accumulator = TestDist.createAccumulator<T>(numSums);
58  std::vector<T> terms(numSums);
59  for (size_t loc = 0; loc < myRecords.size(); ++loc) {
60  for (size_t i = 0; i < numSums; ++i)
61  terms[i] = (i + 1) * myRecords[loc];
62  accumulator->addTerm(loc, terms);
63  }
64  std::vector<T> sums = accumulator->computeResult();
65 
66  EXPECT_EQUAL(sums, expectedSums);
67  }
68 
69  // Part 2: three-argument addTerm overload
70  {
71  auto accumulator = TestDist.createAccumulator<T>(numSums);
72  std::vector<T> terms(numSums);
73  for (size_t loc = 0; loc < myRecords.size(); ++loc)
74  for (size_t i = 0; i < numSums; ++i)
75  accumulator->addTerm(loc, i, (i + 1) * myRecords[loc]);
76  std::vector<T> sums = accumulator->computeResult();
77 
78  EXPECT_EQUAL(sums, expectedSums);
79  }
80 }
81 
82 template <typename T>
83 void testMaxScalar(const Distribution &TestDist, const std::vector<size_t> &myRecords,
84  size_t expectedMax) {
85  // Perform a local reduction
86  T max = std::numeric_limits<T>::lowest();
87  for (size_t loc = 0; loc < myRecords.size(); ++loc)
88  max = std::max<T>(max, myRecords[loc]);
89 
90  // Perform a global reduction
91  TestDist.max(max);
92 
93  EXPECT_EQUAL(max, expectedMax);
94 }
95 
96 template <typename T>
97 void testMaxVector(const Distribution &TestDist, const std::vector<size_t> &myRecords,
98  size_t expectedMax) {
99  // Perform a local reduction
100  T max = std::numeric_limits<T>::lowest();
101  for (size_t loc = 0; loc < myRecords.size(); ++loc)
102  max = std::max<T>(max, myRecords[loc]);
103 
104  // Perform a global reduction
105  const T shift = 10;
106  std::vector<T> maxes{max, max + shift};
107  TestDist.max(maxes);
108  const std::vector<T> expectedMaxes{static_cast<T>(expectedMax),
109  static_cast<T>(expectedMax + shift)};
110 
111  EXPECT_EQUAL(maxes, expectedMaxes);
112 }
113 
114 template <typename T>
115 void testMinScalar(const Distribution &TestDist, const std::vector<size_t> &myRecords,
116  size_t expectedMin) {
117  // Perform a local reduction
118  T min = std::numeric_limits<T>::max();
119  for (size_t loc = 0; loc < myRecords.size(); ++loc)
120  min = std::min<T>(min, myRecords[loc]);
121 
122  // Perform a global reduction
123  TestDist.min(min);
124 
125  EXPECT_EQUAL(min, expectedMin);
126 }
127 
128 template <typename T>
129 void testMinVector(const Distribution &TestDist, const std::vector<size_t> &myRecords,
130  size_t expectedMin) {
131  // Perform a local reduction
132  T min = std::numeric_limits<T>::max();
133  for (size_t loc = 0; loc < myRecords.size(); ++loc)
134  min = std::min<T>(min, myRecords[loc]);
135 
136  // Perform a global reduction
137  const T shift = 10;
138  std::vector<T> mins{min, min + shift};
139  TestDist.min(mins);
140  const std::vector<T> expectedMins{static_cast<T>(expectedMin),
141  static_cast<T>(expectedMin + shift)};
142 
143  EXPECT_EQUAL(mins, expectedMins);
144 }
145 
147  eckit::LocalConfiguration conf(::test::TestEnvironment::config());
148  std::vector<eckit::LocalConfiguration> dist_types;
149  const eckit::mpi::Comm & MpiComm = oops::mpi::world();
150 
151  std::string DistName;
152  std::unique_ptr<ioda::Distribution> TestDist;
153  std::size_t MyRank = MpiComm.rank();
154  std::size_t nprocs = MpiComm.size();
155  conf.get("distribution types", dist_types);
156  for (std::size_t i = 0; i < dist_types.size(); ++i) {
157  conf.get("distribution", dist_types);
158  oops::Log::debug() << "Distribution::DistributionTypes: conf: "
159  << dist_types[i] << std::endl;
160  DistName = dist_types[i].getString("name");
161 
162  eckit::LocalConfiguration DistConfig;
163  DistConfig.set("distribution", DistName);
164 
165  TestDist = DistributionFactory::create(MpiComm, DistConfig);
166 
167  // initialize distributions
168  size_t Gnlocs = nprocs;
169  std::vector<double> glats(Gnlocs, 0.0);
170  std::vector<double> glons(Gnlocs, 0.0);
171  std::vector<size_t> myRecords;
172  for (std::size_t j = 0; j < Gnlocs; ++j) {
173  glons[j] = j*360.0/Gnlocs;
174  eckit::geometry::Point2 point(glons[j], glats[j]);
175  TestDist->assignRecord(j, j, point);
176  if (TestDist->isMyRecord(j))
177  myRecords.push_back(j);
178  }
179  TestDist->computePatchLocs();
180 
181  // Expected results
182  // Accumulate: sum (0 + 1 + ... + nprocs - 1)
183  size_t expectedSum = 0;
184  for (std::size_t i = 0; i < nprocs; i++) {
185  expectedSum += i;
186  }
187  // Max: max (0, 1, ..., nprocs - 1)
188  const size_t expectedMax = nprocs - 1;
189  // Min: min (0, 1, ..., nprocs - 1)
190  const size_t expectedMin = 0;
191 
192  testAccumulateScalar<double>(*TestDist, myRecords, expectedSum);
193  testAccumulateScalar<float>(*TestDist, myRecords, expectedSum);
194  testAccumulateScalar<int>(*TestDist, myRecords, expectedSum);
195  testAccumulateScalar<size_t>(*TestDist, myRecords, expectedSum);
196  testAccumulateVector<double>(*TestDist, myRecords, expectedSum);
197  testAccumulateVector<float>(*TestDist, myRecords, expectedSum);
198  testAccumulateVector<int>(*TestDist, myRecords, expectedSum);
199  testAccumulateVector<size_t>(*TestDist, myRecords, expectedSum);
200 
201  testMaxScalar<double>(*TestDist, myRecords, expectedMax);
202  testMaxScalar<float>(*TestDist, myRecords, expectedMax);
203  testMaxScalar<int>(*TestDist, myRecords, expectedMax);
204  testMaxScalar<size_t>(*TestDist, myRecords, expectedMax);
205  testMaxVector<double>(*TestDist, myRecords, expectedMax);
206  testMaxVector<float>(*TestDist, myRecords, expectedMax);
207  testMaxVector<int>(*TestDist, myRecords, expectedMax);
208  testMaxVector<size_t>(*TestDist, myRecords, expectedMax);
209 
210  testMinScalar<double>(*TestDist, myRecords, expectedMin);
211  testMinScalar<float>(*TestDist, myRecords, expectedMin);
212  testMinScalar<int>(*TestDist, myRecords, expectedMin);
213  testMinScalar<size_t>(*TestDist, myRecords, expectedMin);
214  testMinVector<double>(*TestDist, myRecords, expectedMin);
215  testMinVector<float>(*TestDist, myRecords, expectedMin);
216  testMinVector<int>(*TestDist, myRecords, expectedMin);
217  testMinVector<size_t>(*TestDist, myRecords, expectedMin);
218  }
219 }
220 
221 
222 // -----------------------------------------------------------------------------
223 
224 class DistributionMethods : public oops::Test {
225  public:
227  virtual ~DistributionMethods() {}
228  private:
229  std::string testid() const override {return "test::DistributionMethods";}
230 
231  void register_tests() const override {
232  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
233 
234  ts.emplace_back(CASE("distribution/Distribution/testDistributionMethods")
235  { testDistributionMethods(); });
236  }
237 
238  void clear() const override {}
239 };
240 
241 // -----------------------------------------------------------------------------
242 
243 } // namespace test
244 } // namespace ioda
245 
246 #endif // TEST_DISTRIBUTION_DISTRIBUTIONMETHODS_H_
static std::unique_ptr< Distribution > create(const eckit::mpi::Comm &comm, const eckit::Configuration &config)
Create a Distribution object implementing a particular method of distributing observations across mul...
void register_tests() const override
std::string testid() const override
void testAccumulateScalar(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedSum)
void testMinVector(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedMin)
void testMinScalar(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedMin)
void testMaxVector(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedMax)
void testDistributionMethods()
CASE("Derived variable and unit conversion methods")
void testMaxScalar(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedMax)
void testAccumulateVector(const Distribution &TestDist, const std::vector< size_t > &myRecords, size_t expectedSum)