UFO
ObsCategoricalData.h
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 UK 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 
8 #ifndef UFO_CATEGORICALOPER_OBSCATEGORICALDATA_H_
9 #define UFO_CATEGORICALOPER_OBSCATEGORICALDATA_H_
10 
11 #include <algorithm>
12 #include <map>
13 #include <memory>
14 #include <ostream>
15 #include <string>
16 #include <utility>
17 #include <vector>
18 
19 #include "ioda/ObsVector.h"
20 
21 #include "oops/base/Variables.h"
22 
23 #include "oops/util/Logger.h"
24 
26 
27 /// Forward declarations
28 namespace eckit {
29  class Configuration;
30 }
31 
32 namespace ioda {
33  class ObsSpace;
34 }
35 
36 namespace ufo {
37 
38 // Define some traits that enable ObsCategoricalData to work with both linear and nonlinear
39 // operators.
40 
41 class LinearObsOperatorBase;
42 class LinearObsOperatorFactory;
43 class LinearObsOperatorParametersWrapper;
44 
45 class ObsOperatorBase;
46 class ObsOperatorFactory;
47 class ObsOperatorParametersWrapper;
48 
49 template <typename OPBASE>
51 
52 template <>
56 };
57 
58 template <>
62 };
63 
64 /// \brief Data handler for the Categorical observation operator and TL/AD code.
65 template <typename OPBASE>
67  public:
68  /// Get all information related to the configuration of the Categorical operator and TL/AD code.
69  void configure(const ioda::ObsSpace & odb,
70  const ObsCategoricalParameters & parameters)
71  {
72  // Get categorical variable from ObsSpace (and throw an exception if it is not present).
73  // In the ObsSpace, the categorical variable can be either a vector of strings or
74  // a vector of integers; if the latter, it is converted to a vector of strings here.
75  const std::string &categoricalVariableName = parameters.categoricalVariable.value();
76  oops::Log::debug() << "categorical variable: " << categoricalVariableName << std::endl;
77  categoricalVariable_.assign(odb.nlocs(), "");
78  if (odb.has("MetaData", categoricalVariableName)) {
79  const ioda::ObsDtype dtype = odb.dtype("MetaData", categoricalVariableName);
80  if (dtype == ioda::ObsDtype::String) {
81  odb.get_db("MetaData", categoricalVariableName, categoricalVariable_);
82  } else if (dtype == ioda::ObsDtype::Integer) {
83  std::vector <int> categoricalVariableInt(odb.nlocs());
84  odb.get_db("MetaData", categoricalVariableName, categoricalVariableInt);
85  std::transform(categoricalVariableInt.cbegin(), categoricalVariableInt.cend(),
86  categoricalVariable_.begin(), [](int i) {return std::to_string(i);});
87  } else {
88  throw eckit::UserError("The categorical variable must be a vector of "
89  "either strings or integers", Here());
90  }
91  } else {
92  throw eckit::UserError("The categorical variable " + categoricalVariableName +
93  " does not exist", Here());
94  }
95 
96  // Name of fallback operator.
97  fallbackOperatorName_ = parameters.fallbackOperatorName.value();
98  oops::Log::debug() << "Fallback operator: " << fallbackOperatorName_ << std::endl;
99 
100  // Map of categorised operator names.
102  oops::Log::debug() << "Categorised operators: " << categorisedOperatorNames_ << std::endl;
103 
104  // Fill vector of operator names.
105  for (size_t jloc = 0; jloc < odb.nlocs(); ++jloc) {
106  auto it_operName = categorisedOperatorNames_.find(categoricalVariable_[jloc]);
107  const auto &operName = (it_operName != categorisedOperatorNames_.end() ?
108  it_operName->second :
110  locOperNames_.emplace_back(operName);
111  }
112 
113  // Create list of component operators.
114  for (const eckit::LocalConfiguration &operatorConfig :
115  parameters.operatorConfigurations.value()) {
116  typedef typename ObsOperatorTraits<OPBASE>::Factory_ Factory_;
117  typedef typename ObsOperatorTraits<OPBASE>::ParametersWrapper_ ParametersWrapper_;
118 
119  ParametersWrapper_ operatorParams;
120  operatorParams.validateAndDeserialize(operatorConfig);
121  std::unique_ptr<OPBASE> op(Factory_::create(odb, operatorParams.operatorParameters));
122  requiredVars_ += op->requiredVars();
123  components_.emplace(std::make_pair(operatorConfig.getString("name"), std::move(op)));
124  }
125 
126  // Check the fallback operator has been configured.
127  if (components_.find(fallbackOperatorName_) == components_.end())
128  throw eckit::UserError("The operator " + fallbackOperatorName_ +
129  " has not been configured", Here());
130 
131  // Check the categorised operators have been configured.
132  for (const auto &operName : categorisedOperatorNames_)
133  if (components_.find(operName.second) == components_.end())
134  throw eckit::UserError("The operator " + operName.second +
135  " has not been configured", Here());
136  }
137 
138  /// Return required variables for the operator.
139  const oops::Variables & requiredVars() const {return requiredVars_;}
140 
141  /// Return component operators.
142  const std::map<std::string, std::unique_ptr<OPBASE>> & components() const {return components_;}
143 
144  /// Return list of operator names to use at each location.
145  const std::vector<std::string> & locOperNames() const {return locOperNames_;}
146 
147  /// Fill final H(x) vector from a list of components.
148  void fillHofX(const std::map <std::string, ioda::ObsVector> & ovecs,
149  ioda::ObsVector & ovec) const {
150  // Insert values into ovec according to the categorical variable.
151  // Use the fallback operator when necessary.
152  for (size_t jloc = 0; jloc < ovec.nlocs(); ++jloc) {
153  const auto &ovecloc = ovecs.at(locOperNames_[jloc]);
154  // Loop over each variable at this location.
155  for (size_t jvar = 0; jvar < ovec.nvars(); ++jvar) {
156  const size_t idx = jloc * ovec.nvars() + jvar;
157  ovec[idx] = ovecloc[idx];
158  }
159  }
160  }
161 
162  void print(std::ostream & os) const {
163  os << "ObsCategorical operator:" << std::endl;
164  os << "- Fallback operator: " << fallbackOperatorName_ << std::endl;
165  os << "- Categorised operators: " << categorisedOperatorNames_ << std::endl;
166  }
167 
168  private:
169  /// Required variables.
170  oops::Variables requiredVars_;
171 
172  /// Observation operators which are run by the Categorical operator.
173  std::map<std::string, std::unique_ptr<OPBASE>> components_;
174 
175  /// Value of the categorical variable in the ObsSpace.
176  std::vector <std::string> categoricalVariable_;
177 
178  /// Name of the fallback observation operator.
180 
181  /// Names of the categorised observation operators.
182  std::map<std::string, std::string> categorisedOperatorNames_;
183 
184  /// Operator name at each location.
185  std::vector<std::string> locOperNames_;
186 };
187 
188 } // namespace ufo
189 #endif // UFO_CATEGORICALOPER_OBSCATEGORICALDATA_H_
Linear obs operator factory.
Contains a polymorphic parameter holding an instance of a subclass of ObsOperatorParametersBase.
Data handler for the Categorical observation operator and TL/AD code.
const oops::Variables & requiredVars() const
Return required variables for the operator.
std::map< std::string, std::string > categorisedOperatorNames_
Names of the categorised observation operators.
void print(std::ostream &os) const
const std::map< std::string, std::unique_ptr< OPBASE > > & components() const
Return component operators.
std::vector< std::string > locOperNames_
Operator name at each location.
std::map< std::string, std::unique_ptr< OPBASE > > components_
Observation operators which are run by the Categorical operator.
std::string fallbackOperatorName_
Name of the fallback observation operator.
std::vector< std::string > categoricalVariable_
Value of the categorical variable in the ObsSpace.
const std::vector< std::string > & locOperNames() const
Return list of operator names to use at each location.
void fillHofX(const std::map< std::string, ioda::ObsVector > &ovecs, ioda::ObsVector &ovec) const
Fill final H(x) vector from a list of components.
void configure(const ioda::ObsSpace &odb, const ObsCategoricalParameters &parameters)
Get all information related to the configuration of the Categorical operator and TL/AD code.
oops::Variables requiredVars_
Required variables.
Configuration options recognized by the Categorical operator.
oops::RequiredParameter< std::vector< eckit::LocalConfiguration > > operatorConfigurations
oops::RequiredParameter< std::string > fallbackOperatorName
oops::RequiredParameter< std::map< std::string, std::string > > categorisedOperatorNames
oops::RequiredParameter< std::string > categoricalVariable
Categorical variable used to divide H(x) into sections.
Obs Operator Factory.
Contains a polymorphic parameter holding an instance of a subclass of ObsOperatorParametersBase.
Forward declarations.
Definition: ObsAodExt.h:21
Forward declarations.
Definition: ObsAodExt.h:25
Definition: RunCRTM.h:27
LinearObsOperatorParametersWrapper ParametersWrapper_
ObsOperatorParametersWrapper ParametersWrapper_