UFO
ObsBias.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2017-2019 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 #include "ufo/ObsBias.h"
9 
10 #include <Eigen/Core>
11 #include <fstream>
12 #include <iomanip>
13 #include <set>
14 
15 #include "ioda/ObsVector.h"
16 
17 #include "oops/base/Variables.h"
18 #include "oops/util/IntSetParser.h"
19 #include "oops/util/Logger.h"
20 
21 #include "ufo/ObsBiasIncrement.h"
22 #include "ufo/ObsDiagnostics.h"
23 
24 namespace ufo {
25 
26 // -----------------------------------------------------------------------------
27 
28 ObsBias::ObsBias(ioda::ObsSpace & odb, const eckit::Configuration & conf)
29  : predbases_(0), jobs_(0), odb_(odb), conf_(conf) {
30  oops::Log::trace() << "ObsBias::create starting." << std::endl;
31 
32  // Get the jobs(channels)
33  if (conf_.has("obs bias.jobs")) {
34  const std::set<int> jobs = oops::parseIntSet(conf_.getString("obs bias.jobs"));
35  jobs_.assign(jobs.begin(), jobs.end());
36  }
37 
38  // Predictor factory
39  if (conf_.has("obs bias.predictors")) {
40  std::vector<eckit::LocalConfiguration> confs;
41  conf_.get("obs bias.predictors", confs);
42  for (std::size_t j = 0; j < confs.size(); ++j) {
43  std::shared_ptr<PredictorBase> pred(PredictorFactory::create(confs[j], jobs_));
44  predbases_.push_back(pred);
45  prednames_.push_back(pred->name());
46  geovars_ += pred->requiredGeovars();
47  hdiags_ += pred->requiredHdiagnostics();
48 
49  // Reserve the space for ObsBiasTerm for predictor
50  if (jobs_.size() > 0) {
51  for (auto & job : jobs_) {
52  hdiags_ += oops::Variables({prednames_.back() + "_" + std::to_string(job)});
53  }
54  } else {
55  hdiags_ += oops::Variables({prednames_.back()});
56  }
57  }
58  }
59 
60  if (prednames_.size() * jobs_.size() > 0) {
61  // Initialize the biascoeffs to ZERO
62  biascoeffs_.resize(prednames_.size() * jobs_.size());
63  std::fill(biascoeffs_.begin(), biascoeffs_.end(), 0.0);
64 
65  // Read or initialize bias coefficients
66  this->read(conf);
67  }
68 
69  oops::Log::trace() << "ObsBias::create done." << std::endl;
70 }
71 
72 // -----------------------------------------------------------------------------
73 
74 ObsBias::ObsBias(const ObsBias & other, const bool copy)
75  : odb_(other.odb_), conf_(other.conf_), predbases_(other.predbases_),
76  prednames_(other.prednames_), jobs_(other.jobs_),
77  geovars_(other.geovars_), hdiags_(other.hdiags_) {
78  oops::Log::trace() << "ObsBias::copy ctor starting." << std::endl;
79 
80  // Initialize the biascoeffs
81  biascoeffs_.resize(prednames_.size() * jobs_.size());
82  std::fill(biascoeffs_.begin(), biascoeffs_.end(), 0.0);
83 
84  // Copy the bias coeff data
85  if (copy && biascoeffs_.size() > 0) *this = other;
86 
87  oops::Log::trace() << "ObsBias::copy ctor done." << std::endl;
88 }
89 
90 // -----------------------------------------------------------------------------
91 
93  for (std::size_t jj = 0; jj < biascoeffs_.size(); ++jj)
94  biascoeffs_[jj] += dx[jj];
95  return *this;
96 }
97 
98 // -----------------------------------------------------------------------------
99 
101  if (rhs.size() > 0 && this->size() == rhs.size()) {
102  conf_ = rhs.conf_;
103  biascoeffs_ = rhs.biascoeffs_;
104  predbases_ = rhs.predbases_;
105  prednames_ = rhs.prednames_;
106  jobs_ = rhs.jobs_;
107  geovars_ = rhs.geovars_;
108  hdiags_ = rhs.hdiags_;
109  }
110  return *this;
111 }
112 
113 // -----------------------------------------------------------------------------
114 
115 void ObsBias::read(const eckit::Configuration & conf) {
116  oops::Log::trace() << "ObsBias::read and initialize from file, starting "<< std::endl;
117 
118  // Bias coefficients input file name
119  std::string input_filename;
120  if (conf.has("obs bias.abias_in")) {
121  input_filename = conf.getString("obs bias.abias_in");
122  oops::Log::debug() << "ObsBias::initialize coefficients from file: "
123  << input_filename << std::endl;
124 
125  // Default predictor names from GSI
126  // temporary solution, we should have a self-explanatory obsbias file
127  const std::vector<std::string> gsi_predictors = {"constant",
128  "zenith_angle",
129  "cloud_liquid_water",
130  "lapse_rate_order_2",
131  "lapse_rate",
132  "cosine_of_latitude_times_orbit_node",
133  "sine_of_latitude",
134  "emissivity",
135  "scan_angle_order_4",
136  "scan_angle_order_3",
137  "scan_angle_order_2",
138  "scan_angle"
139  };
140  std::ifstream infile(input_filename);
141 
142  // get the sensor id
143  std::string sensor = conf.getString("obs bias.sensor");
144  oops::Log::debug() << "ObsBias::initialize coefficients for sensor: "
145  << sensor << std::endl;
146 
147  std::size_t ich; // sequential number
148  std::string nusis; // sensor/instrument/satellite
149  std::size_t nuchan; // channel number
150  float tlap, tsum;
151  std::size_t ntlapupdate;
152 
153  if (infile.is_open())
154  {
155  oops::Log::debug() << "ObsBias:: prior file is opened" << std::endl;
156  float par;
157  while (infile >> ich)
158  {
159  infile >> nusis;
160  infile >> nuchan;
161  infile >> tlap;
162  infile >> tsum;
163  infile >> ntlapupdate;
164  if (nusis == sensor) {
165  auto ijob = std::find(jobs_.begin(), jobs_.end(), nuchan);
166  if (ijob != jobs_.end()) {
167  int j = std::distance(jobs_.begin(), ijob);
168 
169  for (auto & item : gsi_predictors) {
170  infile >> par;
171  auto ipred = std::find(prednames_.begin(), prednames_.end(), item);
172  if (ipred != prednames_.end()) {
173  int p = std::distance(prednames_.begin(), ipred);
174  biascoeffs_.at(j*prednames_.size() + p) = static_cast<double>(par);
175  }
176  }
177  } else {
178  for (auto & item : gsi_predictors)
179  infile >> par;
180  }
181  } else {
182  for (auto & item : gsi_predictors)
183  infile >> par;
184  }
185  }
186  infile.close();
187  oops::Log::debug() << "ObsBias:: read prior from " << input_filename << std::endl;
188  } else {
189  oops::Log::error() << "Unable to open file : " << input_filename << std::endl;
190  ABORT("Unable to open bias prior file ");
191  }
192  } else {
193  oops::Log::warning() << "ObsBias::prior file is NOT available, starting from ZERO"
194  << std::endl;
195  }
196 
197  oops::Log::trace() << "ObsBias::read and initilization done " << std::endl;
198 }
199 
200 // -----------------------------------------------------------------------------
201 
202 void ObsBias::write(const eckit::Configuration & conf) const {
203  oops::Log::trace() << "ObsBias::write to file not implemented" << std::endl;
204 
205  // Bias coefficients output file name
206  std::string output_filename;
207  if (conf.has("ObsBias.abias_out")) {
208  output_filename = conf.getString("ObsBias.abias_out");
209  }
210 
211  oops::Log::trace() << "ObsBias::write to file done" << std::endl;
212 }
213 
214 // -----------------------------------------------------------------------------
215 
216 void ObsBias::computeObsBias(ioda::ObsVector & ybias,
217  ObsDiagnostics & ydiags,
218  const std::vector<ioda::ObsVector> & predData) const {
219  oops::Log::trace() << "ObsBias::computeObsBias starting" << std::endl;
220 
221  if (this->size() > 0) {
222  const std::size_t nlocs = ybias.nlocs();
223  const std::size_t npreds = prednames_.size();
224  const std::size_t njobs = jobs_.size();
225 
226  ASSERT(biascoeffs_.size() == npreds*njobs);
227  ASSERT(predData.size() == npreds);
228  ASSERT(ybias.nvars() == njobs);
229 
230  /* predData memory layout (npreds X nlocs X njobs)
231  * Loc 0 1 2 3
232  * --------------------------
233  * pred1 Chan1 | 0 3 6 9
234  * Chan2 | 1 4 7 10
235  * .... | 2 5 8 11
236  *
237  * pred2 Chan1 |12 15 18 21
238  * Chan2 |13 16 19 22
239  * .... |14 17 20 23
240  */
241 
242  ybias.zero();
243 
244  /* ybias memory layout (nlocs X njobs)
245  * ch1 ch2 ch3 ch4
246  * Loc --------------------------
247  * 0 | 0 1 2 3
248  * 1 | 4 5 6 7
249  * 2 | 8 9 10 11
250  * 3 |12 13 14 15
251  * 4 |16 17 18 19
252  * ...|
253  */
254 
255  /* map bias coeff to eigen matrix npreds X njobs (read only)
256  * bias coeff memory layout (npreds X njobs)
257  * ch1 ch2 ch3 ch4
258  * --------------------------
259  * pred1 | 0 1 2 3
260  * pred2 | 4 5 6 7
261  * pred3 | 8 9 10 11
262  * .... |
263  */
264  Eigen::Map<const Eigen::MatrixXd> coeffs(biascoeffs_.data(), npreds, njobs);
265 
266  std::vector<double> biasTerm(nlocs, 0.0);
267  // For each channel: ( nlocs X 1 ) = ( nlocs X npreds ) * ( npreds X 1 )
268  for (std::size_t jch = 0; jch < njobs; ++jch) {
269  for (std::size_t jp = 0; jp < npreds; ++jp) {
270  // axpy
271  const double beta = coeffs(jp, jch);
272  for (std::size_t jl = 0; jl < nlocs; ++jl) {
273  biasTerm[jl] = predData[jp][jl*njobs+jch] * beta;
274  ybias[jl*njobs+jch] += biasTerm[jl];
275  }
276  // Save ObsBiasTerms (bias_coeff * predictor) for QC
277  const std::string varname = predbases_[jp]->name() + "_" + std::to_string(jobs_[jch]);
278  if (ydiags.has(varname)) {
279  ydiags.save(biasTerm, varname, 1);
280  } else {
281  oops::Log::error() << varname << " is not reserved in ydiags !" << std::endl;
282  ABORT("ObsBiasTerm variable is not reserved in ydiags");
283  }
284  }
285  }
286  }
287 
288  oops::Log::trace() << "ObsBias::computeObsBias done." << std::endl;
289 }
290 
291 // -----------------------------------------------------------------------------
292 std::vector<ioda::ObsVector> ObsBias::computePredictors(const GeoVaLs & geovals,
293  const ObsDiagnostics & ydiags) const {
294  const std::size_t npreds = predbases_.size();
295 
296  std::vector<ioda::ObsVector> predData(npreds, ioda::ObsVector(odb_));
297 
298  for (std::size_t p = 0; p < npreds; ++p) {
299  predbases_[p]->compute(odb_, geovals, ydiags, predData[p]);
300  predData[p].save(predbases_[p]->name() + "Predictor");
301  }
302 
303  oops::Log::trace() << "ObsBias::computePredictors done." << std::endl;
304  return predData;
305 }
306 
307 // -----------------------------------------------------------------------------
308 
309 double ObsBias::norm() const {
310  oops::Log::trace() << "ObsBias::norm starting." << std::endl;
311  double zz = 0.0;
312  for (std::size_t jj = 0; jj < biascoeffs_.size(); ++jj) {
313  zz += biascoeffs_[jj] * biascoeffs_[jj];
314  }
315  if (biascoeffs_.size() > 0) zz = std::sqrt(zz/biascoeffs_.size());
316  oops::Log::trace() << "ObsBias::norm done." << std::endl;
317  return zz;
318 }
319 
320 // -----------------------------------------------------------------------------
321 
322 void ObsBias::print(std::ostream & os) const {
323  if (this->size() > 0) {
324  // map bias coeffs to eigen matrix (writable)
325  Eigen::Map<const Eigen::MatrixXd>
326  coeffs(biascoeffs_.data(), prednames_.size(), jobs_.size());
327 
328  os << "ObsBias::print " << std::endl;
329  os << "---------------------------------------------------------------" << std::endl;
330  auto njobs = jobs_.size();
331  for (std::size_t p = 0; p < prednames_.size(); ++p) {
332  os << std::fixed << std::setw(20) << prednames_[p]
333  << ": Min= " << std::setw(15) << std::setprecision(8)
334  << coeffs.row(p).minCoeff()
335  << ", Max= " << std::setw(15) << std::setprecision(8)
336  << coeffs.row(p).maxCoeff()
337  << ", Norm= " << std::setw(15) << std::setprecision(8)
338  << coeffs.row(p).norm()
339  << std::endl;
340  }
341  os << "---------------------------------------------------------------" << std::endl;
342  os << "ObsBias::print done" << std::endl;
343  }
344 }
345 
346 // -----------------------------------------------------------------------------
347 
348 } // namespace ufo
ufo::ObsBias::geovars_
oops::Variables geovars_
Definition: ObsBias.h:89
ufo::ObsBias::prednames_
std::vector< std::string > prednames_
Definition: ObsBias.h:87
ufo::ObsBias::print
void print(std::ostream &) const
Definition: ObsBias.cc:322
ufo::ObsBias::ObsBias
ObsBias(ioda::ObsSpace &, const eckit::Configuration &)
Definition: ObsBias.cc:28
ObsBiasIncrement.h
ufo::ObsBias::conf_
eckit::LocalConfiguration conf_
Definition: ObsBias.h:83
ObsBias.h
ufo_radiancerttov_utils_mod::debug
logical, public debug
Definition: ufo_radiancerttov_utils_mod.F90:100
ufo::ObsBias::operator+=
ObsBias & operator+=(const ObsBiasIncrement &)
Definition: ObsBias.cc:92
ufo::ObsBias::write
void write(const eckit::Configuration &) const
Definition: ObsBias.cc:202
ufo::ObsBias::computePredictors
std::vector< ioda::ObsVector > computePredictors(const GeoVaLs &, const ObsDiagnostics &) const
Definition: ObsBias.cc:292
ufo::ObsBias::jobs_
std::vector< int > jobs_
Definition: ObsBias.h:88
ufo::ObsDiagnostics::has
bool has(const std::string &var) const
Definition: src/ufo/ObsDiagnostics.h:49
ufo
Definition: RunCRTM.h:27
ufo::ObsBias::hdiags_
oops::Variables hdiags_
Definition: ObsBias.h:90
ufo::ObsBias::size
std::size_t size() const
Definition: ObsBias.h:59
ufo::ObsBias
Class to handle observation bias parameters.
Definition: ObsBias.h:44
ufo::ObsDiagnostics
Definition: src/ufo/ObsDiagnostics.h:35
ufo::ObsDiagnostics::save
void save(const std::vector< double > &, const std::string &, const int)
Definition: ObsDiagnostics.cc:37
ufo::ObsBias::norm
double norm() const
Definition: ObsBias.cc:309
ufo::ObsBias::operator=
ObsBias & operator=(const ObsBias &)
Definition: ObsBias.cc:100
ufo::GeoVaLs
GeoVaLs: geophysical values at locations.
Definition: src/ufo/GeoVaLs.h:39
ufo::ObsBiasIncrement
Definition: ObsBiasIncrement.h:39
ufo::ObsBias::predbases_
std::vector< std::shared_ptr< PredictorBase > > predbases_
Definition: ObsBias.h:86
ufo::ObsBias::computeObsBias
void computeObsBias(ioda::ObsVector &, ObsDiagnostics &, const std::vector< ioda::ObsVector > &) const
Definition: ObsBias.cc:216
ufo::PredictorFactory::create
static PredictorBase * create(const eckit::Configuration &, const std::vector< int > &)
Definition: PredictorBase.cc:39
ufo::TrackCheckUtils::distance
float distance(const Point &a, const Point &b)
Returns the distance between the two cartesian-mapped Point arguments
Definition: TrackCheckUtils.cc:51
ufo::ObsBias::odb_
ioda::ObsSpace & odb_
Definition: ObsBias.h:82
ufo::ObsBias::biascoeffs_
std::vector< double > biascoeffs_
Definition: ObsBias.h:85
ufo::ObsBias::read
void read(const eckit::Configuration &)
Definition: ObsBias.cc:115
conf
Definition: conf.py:1