UFO
Thickness.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2021 Met Office
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 <string>
9 #include "ioda/ObsSpace.h"
10 #include "ufo/GeoVaLs.h"
12 #include "ufo/utils/Constants.h"
13 
14 namespace ufo {
15 
17 
18 // -----------------------------------------------------------------------------
19 
20 Thickness::Thickness(const Parameters_ & parameters, const oops::Variables & vars)
21  : PredictorBase(parameters, vars) {
22  // required variables
23  geovars_ += oops::Variables({"air_temperature",
24  "air_pressure"});
25  // required options
26  parameters_ = parameters;
27 
28  // override the predictor name to distinguish between
29  // thickness predictors at different pressure layers
30  name() = name() + "_" + std::to_string(static_cast<int>(parameters_.layerBase.value()/100))
31  + '_' + std::to_string(static_cast<int>(parameters_.layerTop.value()/100))
32  + "hPa";
33 }
34 
35 // -----------------------------------------------------------------------------
36 
37 void Thickness::compute(const ioda::ObsSpace & odb,
38  const GeoVaLs & geovals,
39  const ObsDiagnostics &,
40  ioda::ObsVector & out) const {
41  const std::size_t nlocs = odb.nlocs();
42  const std::size_t nvars = out.nvars();
43 
44  // assure shape of out
45  ASSERT(out.nlocs() == nlocs);
46 
47  const int t_levs = geovals.nlevs("air_temperature");
48  const int p_levs = geovals.nlevs("air_pressure");
49  std::vector <double> p_prof(t_levs, 0.0);
50  std::vector<double> t_prof(p_levs, 0.0);
51  std::vector <double> thick(odb.nlocs(), 0.0);
52 
53  const double p_high = parameters_.layerTop.value();
54  const double p_low = parameters_.layerBase.value();
55  const double pred_mean = parameters_.mean.value();
56  const double pred_std_inv = 1.0/parameters_.stDev.value();;
57 
58  for (std::size_t jl = 0; jl < nlocs; ++jl) {
59  geovals.getAtLocation(p_prof, "air_pressure", jl);
60  geovals.getAtLocation(t_prof, "air_temperature", jl);
61  std::reverse(p_prof.begin(), p_prof.end());
62  std::reverse(t_prof.begin(), t_prof.end());
63 
64  // Check that layer top is within pressure levels
65  if (p_high > p_prof.back()) {
66  oops::Log::error() << "layer top is greater than largest model pressure level" << std::endl;
67  throw eckit::BadValue("layer top is greater than largest model pressure level", Here());
68  }
69 
70  auto lower = std::lower_bound(p_prof.begin(), p_prof.end(), p_high);
71  int i = lower - p_prof.begin();
72 
73  // find the thickness of the top fraction layer.
74  double dp = p_prof[i] - p_high;
75  double f = dp/(p_prof[i] - p_prof[i-1]);
76  double p_av = p_prof[i] - 0.5*dp;
77  double t_av = 0.5*((2.0-f)*t_prof[i]+ f*t_prof[i-1]);
78  thick[jl] = t_av*(dp/p_av);
79  i += 1;
80 
81  // For all the levels in the desired thickness band
82  while (p_prof[i] < p_low && i < p_levs - 1) {
83  dp = p_prof[i] - p_prof[i-1];
84  p_av = p_prof[i] - 0.5*dp;
85  t_av = 0.5*(t_prof[i] + t_prof[i-1]);
86  thick[jl] += t_av*(dp/p_av);
87  i += 1;
88  }
89 
90  // When pressure level below thickness band or at last level
91  if (p_prof[i] >= p_low) {
92  dp = p_low - p_prof[i-1];
93  f = dp/(p_prof[i] - p_prof[i-1]);
94  p_av = p_low - 0.5*dp;
95  t_av = 0.5*((f)*t_prof[i]+ (2.0-f)*t_prof[i-1]);
96  thick[jl] += t_prof[i-1]*(dp/p_av);
97  // Note that the line replicates OPS which contains a bug
98  // the eqaution should be thick[jl] += t_av*(dp/p_av);
99  } else {
100  // Add thickness to last model level
101  dp = p_prof[i] - p_prof[i-1];
102  p_av = p_prof[i] - 0.5*dp;
103  t_av = 0.5*(t_prof[i] + t_prof[i-1]);
104  thick[jl] += t_av*(dp/p_av);
105  // Then assume constant temperature until p_low
106  dp = p_low - p_prof[i];
107  p_av = p_low - 0.5*dp;
108  t_av = t_prof[i];
109  thick[jl] += t_av*(dp/p_av);
110  }
111  }
112 
113  const double km_per_m = 1e-3;
114  const double dry_air_gas_const = 287.0; // Constants::rd not used for compatibility with OPS
115 
116  for (std::size_t jl = 0; jl < nlocs; ++jl) {
117  for (std::size_t jb = 0; jb < nvars; ++jb) {
118  out[jl*nvars+jb] = (thick[jl]
119  *(dry_air_gas_const/Constants::grav)*km_per_m - pred_mean)
120  *pred_std_inv;
121  }
122  }
123 }
124 
125 // -----------------------------------------------------------------------------
126 
127 } // 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 getAtLocation(std::vector< double > &, const std::string &, const int) const
Get GeoVaLs at a specified location.
Definition: GeoVaLs.cc:380
std::string & name()
predictor name
Definition: PredictorBase.h:80
oops::Variables geovars_
required GeoVaLs
Definition: PredictorBase.h:85
void compute(const ioda::ObsSpace &, const GeoVaLs &, const ObsDiagnostics &, ioda::ObsVector &) const override
compute the predictor
Definition: Thickness.cc:37
Thickness(const Parameters_ &, const oops::Variables &)
Definition: Thickness.cc:20
Parameters_ parameters_
Definition: Thickness.h:66
Configuration parameters of the thickness predictor.
Definition: Thickness.h:25
oops::RequiredParameter< double > layerBase
Pressure value (Pa) at the bottom of the required thickness layer.
Definition: Thickness.h:33
oops::RequiredParameter< double > stDev
Climatological standard deviation of predictor.
Definition: Thickness.h:39
oops::RequiredParameter< double > layerTop
Pressure value (Pa) at the top of the required thickness layer.
Definition: Thickness.h:30
oops::RequiredParameter< double > mean
Climatological mean of predictor.
Definition: Thickness.h:36
logical, parameter f
ObsPair reverse(const ObsPair &pair)
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27
static PredictorMaker< Thickness > makerFuncThickness_("thickness")
static constexpr double grav
Definition: Constants.h:23