UFO
ProfileVerticalAveraging.cc
Go to the documentation of this file.
1 /*
2  * (C) Crown 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 <algorithm>
9 #include <functional>
10 #include <utility>
11 
12 #include "eckit/exception/Exceptions.h"
13 
14 #include "oops/util/CompareNVectors.h"
15 #include "oops/util/Logger.h"
16 #include "oops/util/missingValues.h"
17 #include "oops/util/PropertiesOfNVectors.h"
18 
20 
22 
23 namespace ufo {
24  void calculateVerticalAverage(const std::vector <int> &flagsIn,
25  const std::vector <float> &valuesIn,
26  const std::vector <float> &coordIn,
27  const std::vector <float> &bigGap,
28  const std::vector <float> &coordOut,
29  float DZFrac,
31  std::vector <int> &flagsOut,
32  std::vector <float> &valuesOut,
33  int &numGaps,
34  std::vector <float> *coordMax,
35  std::vector <float> *coordMin)
36  {
37  const float missingValueFloat = util::missingValue(missingValueFloat);
38  const size_t numRepLev = coordIn.size();
39  const size_t numInterp = coordOut.size();
40  const size_t numOut = method == ProfileAveraging::Method::Interpolation ?
41  numInterp : numInterp - 1; // Number of output levels.
42 
43  // Coordinates in ascending order?
44  const bool Ascending = coordOut[0] < coordOut[numInterp - 1];
45 
46  // Make local copies of the vertical coordinates in order to allow them to be reversed.
47  std::vector <float> ZIn = coordIn;
48  std::vector <float> ZOut = coordOut;
49 
50  // Multiply coordinates by -1 if ascending.
51  if (Ascending) {
52  std::transform(ZIn.begin(), ZIn.end(), ZIn.begin(),
53  std::bind(std::multiplies<float>(), std::placeholders::_1, -1));
54  std::transform(ZOut.begin(), ZOut.end(), ZOut.begin(),
55  std::bind(std::multiplies<float>(), std::placeholders::_1, -1));
56  }
57 
58  // Initialise coordMin and coordMax, which record the minimum and maximum
59  // coordinates of the values used in the model layer average.
60  if (coordMin) coordMin->assign(numOut, missingValueFloat);
61  if (coordMax) coordMax->assign(numOut, missingValueFloat);
62 
63  // Note observation levels with data that are useful for averaging.
64 
65  // Interpolated/averaged values.
66  std::vector <float> valuesInterp(numInterp, missingValueFloat);
67  // Indices of useful data.
68  std::vector <size_t> idxUsefulLevels;
69  // True if there is a big gap relative to the previous level.
70  std::vector <bool> bigGapWithPreviousLevel(numRepLev + 1, false);
71  // Previous value of JLev.
72  size_t JLevP = 0;
73  for (size_t JLev = 0; JLev < numRepLev; ++JLev) {
74  // Ignore levels with missing data or pre-existing rejection flags.
75  if (valuesIn[JLev] == missingValueFloat ||
77  continue;
78  // Ignore duplicate levels.
79  if (idxUsefulLevels.size() > 0 && ZIn[JLevP] == ZIn[JLev]) continue;
80  // Set bigGapWithPreviousLevel in two cases:
81  if (idxUsefulLevels.size() == 0) {
82  // Cannot interpolate before first level.
83  bigGapWithPreviousLevel[idxUsefulLevels.size()] = true;
84  } else if (ZIn[JLevP] - ZIn[JLev] > std::max(bigGap[JLevP], bigGap[JLev])) {
85  numGaps++;
86  // Big gap from previous useful level.
87  bigGapWithPreviousLevel[idxUsefulLevels.size()] = true;
88  }
89  // Record index of this level.
90  idxUsefulLevels.push_back(JLev);
91  JLevP = JLev;
92  }
93 
94  // Loop over model levels, interpolating from useful reported levels.
95 
96  std::vector <int> flagsInterp(numInterp, 0); // Interpolation flags.
97  // Counter over reported levels which is incremented inside the loop over model levels.
98  size_t JLev = 0;
99  for (size_t MLev = 0; MLev < numInterp; ++MLev) {
100  JLevP = JLev; // Previous value of JLev.
101  const double ZMLev = ZOut[MLev]; // Coordinate value at current output level.
102  // Increment JLev until the associated coordinate is less than ZMLev
103  // or the number of useful levels is reached.
104  while (JLev != idxUsefulLevels.size() && ZIn[idxUsefulLevels[JLev]] >= ZMLev)
105  ++JLev;
106 
107  // JLev is the first reported level above model level MLev.
108  // JLev = 0 => ZMLev below bottom level.
109  // JLev = idxUsefulLevels.size() => ZMLev above top level.
110  if (JLev == 0 || JLev == idxUsefulLevels.size()) {
111  // Top or bottom - do not allow extrapolation.
112  } else if (!bigGapWithPreviousLevel[JLev]) {
113  // Interpolate to model layer boundaries.
114  const size_t J1 = idxUsefulLevels[JLev - 1];
115  const size_t J2 = idxUsefulLevels[JLev];
116  // Weight given to upper layer in interpolation.
117  const double Interp_factor = (ZMLev - ZIn[J1]) / (ZIn[J2] - ZIn[J1]);
118  // Interpolate reported level values onto model levels.
119  valuesInterp[MLev] = valuesIn[J1] + (valuesIn[J2] - valuesIn[J1]) * Interp_factor;
120  flagsInterp[MLev] = flagsIn[J1] | flagsIn[J2];
121  }
122 
123  // Loop if interpolating or at lowest model level.
124  if (method == ProfileAveraging::Method::Interpolation || MLev == 0)
125  continue;
126 
127  // Average over model layers.
128 
129  if (JLevP == JLev) {
130  // Model layer contains no reported levels.
131  if (valuesInterp[MLev - 1] != missingValueFloat &&
132  valuesInterp[MLev] != missingValueFloat) {
133  // Use mean of values at model layer bounds.
134  valuesInterp[MLev - 1] = (valuesInterp[MLev - 1] + valuesInterp[MLev]) * 0.5;
135  flagsInterp[MLev - 1] |= flagsInterp[MLev];
136  if (coordMax) (*coordMax)[MLev - 1] = ZOut[MLev - 1];
137  if (coordMin) (*coordMin)[MLev - 1] = ZOut[MLev];
138  }
139  } else {
140  // Model layer contains at least one reported level.
141  // Compute mean of values over the layer,
142  // weighting by the difference between coordinates in each segment.
143 
144  // Sum of DZ (coordinate differences) within model layer.
145  // Used as the denominator in the weighted mean.
146  // This is only different from unity if one or both of the upper or lower model bounds
147  // are missing.
148  float DZSum = 0.0;
149 
150  // Contribution from lowest segment, i.e. gap between
151  // lower bound of model layer and lower reported level coordinate.
152  size_t J2 = idxUsefulLevels[JLevP];
153  if (valuesInterp[MLev - 1] != missingValueFloat) {
154  const double DZ = ZOut[MLev - 1] - ZIn[J2];
155  valuesInterp[MLev - 1] = (valuesInterp[MLev - 1] + valuesIn[J2]) * DZ;
156  DZSum += DZ;
157  if (coordMax) (*coordMax)[MLev - 1] = ZOut[MLev - 1];
158  } else {
159  valuesInterp[MLev - 1] = 0.0;
160  if (coordMax) (*coordMax)[MLev - 1] = ZIn[J2];
161  }
162 
163  // Contribution from highest segment, i.e. gap between
164  // highest reported level coordinate and upper bound of model layer.
165  size_t J1 = idxUsefulLevels[JLev - 1];
166  if (valuesInterp[MLev] != missingValueFloat) {
167  const double DZ = ZIn[J1] - ZOut[MLev];
168  valuesInterp[MLev - 1] += (valuesInterp[MLev] + valuesIn[J1]) * DZ;
169  DZSum += DZ;
170  flagsInterp[MLev - 1] |= flagsInterp[MLev];
171  if (coordMin) (*coordMin)[MLev - 1] = ZOut[MLev];
172  } else {
173  if (coordMin) (*coordMin)[MLev - 1] = ZIn[J1];
174  }
175 
176  // Sum contributions from intermediate segments, i.e. gaps between
177  // adjacent reported level coordinates.
178  for (size_t J = JLevP + 1; J < JLev; ++J) {
179  J1 = idxUsefulLevels[J - 1];
180  J2 = idxUsefulLevels[J];
181  const double DZ = ZIn[J1] - ZIn[J2];
182  valuesInterp[MLev - 1] += (valuesIn[J1] + valuesIn[J2]) * DZ;
183  DZSum += DZ;
184  flagsInterp[MLev - 1] |= flagsIn[J1];
185  }
186  J1 = idxUsefulLevels[JLev - 1];
187  flagsInterp[MLev - 1] |= flagsIn[J1];
188 
189  // Calculate layer mean values.
190 
191  // Difference in coordinate across model layer.
192  const double DZMod = ZOut[MLev - 1] - ZOut[MLev];
193 
194  // Only process layers that are full by more than a certain amount.
195  if (DZSum > DZFrac * DZMod) {
196  // Set partial layer flag if the layer is between DZFrac and 99.5% full.
197  if (DZSum <= 0.995 * DZMod)
198  flagsInterp[MLev - 1] |= ufo::MetOfficeQCFlags::Profile::PartialLayerFlag;
199  // Divide the weighted sum by sum of the weights
200  // (the factor of 0.5 ensures correct normalisation).
201  valuesInterp[MLev - 1] *= 0.5 / DZSum;
202  } else {
203  valuesInterp[MLev - 1] = missingValueFloat;
204  }
205  }
206 
207  // Negate and swap coordMax and coordMin for this model level if the coordinates are ascending
208  // and the values are not missing.
209  if (Ascending && coordMin && coordMax &&
210  (*coordMax)[MLev - 1] != missingValueFloat &&
211  (*coordMin)[MLev - 1] != missingValueFloat) {
212  (*coordMax)[MLev - 1] *= -1;
213  (*coordMin)[MLev - 1] *= -1;
214  std::swap((*coordMax)[MLev - 1], (*coordMin)[MLev - 1]);
215  }
216  }
217 
218  // Fill output arrays.
219  flagsOut = {flagsInterp.begin(), flagsInterp.begin() + numOut};
220  valuesOut = {valuesInterp.begin(), valuesInterp.begin() + numOut};
221  }
222 } // namespace ufo
@ PartialLayerFlag
Partial Layer Vert Average.
@ FinalRejectFlag
Final QC flag.
Definition: RunCRTM.h:27
void calculateVerticalAverage(const std::vector< int > &flagsIn, const std::vector< float > &valuesIn, const std::vector< float > &coordIn, const std::vector< float > &bigGap, const std::vector< float > &coordOut, float DZFrac, ProfileAveraging::Method method, std::vector< int > &flagsOut, std::vector< float > &valuesOut, int &numGaps, std::vector< float > *coordMax, std::vector< float > *coordMin)
Profile vertical averaging onto model levels.