UFO
rttovcpp_interface.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2017-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 
9 
10 #include <ostream>
11 #include <string>
12 #include <vector>
13 
14 #include "ioda/ObsSpace.h"
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 #include "oops/util/missingValues.h"
21 
22 #include "ufo/GeoVaLs.h"
23 #include "ufo/ObsBias.h"
24 #include "ufo/ObsDiagnostics.h"
25 
26 #include "rttov/wrapper/Profile.h"
27 #include "rttov/wrapper/RttovSafe.h"
28 
29 namespace ioda {
30  class ObsSpace;
31  class ObsVector;
32 }
33 
34 namespace ufo {
35  class GeoVaLs;
36  class ObsDiagnostics;
37 }
38 
39 namespace ufo {
40 
41 // -----------------------------------------------------------------------------
42 /*! \brief Set one single RttovSafe object for a single sensor
43 *
44 * \details **rttovcpp_interface()** loads rttov coef files, set profiles and
45 * surface emissivity/reflectance, call Jacobian function, and perform some quality
46 * control. It is called by both simulateObs() and setTrajectory().
47 *
48 * \param[in] geovals reference to the input full model state at observation locations
49 * \param[in] odb_ reference to the input observational information
50 * \param[in] CoefFileName rttov coef file to be loaded
51 * \param[in] channels_ indexes for a subset of channels for a single sensor
52 * \param[out] nlevels number of model vertical levels
53 * \param[out] aRttov_ reference to the output rttov object
54 * \param[out] skip_profile logical to determine if a profile is used or not
55 *
56 * \author Zhiquan (Jake) Liu (NCAR/MMM), initial version for clear-sky DA
57 *
58 * \date Feb. 2021, initial version
59 *
60 */
61 void rttovcpp_interface(const GeoVaLs & geovals, const ioda::ObsSpace & odb_,
62  rttov::RttovSafe & aRttov_, const std::string CoefFileName,
63  const std::vector<int> channels_, std::size_t & nlevels,
64  std::vector<bool> & skip_profile) {
65  // 1. Set options for a RttovSafe instance:
66  //-----------------------------------------------
67  // 1.1 general setting for all sensors: clear-sky
68 
69  aRttov_.setFileCoef(CoefFileName);
70  aRttov_.options.setAddInterp(true); // input P differ from coef file levels
71  aRttov_.options.setVerboseWrapper(true); // more output info
72  aRttov_.options.setCO2Data(false);
73  aRttov_.options.setStoreRad(true);
74  aRttov_.options.setDoCheckinput(false); // turn this off, or many failing profiles?
75  aRttov_.options.setSwitchrad(true); // use radiance_k%bt(:)=1 input perturbation
76 
77  // 1.2 for Microwave Sensors
78  aRttov_.options.setFastemVersion(6); // emissivity over sea
79  aRttov_.options.setSupplyFoamFraction(false);
80  aRttov_.options.setApplyBandCorrection(true);
81 
82  // 1.3 Load coef for subset channels of an instrument
83  //-------------------------------------------------
84  try {
85  aRttov_.loadInst(channels_);
86  }
87  catch (std::exception& e) {
88  oops::Log::error() << "Error loading instrument " << e.what() << std::endl;
89  }
90 
91  oops::Log::info() << "ObsRadianceRTTOVCPP channels: " << channels_ << std::endl;
92 
93  // 2. Allocate profiles
94  //---------------------------------------------------------------------------------
95  nlevels = geovals.nlevs("air_temperature"); // set private data member
96  std::size_t nprofiles = odb_.nlocs();
97  std::size_t nchannels = aRttov_.getNchannels();
98 
99  std::vector <rttov::Profile> profiles; // RTTOV Profile object
100  for (std::size_t p = 0; p < nprofiles; p++) {
101  rttov::Profile aProfile(nlevels);
102  profiles.push_back(aProfile);
103  }
104 
105  // global scope for it accessable by reflectance* cos(sunzen)
106  std::vector<double> sunzen(nprofiles, 0.0);
107 
108  // 3. Populate the profiles object
109  //---------------------------------------------------------------------------------
110  try {
111  std::vector<double> tmpvar1d(nlevels, 0.0); // one single vertical profile
112  std::vector<double> tmpvar2d(nprofiles, 0.0); // one single level field
113 
114  // 3.1 Common 3D fields needed
115  //----------------------------------------------------
116  // 3.1.1 Retrieve pressure in hPa
117  std::vector<std::vector<double>> tmpvar3d; // [nlevels][nprofiles]
118  for (std::size_t i = 0; i < nlevels; ++i) {
119  geovals.getAtLevel(tmpvar2d, "air_pressure", i); // get one level P
120  tmpvar3d.push_back(tmpvar2d); // push one level P into 3D P
121  }
122  for (std::size_t i = 0; i < nprofiles; ++i) {
123  for (std::size_t k = 0; k < nlevels; ++k) {
124  // get one vertical profile, rttov level index is from top to bottom
125  tmpvar1d[k] = tmpvar3d[k][i]*0.01;
126  }
127  profiles[i].setP(tmpvar1d);
128  }
129  // release memory of tmpvar3d variable
130  std::vector<std::vector<double>>().swap(tmpvar3d);
131 
132  // 3.1.2 Retrieve temperature in K
133  std::vector<std::vector<double>> tmpvar3d_T; // [nlevels][nprofiles]
134  for (std::size_t i = 0; i < nlevels; ++i) {
135  geovals.getAtLevel(tmpvar2d, "air_temperature", i); // get one level T
136  tmpvar3d_T.push_back(tmpvar2d); // push one level T into 3D T
137  }
138  for (std::size_t i = 0; i < nprofiles; ++i) {
139  for (std::size_t k = 0; k < nlevels; ++k) {
140  tmpvar1d[k] = tmpvar3d_T[k][i];
141  }
142  profiles[i].setT(tmpvar1d);
143  }
144  std::vector<std::vector<double>>().swap(tmpvar3d_T);
145 
146  // 3.1.3 Retrieve specific humidity in kg/kg
147  std::vector<std::vector<double>> tmpvar3d_Q; // [nlevels][nprofiles]
148  for (std::size_t i = 0; i < nlevels; ++i) {
149  geovals.getAtLevel(tmpvar2d, "specific_humidity", i);
150  tmpvar3d_Q.push_back(tmpvar2d);
151  }
152  for (std::size_t i = 0; i < nprofiles; ++i) {
153  profiles[i].setGasUnits(rttov::kg_per_kg);
154  for (std::size_t k = 0; k < nlevels; ++k) {
155  tmpvar1d[k] = tmpvar3d_Q[k][i];
156  }
157  profiles[i].setQ(tmpvar1d);
158  }
159  std::vector<std::vector<double>>().swap(tmpvar3d_Q);
160 
161  // 3.2 2D surface fields at obs locations
162  //-------------------------------------------
163  std::vector<double> ps(nprofiles, 0.0);
164  std::vector<double> t2m(nprofiles, 0.0);
165  std::vector<double> q2m(nprofiles, 0.0);
166  std::vector<double> u10(nprofiles, 0.0);
167  std::vector<double> v10(nprofiles, 0.0);
168  std::vector<double> tskin(nprofiles, 0.0);
169  std::vector<int> landmask(nprofiles); // 1: land, 0:ocean
170  std::vector<double> seaice_frac(nprofiles, 0.0);
171 
172  // Retrieve surface variables
173  geovals.get(ps, "surface_pressure"); // in Pa, get one level Ps
174  geovals.get(t2m, "surface_temperature"); // Kelvin
175  geovals.get(q2m, "specific_humidity_at_two_meters_above_surface"); // kg/kg
176  geovals.get(u10, "uwind_at_10m");
177  geovals.get(v10, "vwind_at_10m");
178  geovals.get(tskin, "skin_temperature"); // Kelvin
179  geovals.get(landmask, "landmask"); // 1: land, 0:ocean
180  geovals.get(seaice_frac, "seaice_fraction");
181 
182  // 3.3 Obs metadata
183  //-----------------------------------------------
184  std::vector<double> satzen(nprofiles, 0.0); // always needed
185  std::vector<double> satazi(nprofiles, 0.0); // not always needed
186  std::vector<double> sunazi(nprofiles, 0.0); // not always needed
187  std::vector<double> lat(nprofiles, 0.0);
188  std::vector<double> lon(nprofiles, 0.0);
189  std::vector<double> elev(nprofiles, 0.0);
190  std::vector<util::DateTime> times(nprofiles);
191 
192  odb_.get_db("MetaData", "sensor_zenith_angle", satzen); // in degree
193  odb_.get_db("MetaData", "sensor_azimuth_angle", satazi); // in degree
194  odb_.get_db("MetaData", "solar_zenith_angle", sunzen); // in degree
195  odb_.get_db("MetaData", "solar_azimuth_angle", sunazi); // in degree
196  odb_.get_db("MetaData", "latitude", lat); // -90~90 in degree
197  odb_.get_db("MetaData", "longitude", lon); // 0~360 in degree
198  odb_.get_db("MetaData", "height_above_mean_sea_level", elev); // in m
199  odb_.get_db("MetaData", "datetime", times);
200 
201  // 4. Call rttov set functions
202  //---------------------------------------------------------------------------------
203  util::DateTime time1;
204  int year, month, day, hour, minute, second;
205  int surftype;
206 
207  for (std::size_t i = 0; i < nprofiles; i++) {
208  profiles[i].setGasUnits(rttov::kg_per_kg);
209 
210  // convert mpas landmask/xice to rttov surface type
211  // may need to make this more generic for different models
212  if ( landmask[i] == 0 ) surftype=1; // sea
213  if ( landmask[i] == 1 ) surftype=0; // land
214  if ( seaice_frac[i] >= 0.5 ) surftype=2; // sea-ice
215  profiles[i].setSurfGeom(lat[i], lon[i], 0.001*elev[i]);
216 
217  time1 = times[i];
218  time1.toYYYYMMDDhhmmss(year, month, day, hour, minute, second);
219  profiles[i].setDateTimes(year, month, day, hour, minute, second);
220 
221  // 0:land, 1:sea, 2:sea-ice, (sea, fresh water) temporary
222  profiles[i].setSurfType(surftype, 0);
223 
224  // Ps (hPa), t2m (k), q2m (kg/kg), u10/v10 (m/s), wind fetch
225  profiles[i].setS2m(ps[i]*0.01, t2m[i], q2m[i], u10[i], v10[i], 100000.);
226 
227  // tskin (k), salinity (35), snow_fraction, foam_fraction, fastem_coef_1-5, specularity
228  // over sea/land
229  profiles[i].setSkin(tskin[i], 35., 0., 0., 3.0, 5.0, 15.0, 0.1, 0.3, 0.);
230  if ( surftype == 2 ) // over seaice, newice(no snow)
231  profiles[i].setSkin(tskin[i], 35., 0., 0., 2.9, 3.4, 27.0, 0.0, 0.0, 0.);
232 
233  profiles[i].setAngles(satzen[i], satazi[i], sunzen[i], sunazi[i]);
234  }
235  } // end try
236  catch (std::exception& e) {
237  oops::Log::error() << "Error defining the profile data " << e.what() << std::endl;
238  }
239 
240  // 4.1 Associate the profiles with each RttovSafe instance: the profiles undergo
241  // some checks so use a try block to catch any errors that are thrown up
242  try {
243  aRttov_.setTheProfiles(profiles);
244  }
245  catch (exception& e) {
246  oops::Log::error() << "Error setting the profiles " << e.what() << std::endl;
247  }
248 
249  // 5. Set the surface emissivity/reflectance arrays
250  // and associate with the Rttov objects
251  //--------------------------------------------------
252  double surfemisrefl[2][nprofiles][nchannels];
253 
254  aRttov_.setSurfEmisRefl(reinterpret_cast<double *>(surfemisrefl));
255 
256 // Surface emissivity/reflectance arrays must be initialised *before every call to RTTOV*
257 // Negative values will cause RTTOV to supply emissivity/BRDF values (i.e. equivalent to
258 // calcemis/calcrefl TRUE - see RTTOV user guide)
259  for (int i = 0; i < nprofiles; i++) {
260  for (int j = 0; j < 2; j++) {
261  for (int c = 0; c < nchannels; c++) surfemisrefl[j][i][c] = -1.;
262  }
263  }
264 
265 // 6. Call the RTTOV K model for one instrument for all profiles:
266 // no arguments are supplied so all 'loaded' channels are simulated
267 //----------------------------------------------------------------------
268  try {
269  aRttov_.runK();
270  }
271  catch (std::exception& e) {
272  oops::Log::error() << "Error running RTTOV K model " << e.what() << std::endl;
273  }
274 
275 // 7. Check if Jacobian has any NaN and set to skip bad profiles
276 //----------------------------------------------------------------------
277  std::vector<double> var_k(nlevels, 0.0);
278 
279  for (size_t p = 0; p < nprofiles; p++) skip_profile.push_back(false);
280 
281  for (size_t p = 0; p < nprofiles; p++) {
282  for (size_t c = 0; c < nchannels; c++) {
283  var_k = aRttov_.getTK(p, c); // T Jacobian for a single profile/channel
284  for (size_t l = 0; l < nlevels; ++l) if (std::isnan(var_k[l])) {skip_profile[p] = true;}
285 
286  var_k = aRttov_.getItemK(rttov::Q, p, c); // Q Jacobian for a single profile/channel
287  for (size_t l = 0; l < nlevels; ++l) if (std::isnan(var_k[l])) {skip_profile[p] = true;}
288  }
289  }
290 
291  oops::Log::trace() << "rttovcpp_interface done" << std::endl;
292 }
293 
294 // -----------------------------------------------------------------------------
295 
296 } // namespace ufo
GeoVaLs: geophysical values at locations.
size_t nlevs(const std::string &var) const
Return number of levels for a specified variable.
Definition: GeoVaLs.cc:310
void getAtLevel(std::vector< double > &, const std::string &, const int) const
Get GeoVaLs at a specified level.
Definition: GeoVaLs.cc:330
void get(std::vector< double > &, const std::string &var) const
Get 2D GeoVaLs for variable var (fails for 3D GeoVaLs)
Definition: GeoVaLs.cc:359
Forward declarations.
Definition: ObsAodExt.h:25
Definition: RunCRTM.h:27
void rttovcpp_interface(const GeoVaLs &geovals, const ioda::ObsSpace &odb_, rttov::RttovSafe &aRttov_, const std::string CoefFileName, const std::vector< int > channels_, std::size_t &nlevels, std::vector< bool > &skip_profile)
Set one single RttovSafe object for a single sensor.