UFO
ObsBiasCovariance.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2018-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 <Eigen/Core>
9 #include <cmath>
10 #include <fstream>
11 #include <memory>
12 #include <random>
13 #include <set>
14 
15 #include "ufo/ObsBiasCovariance.h"
16 
17 #include "ioda/ObsSpace.h"
18 #include "ioda/ObsVector.h"
19 
20 #include "oops/util/IntSetParser.h"
21 #include "oops/util/Logger.h"
22 #include "oops/util/Random.h"
23 
24 #include "ufo/ObsBias.h"
25 #include "ufo/ObsBiasIncrement.h"
27 
28 namespace ufo {
29 
30 // -----------------------------------------------------------------------------
31 
32 ObsBiasCovariance::ObsBiasCovariance(ioda::ObsSpace & odb, const eckit::Configuration & conf)
33  : conf_(conf), odb_(odb), prednames_(0), jobs_(0), variances_(0), preconditioner_(0),
34  ht_rinv_h_(0), obs_num_(0), analysis_variances_(0), minimal_required_obs_number_(0) {
35  oops::Log::trace() << "ObsBiasCovariance::Constructor starting" << std::endl;
36 
37  // Get the jobs(channels)
38  if (conf_.has("obs bias.jobs")) {
39  const std::set<int> jobs = oops::parseIntSet(conf_.getString("obs bias.jobs"));
40  jobs_.assign(jobs.begin(), jobs.end());
41  }
42 
43  // Predictor factory
44  if (conf_.has("obs bias.predictors")) {
45  std::vector<eckit::LocalConfiguration> confs;
46  conf_.get("obs bias.predictors", confs);
47  for (std::size_t j = 0; j < confs.size(); ++j) {
48  std::shared_ptr<PredictorBase> pred(PredictorFactory::create(confs[j], jobs_));
49  prednames_.push_back(pred->name());
50  }
51  }
52 
53  if (prednames_.size()*jobs_.size() > 0) {
54  // Get the minimal required filtered obs number
56  conf_.getUnsigned("obs bias.covariance.minimal required obs number");
57 
58  // Override the variance range if provided
59  if (conf_.has("obs bias.covariance.variance range")) {
60  const std::vector<double>
61  range = conf_.getDoubleVector("obs bias.covariance.variance range");
62  ASSERT(range.size() == 2);
63  smallest_variance_ = range[0];
64  largest_variance_ = range[1];
65  }
66 
67  // Override the preconditioning step size if provided
68  if (conf_.has("obs bias.covariance.step size")) {
69  step_size_ = conf_.getDouble("obs bias.covariance.step size");
70  }
71 
72  // Override the largest analysis variance if provided
73  if (conf_.has("obs bias.covariance.largest analysis variance")) {
75  conf_.getDouble("obs bias.covariance.largest analysis variance");
76  }
77 
78  // Initialize the variances to upper limit
79  variances_.resize(prednames_.size() * jobs_.size());
80  std::fill(variances_.begin(), variances_.end(), largest_variance_);
81 
82  // Initialize the hessian contribution to zero
83  ht_rinv_h_.resize(prednames_.size() * jobs_.size());
84  std::fill(ht_rinv_h_.begin(), ht_rinv_h_.end(), 0.0);
85 
86  // Initialize the preconditioner to default step size
87  preconditioner_.resize(prednames_.size() * jobs_.size());
88  std::fill(preconditioner_.begin(), preconditioner_.end(), step_size_);
89 
90  // Initialize obs_num_ to ZERO
91  obs_num_.resize(jobs_.size());
92  std::fill(obs_num_.begin(), obs_num_.end(), 0);
93 
94  // Initialize analysis error variances to the upper limit
95  analysis_variances_.resize(prednames_.size() * jobs_.size());
97 
98  // Initializes from given prior
99  if (conf_.has("obs bias.covariance.prior")) {
100  // Get default inflation ratio
101  const double inflation_ratio =
102  conf.getDouble("obs bias.covariance.prior.inflation.ratio");
103 
104  // Check the large inflation ratio when obs number < minimal_required_obs_number
105  const double large_inflation_ratio =
106  conf.getDouble("obs bias.covariance.prior.inflation.ratio for small dataset");
107 
108  // read in Variances prior (analysis_variances_) and number of obs. (obs_num_)
109  // from previous cycle
110  this->read(conf_);
111 
112  // set variances for bias predictor coeff. based on diagonal info
113  // of previous analysis error variance
114  std::size_t ii;
115  for (std::size_t j = 0; j < jobs_.size(); ++j) {
116  const double inflation = (obs_num_[j] <= minimal_required_obs_number_) ?
117  large_inflation_ratio : inflation_ratio;
118  for (std::size_t p = 0; p < prednames_.size(); ++p) {
119  ii = j*prednames_.size() + p;
120  if (inflation > inflation_ratio)
122  variances_[ii] = inflation * analysis_variances_[ii] + smallest_variance_;
126  }
127  }
128  }
129  }
130 
131  oops::Log::trace() << "ObsBiasCovariance::Constructor is done" << std::endl;
132 }
133 
134 // -----------------------------------------------------------------------------
135 
136 void ObsBiasCovariance::read(const eckit::Configuration & conf) {
137  oops::Log::trace() << "ObsBiasCovariance::read from file " << std::endl;
138 
139  // Default predictor names from GSI
140  const std::vector<std::string> gsi_predictors = {"constant",
141  "zenith_angle",
142  "cloud_liquid_water",
143  "lapse_rate_order_2",
144  "lapse_rate",
145  "cosine_of_latitude_times_orbit_node",
146  "sine_of_latitude",
147  "emissivity",
148  "scan_angle_order_4",
149  "scan_angle_order_3",
150  "scan_angle_order_2",
151  "scan_angle"
152  };
153 
154  const std::string sensor = conf.getString("obs bias.sensor");
155 
156  if (conf.has("obs bias.covariance.prior.datain")) {
157  const std::string filename = conf.getString("obs bias.covariance.prior.datain");
158  std::ifstream infile(filename);
159 
160  if (infile.is_open())
161  {
162  int ich; // sequential number
163  std::string nusis; // sensor/instrument/satellite
164  int nuchan; // channel number
165  float number; // QCed obs number from previous cycle
166 
167  float par;
168  while (infile >> ich)
169  {
170  infile >> nusis;
171  infile >> nuchan;
172  infile >> number;
173  if (nusis == sensor) {
174  auto ijob = std::find(jobs_.begin(), jobs_.end(), nuchan);
175  if (ijob != jobs_.end()) {
176  int j = std::distance(jobs_.begin(), ijob);
177  obs_num_[j] = static_cast<int>(number);
178 
179  for (auto & item : gsi_predictors) {
180  infile >> par;
181  auto ipred = std::find(prednames_.begin(), prednames_.end(), item);
182  if (ipred != prednames_.end()) {
183  int p = std::distance(prednames_.begin(), ipred);
184  analysis_variances_.at(j*prednames_.size() + p) = static_cast<double>(par);
185  }
186  }
187  } else {
188  for (auto & item : gsi_predictors)
189  infile >> par;
190  }
191  } else {
192  for (auto & item : gsi_predictors)
193  infile >> par;
194  }
195  }
196  infile.close();
197  oops::Log::trace() << "ObsBiasCovariance::read from prior file: "
198  << filename << " Done " << std::endl;
199  } else {
200  oops::Log::error() << "Unable to open file : " << filename << std::endl;
201  ABORT("Unable to open bias correction coeffs variance prior file ");
202  }
203  }
204 
205  oops::Log::trace() << "ObsBiasCovariance::read is done " << std::endl;
206 }
207 
208 // -----------------------------------------------------------------------------
209 
210 void ObsBiasCovariance::write(const eckit::Configuration & conf) {
211  oops::Log::trace() << "ObsBiasCovariance::write to file " << std::endl;
212  oops::Log::trace() << "ObsBiasCovariance::write is not implemented " << std::endl;
213  oops::Log::trace() << "ObsBiasCovariance::write is done " << std::endl;
214 }
215 
216 // -----------------------------------------------------------------------------
217 
218 void ObsBiasCovariance::linearize(const ObsBias & bias, const eckit::Configuration & innerConf) {
219  oops::Log::trace() << "ObsBiasCovariance::linearize starts" << std::endl;
220  if (bias) {
221  // Retrieve the QC flags and do statistics from second outer loop
222  const int jouter = innerConf.getInt("iteration");
223  if (jouter >= 1) {
224  // Reset the number of filtered Obs.
225  std::fill(obs_num_.begin(), obs_num_.end(), 0);
226 
227  // Retrieve the QC flags of previous outer loop and get the number of effective obs.
228  const std::string group_name = "EffectiveQC" + std::to_string(jouter-1);
229  const std::vector<std::string> vars = odb_.obsvariables().variables();
230  std::vector<int> qc_flags(odb_.nlocs(), 999);
231  for (std::size_t j = 0; j < vars.size(); ++j) {
232  if (odb_.has(group_name , vars[j])) {
233  odb_.get_db(group_name, vars[j], qc_flags);
234  obs_num_[j] = std::count(qc_flags.begin(), qc_flags.end(), 0);
235  } else {
236  throw eckit::UserError("Unable to find QC flags : " + vars[j] + "@" + group_name);
237  }
238  }
239 
240  // Sum across the processros
241  if (odb_.isDistributed())
242  odb_.comm().allReduceInPlace(obs_num_.begin(), obs_num_.end(),
243  eckit::mpi::sum());
244 
245  const float missing = util::missingValue(missing);
246 
247  // compute the hessian contribution from Jo bias terms channel by channel
248  // retrieve the effective error (after QC) for this channel
249  ioda::ObsVector r_inv(odb_, "EffectiveError", true);
250 
251  // compute \mathrm{R}^{-1}
252  std::size_t nvars = r_inv.nvars();
253  for (size_t vv = 0; vv < nvars; ++vv) {
254  for (size_t ii = 0; ii < r_inv.nlocs(); ++ii) {
255  if (r_inv[ii*nvars + vv] != missing) {
256  r_inv[ii*nvars + vv] = 1.0f / pow(r_inv[ii*nvars + vv], 2);
257  } else {
258  r_inv[ii*nvars + vv] = 0.0f;
259  }
260  }
261  }
262 
263  // Sum the total number of effective obs. across tasks
264  if (odb_.isDistributed())
265  odb_.comm().allReduceInPlace(obs_num_.begin(), obs_num_.end(), eckit::mpi::sum());
266 
267  // compute \mathrm{H}_\beta^\intercal \mathrm{R}^{-1} \mathrm{H}_\beta
268  // -----------------------------------------
269  std::fill(ht_rinv_h_.begin(), ht_rinv_h_.end(), 0.0);
270  for (std::size_t p = 0; p < prednames_.size(); ++p) {
271  // retrieve the predictors
272  const ioda::ObsVector predx(odb_, prednames_[p] + "Predictor", true);
273 
274  // for each variable
275  ASSERT(r_inv.nlocs() == predx.nlocs());
276  std::size_t nvars = predx.nvars();
277  // only keep the diagnoal
278  for (size_t vv = 0; vv < nvars; ++vv) {
279  for (size_t ii = 0; ii < predx.nlocs(); ++ii)
280  ht_rinv_h_[vv*prednames_.size() + p] +=
281  pow(predx[ii*nvars + vv], 2) * r_inv[ii*nvars + vv];
282  }
283  }
284 
285  // Sum the hessian contributions across the tasks
286  if (odb_.isDistributed())
287  odb_.comm().allReduceInPlace(ht_rinv_h_.begin(), ht_rinv_h_.end(), eckit::mpi::sum());
288  }
289 
290  // reset variances for bias predictor coeff. based on current data count
291  for (std::size_t j = 0; j < jobs_.size(); ++j) {
293  for (std::size_t p = 0; p < prednames_.size(); ++p)
294  variances_[j*prednames_.size() + p] = smallest_variance_;
295  }
296  }
297 
298  // set a coeff. factor for variances of control variables
299  std::size_t index;
300  for (std::size_t j = 0; j < jobs_.size(); ++j) {
301  for (std::size_t p = 0; p < prednames_.size(); ++p) {
302  index = j*prednames_.size() + p;
303  preconditioner_[index] = step_size_;
304  // L = \mathrm{A}^{-1}
305  if (obs_num_[j] > 0)
306  preconditioner_[index] = 1.0 / (1.0 / variances_[index] + ht_rinv_h_[index]);
308  if (ht_rinv_h_[index] > 0.0) {
309  analysis_variances_[index] = 1.0 / (1.0 / variances_[index] + ht_rinv_h_[index]);
310  } else {
312  }
313  }
314  }
315  }
316  }
317  oops::Log::trace() << "ObsBiasCovariance::linearize is done" << std::endl;
318 }
319 
320 // -----------------------------------------------------------------------------
321 
323  ObsBiasIncrement & dx2) const {
324  oops::Log::trace() << "ObsBiasCovariance::multiply starts" << std::endl;
325 
326  for (std::size_t jj = 0; jj < variances_.size(); ++jj) {
327  dx2[jj] = dx1[jj] * variances_[jj];
328  }
329 
330  oops::Log::trace() << "ObsBiasCovariance::multiply is done" << std::endl;
331 }
332 
333 // -----------------------------------------------------------------------------
334 
336  ObsBiasIncrement & dx2) const {
337  oops::Log::trace() << "ObsBiasCovariance::inverseMultiply starts" << std::endl;
338 
339  for (std::size_t jj = 0; jj < variances_.size(); ++jj) {
340  dx2[jj] = dx1[jj] / variances_[jj];
341  }
342 
343  oops::Log::trace() << "ObsBiasCovariance::inverseMultiply is done" << std::endl;
344 }
345 
346 // -----------------------------------------------------------------------------
347 
349  oops::Log::trace() << "ObsBiasCovariance::randomize starts" << std::endl;
350  if (dx) {
351  static util::NormalDistribution<double> dist(variances_.size());
352  for (std::size_t jj = 0; jj < variances_.size(); ++jj) {
353  dx[jj] = dist[jj] * std::sqrt(variances_[jj]);
354  }
355  }
356  oops::Log::trace() << "ObsBiasCovariance::randomize is done" << std::endl;
357 }
358 
359 // -----------------------------------------------------------------------------
360 
361 } // namespace ufo
ufo::ObsBiasCovariance::prednames_
std::vector< std::string > prednames_
Definition: ObsBiasCovariance.h:92
ufo::ObsBiasCovariance::randomize
void randomize(ObsBiasIncrement &) const
Definition: ObsBiasCovariance.cc:348
PredictorBase.h
ufo::ObsBiasCovariance::odb_
ioda::ObsSpace & odb_
Definition: ObsBiasCovariance.h:60
ObsBiasIncrement.h
ObsBias.h
ufo::ObsBiasCovariance::ht_rinv_h_
std::vector< double > ht_rinv_h_
Definition: ObsBiasCovariance.h:63
ufo::ObsBiasCovariance::smallest_variance_
double smallest_variance_
Definition: ObsBiasCovariance.h:81
ufo::ObsBiasCovariance::obs_num_
std::vector< std::size_t > obs_num_
Definition: ObsBiasCovariance.h:69
ufo::ObsBiasCovariance::minimal_required_obs_number_
std::size_t minimal_required_obs_number_
Definition: ObsBiasCovariance.h:72
ufo::ObsBiasCovariance::write
void write(const eckit::Configuration &)
Definition: ObsBiasCovariance.cc:210
ufo
Definition: RunCRTM.h:27
ufo::ObsBiasCovariance::ObsBiasCovariance
ObsBiasCovariance(ioda::ObsSpace &, const eckit::Configuration &)
Definition: ObsBiasCovariance.cc:32
ufo::ObsBiasCovariance::analysis_variances_
std::vector< double > analysis_variances_
Definition: ObsBiasCovariance.h:75
ObsBiasCovariance.h
ufo::ObsBiasCovariance::read
void read(const eckit::Configuration &)
Definition: ObsBiasCovariance.cc:136
ufo::ObsBias
Class to handle observation bias parameters.
Definition: ObsBias.h:44
ufo::ObsBiasCovariance::linearize
void linearize(const ObsBias &, const eckit::Configuration &)
Definition: ObsBiasCovariance.cc:218
ufo::ObsBiasCovariance::largest_analysis_variance_
double largest_analysis_variance_
Definition: ObsBiasCovariance.h:87
ufo::QCflags::missing
constexpr int missing
Definition: QCflags.h:15
ufo::ObsBiasCovariance::variances_
std::vector< double > variances_
Definition: ObsBiasCovariance.h:78
ufo::ObsBiasCovariance::step_size_
double step_size_
Definition: ObsBiasCovariance.h:90
ufo::ObsBiasCovariance::jobs_
std::vector< int > jobs_
Definition: ObsBiasCovariance.h:93
ufo::ObsBiasIncrement
Definition: ObsBiasIncrement.h:39
ufo::ObsBiasCovariance::largest_variance_
double largest_variance_
Definition: ObsBiasCovariance.h:84
ufo::ObsBiasCovariance::multiply
void multiply(const ObsBiasIncrement &, ObsBiasIncrement &) const
Definition: ObsBiasCovariance.cc:322
ufo::PredictorFactory::create
static PredictorBase * create(const eckit::Configuration &, const std::vector< int > &)
Definition: PredictorBase.cc:39
ufo::ObsBiasCovariance::preconditioner_
std::vector< double > preconditioner_
Definition: ObsBiasCovariance.h:66
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::ObsBiasCovariance::inverseMultiply
void inverseMultiply(const ObsBiasIncrement &, ObsBiasIncrement &) const
Definition: ObsBiasCovariance.cc:335
conf
Definition: conf.py:1
ufo::ObsBiasCovariance::conf_
const eckit::LocalConfiguration conf_
Definition: ObsBiasCovariance.h:59