UFO
ObsProfileAverageData.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 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 
8 #include "oops/util/FloatCompare.h"
9 #include "oops/util/missingValues.h"
10 
13 #include "ufo/utils/OperatorUtils.h" // for getOperatorVariables
14 
15 namespace ufo {
16 
18  const eckit::Configuration & config)
19  : odb_(odb)
20  {
21  // Ensure observations have been grouped into profiles.
22  if (odb_.obs_group_vars().empty())
23  throw eckit::UserError("Group variables configuration is empty", Here());
24 
25  // Ensure observations have been sorted by air pressure in descending order.
26  if (odb_.obs_sort_var() != "air_pressure")
27  throw eckit::UserError("Sort variable must be air_pressure", Here());
28  if (odb_.obs_sort_order() != "descending")
29  throw eckit::UserError("Profiles must be sorted in descending order", Here());
30 
31  // Check the ObsSpace has been extended. If this is not the case
32  // then it will not be possible to access profiles in the original and
33  // extended sections of the ObsSpace.
34  if (!odb_.has("MetaData", "extended_obs_space"))
35  throw eckit::UserError("The extended obs space has not been produced", Here());
36 
37  // Check the observed pressure is present. This is necessary in order to
38  // determine the slant path locations.
39  if (!odb_.has("MetaData", "air_pressure"))
40  throw eckit::UserError("air_pressure@MetaData not present", Here());
41 
42  // Set up configuration options.
43  options_.validateAndDeserialize(config);
44 
45  // Add model air pressure to the list of variables used in this operator.
46  // This GeoVaL is used to determine the slant path locations.
48  requiredVars_ += oops::Variables({modelVerticalCoord_});
49 
50  // Add any simulated variables to the list of variables used in this operator.
53 
54  // If required, set up vectors for OPS comparison.
55  if (options_.compareWithOPS.value())
57  }
58 
59  const oops::Variables & ObsProfileAverageData::requiredVars() const {
60  return requiredVars_;
61  }
62 
63  const oops::Variables & ObsProfileAverageData::simulatedVars() const {
64  return operatorVars_;
65  }
66 
67  const std::vector<int> & ObsProfileAverageData::operatorVarIndices() const {
68  return operatorVarIndices_;
69  }
70 
72  // Only perform the caching once.
73  if (!cachedGeoVaLs_) cachedGeoVaLs_.reset(new GeoVaLs(gv));
74  }
75 
77  (const std::vector<std::size_t> & locsOriginal,
78  const std::vector<std::size_t> & locsExtended) const
79  {
80  const std::vector<std::size_t> slant_path_location =
83  locsOriginal,
86 
87  // If required, compare slant path locations and slant pressure with OPS output.
88  if (options_.compareWithOPS.value()) {
89  // Vector of slanted pressures, used for comparisons with OPS.
90  std::vector<float> slant_pressure;
91  // Number of levels for model pressure.
92  const std::size_t nlevs_p = cachedGeoVaLs_->nlevs(modelVerticalCoord_);
93  // Vector used to store different pressure GeoVaLs.
94  std::vector <float> pressure_gv(nlevs_p);
95  for (std::size_t mlev = 0; mlev < nlevs_p; ++mlev) {
96  cachedGeoVaLs_->getAtLocation(pressure_gv, modelVerticalCoord_, slant_path_location[mlev]);
97  slant_pressure.push_back(pressure_gv[mlev]);
98  }
99  this->compareAuxiliaryReferenceVariables(locsExtended,
100  slant_path_location,
101  slant_pressure);
102  }
103 
104  return slant_path_location;
105  }
106 
108  if (!(odb_.has("MetOfficeHofX", "slant_path_location") &&
109  odb_.has("MetOfficeHofX", "slant_pressure")))
110  throw eckit::UserError("At least one reference variable is not present", Here());
111  // Get reference values of the slant path locations and pressures.
112  slant_path_location_ref_.resize(odb_.nlocs());
113  slant_pressure_ref_.resize(odb_.nlocs());
114  odb_.get_db("MetOfficeHofX", "slant_path_location", slant_path_location_ref_);
115  odb_.get_db("MetOfficeHofX", "slant_pressure", slant_pressure_ref_);
116  }
117 
119  (const std::vector<std::size_t> & locsExtended,
120  const std::vector<std::size_t> & slant_path_location,
121  const std::vector<float> & slant_pressure) const {
122  std::vector<int> slant_path_location_ref_profile;
123  std::vector<float> slant_pressure_ref_profile;
124  for (const std::size_t loc : locsExtended) {
125  slant_path_location_ref_profile.push_back(slant_path_location_ref_[loc]);
126  slant_pressure_ref_profile.push_back(slant_pressure_ref_[loc]);
127  }
128  std::stringstream errmsg;
129  for (std::size_t mlev = 0; mlev < locsExtended.size(); ++mlev) {
130  if (slant_path_location[mlev] != slant_path_location_ref_profile[mlev]) {
131  errmsg << "Mismatch for slant_path_location, level = " << mlev
132  << " (this code, OPS): "
133  << slant_path_location[mlev] << ", "
134  << slant_path_location_ref_profile[mlev];
135  throw eckit::BadValue(errmsg.str(), Here());
136  }
137  if (!oops::is_close_relative(slant_pressure[mlev],
138  slant_pressure_ref_profile[mlev],
139  1e-5f)) {
140  errmsg << "Mismatch for slant_pressure, level = " << mlev
141  << " (this code, OPS): "
142  << slant_pressure[mlev] << ", "
143  << slant_pressure_ref_profile[mlev];
144  throw eckit::BadValue(errmsg.str(), Here());
145  }
146  }
147  }
148 
149  void ObsProfileAverageData::print(std::ostream & os) const {
150  os << "ObsProfileAverage operator" << std::endl;
151  os << "config = " << options_ << std::endl;
152  }
153 } // namespace ufo
GeoVaLs: geophysical values at locations.
std::string modelVerticalCoord_
Name of model vertical coordinate.
void compareAuxiliaryReferenceVariables(const std::vector< std::size_t > &locsExtended, const std::vector< std::size_t > &slant_path_location, const std::vector< float > &slant_pressure) const
Compare auxiliary reference variables with those obtained in OPS.
std::vector< std::size_t > getSlantPathLocations(const std::vector< std::size_t > &locsOriginal, const std::vector< std::size_t > &locsExtended) const
oops::Variables requiredVars_
Required variables.
ObsProfileAverageData(const ioda::ObsSpace &odb, const eckit::Configuration &config)
std::vector< int > operatorVarIndices_
Indices of operator variables.
std::unique_ptr< GeoVaLs > cachedGeoVaLs_
Cached GeoVaLs.
ObsProfileAverageParameters options_
Options for this observation operator.
const oops::Variables & requiredVars() const
Return required variables for the operator.
void cacheGeoVaLs(const GeoVaLs &gv) const
Cache the initial values of the GeoVaLs.
const oops::Variables & simulatedVars() const
Return simulated variables for the operator.
const std::vector< int > & operatorVarIndices() const
Return operator variable indices for the operator.
void print(std::ostream &os) const
Print operator configuration options.
std::vector< float > slant_pressure_ref_
Reference values of slant path pressures.
std::vector< int > slant_path_location_ref_
Reference values of slant path locations.
oops::Variables operatorVars_
Operator variables.
const ioda::ObsSpace & odb_
ObsSpace.
oops::Parameter< bool > compareWithOPS
Perform comparisons of auxiliary variables with OPS?
oops::RequiredParameter< std::string > modelVerticalCoordinate
Name of model vertical coordinate.
Definition: RunCRTM.h:27
void getOperatorVariables(const eckit::Configuration &conf, const oops::Variables &simulatedVariables, oops::Variables &operatorVariables, std::vector< int > &operatorVariableIndices)
std::vector< std::size_t > getSlantPathLocations(const ioda::ObsSpace &odb, const GeoVaLs &gv, const std::vector< std::size_t > &locs, const std::string &modelVerticalCoord, const int itermax)