IODA
test/distribution/Distribution.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2009-2016 ECMWF.
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  * In applying this licence, ECMWF does not waive the privileges and immunities
7  * granted to it by virtue of its status as an intergovernmental organisation nor
8  * does it submit to any jurisdiction.
9  */
10 
11 #ifndef TEST_DISTRIBUTION_DISTRIBUTION_H_
12 #define TEST_DISTRIBUTION_DISTRIBUTION_H_
13 
14 #include <cmath>
15 #include <memory>
16 #include <numeric>
17 #include <set>
18 #include <string>
19 #include <vector>
20 
21 #define ECKIT_TESTING_SELF_REGISTER_CASES 0
22 
23 #include <boost/noncopyable.hpp>
24 
25 #include "eckit/config/LocalConfiguration.h"
26 #include "eckit/mpi/Comm.h"
27 #include "eckit/testing/Test.h"
28 
29 #include "oops/mpi/mpi.h"
30 #include "oops/runs/Test.h"
31 #include "oops/test/TestEnvironment.h"
32 #include "oops/util/Logger.h"
33 
34 #include "ioda/distribution/Distribution.h"
35 #include "ioda/distribution/DistributionFactory.h"
36 
37 namespace ioda {
38 namespace test {
39 
40 // -----------------------------------------------------------------------------
41 
42 void testConstructor() {
43  const eckit::LocalConfiguration conf(::test::TestEnvironment::config());
44  std::vector<eckit::LocalConfiguration> dist_types;
45  const eckit::mpi::Comm & MpiComm = oops::mpi::world();
46 
47  std::string TestDistType;
48  std::string DistName;
49  std::unique_ptr<ioda::Distribution> TestDist;
50  DistributionFactory * DistFactory = nullptr;
51 
52  // Walk through the different distribution types and try constructing.
53  conf.get("distribution types", dist_types);
54  for (std::size_t i = 0; i < dist_types.size(); ++i) {
55  oops::Log::debug() << "Distribution::DistributionTypes: conf: " << dist_types[i] << std::endl;
56 
57  TestDistType = dist_types[i].getString("distribution");
58  oops::Log::debug() << "Distribution::DistType: " << TestDistType << std::endl;
59 
60  DistName = dist_types[i].getString("specs.name");
61  TestDist.reset(DistFactory->createDistribution(MpiComm, DistName));
62  EXPECT(TestDist.get());
63  }
64  }
65 
66 // -----------------------------------------------------------------------------
67 
69  const eckit::LocalConfiguration conf(::test::TestEnvironment::config());
70  std::vector<eckit::LocalConfiguration> dist_types;
71  const eckit::mpi::Comm & MpiComm = oops::mpi::world();
72 
73  std::string TestDistType;
74  std::string DistName;
75  std::unique_ptr<ioda::Distribution> TestDist;
76  DistributionFactory * DistFactory = nullptr;
77 
78  std::size_t MyRank = MpiComm.rank();
79 
80  // Walk through the different distribution types and try constructing.
81  conf.get("distribution types", dist_types);
82  for (std::size_t i = 0; i < dist_types.size(); ++i) {
83  oops::Log::debug() << "Distribution::DistributionTypes: conf: "
84  << dist_types[i] << std::endl;
85 
86  TestDistType = dist_types[i].getString("distribution");
87  oops::Log::debug() << "Distribution::DistType: " << TestDistType << std::endl;
88 
89  DistName = dist_types[i].getString("specs.name");
90  TestDist.reset(DistFactory->createDistribution(MpiComm, DistName));
91  EXPECT(TestDist.get());
92 
93  // Expected results are listed in "specs.index" with the MPI rank number
94  // appended on the end.
95  std::string MyRankCfgName = "specs.rank" + std::to_string(MyRank);
96  eckit::LocalConfiguration MyRankConfig = dist_types[i].getSubConfiguration(MyRankCfgName);
97  oops::Log::debug() << "Distribution::DistributionTypes: "
98  << MyRankCfgName << ": " << MyRankConfig << std::endl;
99 
100  std::size_t ExpectedNlocs = MyRankConfig.getUnsigned("nlocs");
101  std::size_t ExpectedNrecs = MyRankConfig.getUnsigned("nrecs");
102  std::vector<std::size_t> ExpectedIndex =
103  MyRankConfig.getUnsignedVector("index");
104  std::vector<std::size_t> ExpectedRecnums =
105  MyRankConfig.getUnsignedVector("recnums");
106 
107  // If obsgrouping is specified then read the record grouping directly from
108  // the config file. Otherwise, assign 0 to Gnlocs-1 into the record grouping
109  // vector.
110  std::size_t Gnlocs = dist_types[i].getInt("specs.gnlocs");
111  std::vector<std::size_t> Groups(Gnlocs, 0);
112  if (dist_types[i].has("specs.obsgrouping")) {
113  Groups = dist_types[i].getUnsignedVector("specs.obsgrouping");
114  } else {
115  std::iota(Groups.begin(), Groups.end(), 0);
116  }
117 
118  // Loop on gnlocs, and keep the indecies according to the distribution type.
119  std::vector<std::size_t> Index;
120  std::vector<std::size_t> Recnums;
121  std::set<std::size_t> UniqueRecnums;
122  for (std::size_t j = 0; j < Gnlocs; ++j) {
123  std::size_t RecNum = Groups[j];
124  if (TestDist->isMyRecord(RecNum)) {
125  Index.push_back(j);
126  Recnums.push_back(RecNum);
127  UniqueRecnums.insert(RecNum);
128  }
129  }
130 
131  // Check the location and record counts
132  std::size_t Nlocs = Index.size();
133  std::size_t Nrecs = UniqueRecnums.size();
134  EXPECT(Nlocs == ExpectedNlocs);
135  EXPECT(Nrecs == ExpectedNrecs);
136 
137  // Check the resulting index and recnum vectors
138  for (std::size_t j = 0; j < ExpectedIndex.size(); ++j) {
139  EXPECT(Index[j] == ExpectedIndex[j]);
140  }
141 
142  for (std::size_t j = 0; j < ExpectedRecnums.size(); ++j) {
143  EXPECT(Recnums[j] == ExpectedRecnums[j]);
144  }
145  }
146 }
147 
148 // -----------------------------------------------------------------------------
149 
150 class Distribution : public oops::Test {
151  public:
153  virtual ~Distribution() {}
154  private:
155  std::string testid() const override {return "test::Distribution";}
156 
157  void register_tests() const override {
158  std::vector<eckit::testing::Test>& ts = eckit::testing::specification();
159 
160  ts.emplace_back(CASE("distribution/Distribution/testConstructor")
161  { testConstructor(); });
162  ts.emplace_back(CASE("distribution/Distribution/testDistribution")
163  { testDistribution(); });
164  }
165 
166  void clear() const override {}
167 };
168 
169 // -----------------------------------------------------------------------------
170 
171 } // namespace test
172 } // namespace ioda
173 
174 #endif // TEST_DISTRIBUTION_DISTRIBUTION_H_
ioda::DistributionFactory::createDistribution
Distribution * createDistribution(const eckit::mpi::Comm &Comm, const std::string &Method)
create a Distribution object
Definition: DistributionFactory.cc:26
ioda::test::Distribution::Distribution
Distribution()
Definition: test/distribution/Distribution.h:152
ioda::test::Distribution::testid
std::string testid() const override
Definition: test/distribution/Distribution.h:155
ioda
Definition: IodaUtils.cc:13
ioda::DistributionFactory
Factory class to instantiate objects of Distribution subclasses.
Definition: DistributionFactory.h:28
ioda::test::Distribution::register_tests
void register_tests() const override
Definition: test/distribution/Distribution.h:157
ioda::test::Distribution::clear
void clear() const override
Definition: test/distribution/Distribution.h:166
ioda::test::testConstructor
void testConstructor()
Definition: test/core/ObsSpaceContainer.h:76
ioda::test::testDistribution
void testDistribution()
Definition: test/distribution/Distribution.h:68
ioda::test::Distribution::~Distribution
virtual ~Distribution()
Definition: test/distribution/Distribution.h:153
ioda::test::Distribution
Definition: test/distribution/Distribution.h:150