IODA Bundle
SatBiasConverter.cpp
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 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 <memory>
9 #include <string>
10 #include <vector>
11 
12 #include <Eigen/Dense>
13 
14 #include "eckit/config/LocalConfiguration.h"
15 #include "eckit/config/YAMLConfiguration.h"
16 #include "eckit/exception/Exceptions.h"
17 #include "eckit/filesystem/PathName.h"
18 
19 #include "ioda/ObsGroup.h"
20 #include "ioda/Engines/HH.h"
21 
22 #include "oops/util/missingValues.h"
23 
24 #include "GsiSatBiasReader.h"
25 
26 // Return ObsGroup with bias coefficients for a given sensor
28  const std::string & coeffile, const std::string & errfile,
29  const std::string & sensor,
30  const std::vector<std::string> & predictors,
31  const size_t nchannels) {
32  // Channels & predictors
33  std::vector<int> channels(nchannels), channels_in_errorfile(nchannels);
34  long numPreds = predictors.size();
35  long numChans = channels.size();
36 
37  // Allocate space for bias coefficients and read them from GSI satbias file
38  Eigen::ArrayXXf biascoeffs(numPreds, numChans);
39  Eigen::ArrayXXf biascoefferrs(numPreds, numChans);
40  Eigen::ArrayXf nobs(numChans);
41  readObsBiasCoefficients(coeffile, sensor, channels, biascoeffs);
42  readObsBiasCoeffErrors(errfile, sensor, channels_in_errorfile, biascoefferrs, nobs);
43  ASSERT(channels == channels_in_errorfile);
44 
45  // Creating dimensions: npredictors & nchannels
47  ioda::NewDimensionScale<int>("npredictors", numPreds),
48  ioda::NewDimensionScale<int>("nchannels", numChans)
49  };
50 
51  // Construct an ObsGroup object, with 2 dimensions npred, nchans
52  ioda::ObsGroup ogrp = ioda::ObsGroup::generate(empty_base_object, newDims);
53 
54  // Save the predictors and the channels values
55  ioda::Variable predsVar = ogrp.vars.createWithScales<std::string>(
56  "predictors", {ogrp.vars["npredictors"]});
57  predsVar.write(predictors);
58  ioda::Variable chansVar = ogrp.vars.createWithScales<int>("channels", {ogrp.vars["nchannels"]});
59  chansVar.write(channels);
60 
61  // Set up the creation parameters for the bias coefficients variable
63  float_params.chunk = true; // allow chunking
64  float_params.compressWithGZIP(); // compress using gzip
65  float missing_value = util::missingValue(missing_value);
66  float_params.setFillValue<float>(missing_value);
67 
68  // Create a variable for bias coefficients, save bias coeffs to the variable
69  ioda::Variable biasVar = ogrp.vars.createWithScales<float>("bias_coefficients",
70  {ogrp.vars["npredictors"], ogrp.vars["nchannels"]}, float_params);
71  biasVar.writeWithEigenRegular(biascoeffs);
72 
73  // Create a variable for bias coefficient error variances
74  ioda::Variable biaserrVar = ogrp.vars.createWithScales<float>("bias_coeff_errors",
75  {ogrp.vars["npredictors"], ogrp.vars["nchannels"]}, float_params);
76  biaserrVar.writeWithEigenRegular(biascoefferrs);
77 
78  // Create a variable for number of obs (used in the bias coeff error covariance)
79  ioda::Variable nobsVar = ogrp.vars.createWithScales<float>("number_obs_assimilated",
80  {ogrp.vars["nchannels"]}, float_params);
81  nobsVar.writeWithEigenRegular(nobs);
82  return ogrp;
83 }
84 
85 int main(int argc, char** argv) {
86  /// Open yaml with configuration for this converter
87  ASSERT(argc >= 2);
88  eckit::PathName configfile = argv[1];
89  eckit::YAMLConfiguration config(configfile);
90  const std::string coeffile = config.getString("input coeff file");
91  const std::string errfile = config.getString("input err file");
92 
93  std::vector<std::string> sensors;
94  std::vector<int> nchannels;
95 
96  /// Find all sensors and number of channels in the GSI bias coefficients file
97  findSensorsChannels(coeffile, sensors, nchannels);
98 
99  std::cout << "Found " << sensors.size() << " sensors:" << std::endl;
100  for (size_t jj = 0; jj < sensors.size(); ++jj) {
101  std::cout << "-- " << sensors[jj] << ", " << nchannels[jj] << " channels." << std::endl;
102  }
103 
104  std::vector<eckit::LocalConfiguration> configs = config.getSubConfigurations("output");
105  for (size_t jj = 0; jj < configs.size(); ++jj) {
106  const std::string sensor = configs[jj].getString("sensor");
107  const std::string output_filename = configs[jj].getString("output file");
108  const std::vector<std::string> predictors = configs[jj].getStringVector("predictors");
109  if (predictors.size() != gsi_npredictors) {
110  const std::string error = "Number of predictors specified in yaml must be " +
111  std::to_string(gsi_npredictors) + " (same as number of predictors in GSI satinfo)";
112  throw eckit::BadValue(error, Here());
113  }
114  auto it = find(sensors.begin(), sensors.end(), sensor);
115  if (it != sensors.end()) {
116  int index = it - sensors.begin();
117  ioda::Group group = ioda::Engines::HH::createFile(output_filename,
119  makeObsBiasObject(group, coeffile, errfile, sensor, predictors, nchannels[index]);
120  } else {
121  const std::string error = "No " + sensor + " sensor in the input file";
122  throw eckit::BadValue(error, Here());
123  }
124  }
125  return 0;
126 }
void readObsBiasCoefficients(const std::string &filename, const std::string &sensor, std::vector< int > &channels, Eigen::ArrayXXf &coeffs)
void findSensorsChannels(const std::string &filename, std::vector< std::string > &sensors, std::vector< int > &nchannels)
void readObsBiasCoeffErrors(const std::string &filename, const std::string &sensor, std::vector< int > &channels, Eigen::ArrayXXf &errs, Eigen::ArrayXf &nobs)
constexpr size_t gsi_npredictors
Number of predictors in GSI satbias file.
HDF5 engine.
Interfaces for ioda::ObsGroup and related classes.
int main(int argc, char **argv)
ioda::ObsGroup makeObsBiasObject(ioda::Group &empty_base_object, const std::string &coeffile, const std::string &errfile, const std::string &sensor, const std::vector< std::string > &predictors, const size_t nchannels)
Groups are a new implementation of ObsSpaces.
Definition: Group.h:159
An ObsGroup is a specialization of a ioda::Group. It provides convenience functions and guarantees th...
Definition: ObsGroup.h:32
static ObsGroup generate(Group &emptyGroup, const NewDimensionScales_t &fundamentalDims, std::shared_ptr< const detail::DataLayoutPolicy > layout=nullptr)
Create an empty ObsGroup and populate it with the fundamental dimensions.
Definition: ObsGroup.cpp:72
Has_Variables vars
Use this to access variables.
Definition: Group.h:123
Variable createWithScales(const std::string &name, const std::vector< Variable > &dimension_scales, const VariableCreationParameters &params=VariableCreationParameters::defaulted< DataType >())
Convenience function to create a Variable from certain dimension scales.
Variable_Implementation writeWithEigenRegular(const EigenClass &d, const Selection &mem_selection=Selection::all, const Selection &file_selection=Selection::all)
Write an Eigen object (a Matrix, an Array, a Block, a Map).
virtual Variable write(gsl::span< char > data, const Type &in_memory_dataType, const Selection &mem_selection=Selection::all, const Selection &file_selection=Selection::all)
The fundamental write function. Backends overload this function to implement all write operations.
Definition: Variable.cpp:317
IODA_DL Group createFile(const std::string &filename, BackendCreateModes mode, HDF5_Version_Range compat=defaultVersionRange())
Create a ioda::Group backed by an HDF5 file.
Definition: HH.cpp:120
@ Truncate_If_Exists
If the file already exists, overwrite it.
list newDims
Definition: 05-ObsGroup.py:95
std::vector< std::shared_ptr< NewDimensionScale_Base > > NewDimensionScales_t
Used to specify Variable creation-time properties.
Definition: Has_Variables.h:57
VariableCreationParameters & setFillValue(DataType fill)
Definition: Has_Variables.h:69
bool chunk
Do we chunk this variable? Required for extendible / compressible Variables.
Definition: Has_Variables.h:84