UFO
CloudDetectMinResidualIR.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 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 
9 
10 #include <cmath>
11 
12 #include <algorithm>
13 #include <cfloat>
14 #include <iomanip>
15 #include <iostream>
16 #include <set>
17 #include <string>
18 #include <vector>
19 
20 #include "ioda/ObsDataVector.h"
21 #include "oops/util/IntSetParser.h"
22 #include "oops/util/missingValues.h"
23 #include "ufo/filters/Variable.h"
24 #include "ufo/utils/Constants.h"
25 
26 namespace ufo {
27 
28 static ObsFunctionMaker<CloudDetectMinResidualIR>
29  makerCloudDetectMinResidualIR_("CloudDetectMinResidualIR");
30 
31 // -----------------------------------------------------------------------------
32 
33 CloudDetectMinResidualIR::CloudDetectMinResidualIR(const eckit::LocalConfiguration & conf)
34  : invars_() {
35  // Check options
36  options_.deserialize(conf);
37 
38  // Get channels from options
39  std::set<int> channelset = oops::parseIntSet(options_.channelList);
40  std::copy(channelset.begin(), channelset.end(), std::back_inserter(channels_));
41  ASSERT(channels_.size() > 0);
42 
43  // Get test groups from options
44  const std::string &flaggrp = options_.testQCflag.value();
45  const std::string &errgrp = options_.testObserr.value();
46  const std::string &hofxgrp = options_.testHofX.value();
47 
48  // Include required variables from ObsDiag
49  invars_ += Variable("brightness_temperature_jacobian_surface_temperature@ObsDiag", channels_);
50  invars_ += Variable("brightness_temperature_jacobian_air_temperature@ObsDiag", channels_);
51  invars_ += Variable("transmittances_of_atmosphere_layer@ObsDiag", channels_);
52  invars_ += Variable("pressure_level_at_peak_of_weightingfunction@ObsDiag", channels_);
53 
54  // Include list of required data from ObsSpace
55  invars_ += Variable("brightness_temperature@"+flaggrp, channels_);
56  invars_ += Variable("brightness_temperature@"+errgrp, channels_);
57  invars_ += Variable("brightness_temperature@"+hofxgrp, channels_);
58  invars_ += Variable("brightness_temperature@ObsValue", channels_);
59  invars_ += Variable("brightness_temperature@ObsError", channels_);
60 
61  // Include list of required data from GeoVaLs
62  invars_ += Variable("water_area_fraction@GeoVaLs");
63  invars_ += Variable("land_area_fraction@GeoVaLs");
64  invars_ += Variable("ice_area_fraction@GeoVaLs");
65  invars_ += Variable("surface_snow_area_fraction@GeoVaLs");
66  invars_ += Variable("average_surface_temperature_within_field_of_view@GeoVaLs");
67  invars_ += Variable("air_pressure@GeoVaLs");
68  invars_ += Variable("air_temperature@GeoVaLs");
69  invars_ += Variable("tropopause_pressure@GeoVaLs");
70 }
71 
72 // -----------------------------------------------------------------------------
73 
75 
76 // -----------------------------------------------------------------------------
77 
79  ioda::ObsDataVector<float> & out) const {
80  // Get channel use flags from options
81  std::vector<int> use_flag = options_.useflagChannel.value();
82  std::vector<int> use_flag_clddet = options_.useflagCloudDetect.value();
83 
84  // Get tuning parameters for surface sensitivity over sea/land/oce/snow/mixed from options
85  std::vector<float> dtempf_in = options_.obserrScaleFactorTsfc.value();
86 
87  // Get dimensions
88  size_t nlocs = in.nlocs();
89  size_t nchans = channels_.size();
90  size_t nlevs = in.nlevs(Variable("air_pressure@GeoVaLs"));
91 
92  // Get test groups from options
93  const std::string &flaggrp = options_.testQCflag.value();
94  const std::string &errgrp = options_.testObserr.value();
95  const std::string &hofxgrp = options_.testHofX.value();
96 
97  // Get variables from ObsDiag
98  // Load surface temperature jacobian
99  std::vector<std::vector<float>> dbtdts(nchans, std::vector<float>(nlocs));
100  for (size_t ichan = 0; ichan < nchans; ++ichan) {
101  in.get(Variable("brightness_temperature_jacobian_surface_temperature@ObsDiag",
102  channels_)[ichan], dbtdts[ichan]);
103  }
104 
105  // Get temperature jacobian
106  std::vector<std::vector<std::vector<float>>>
107  dbtdt(nchans, std::vector<std::vector<float>>(nlevs, std::vector<float>(nlocs)));
108  for (size_t ichan = 0; ichan < nchans; ++ichan) {
109  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
110  const int level = nlevs - ilev - 1;
111  in.get(Variable("brightness_temperature_jacobian_air_temperature@ObsDiag",
112  channels_)[ichan], level, dbtdt[ichan][ilev]);
113  }
114  }
115 
116  // Get layer-to-space transmittance
117  std::vector<std::vector<std::vector<float>>>
118  tao(nchans, std::vector<std::vector<float>>(nlevs, std::vector<float>(nlocs)));
119  for (size_t ichan = 0; ichan < nchans; ++ichan) {
120  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
121  const int level = nlevs - ilev - 1;
122  in.get(Variable("transmittances_of_atmosphere_layer@ObsDiag",
123  channels_)[ichan], level, tao[ichan][ilev]);
124  }
125  }
126 
127  // Get pressure level at the peak of the weighting function
128  std::vector<float> values(nlocs, 0.0);
129  std::vector<std::vector<float>> wfunc_pmaxlev(nchans, std::vector<float>(nlocs));
130  for (size_t ichan = 0; ichan < nchans; ++ichan) {
131  in.get(Variable("pressure_level_at_peak_of_weightingfunction@ObsDiag",
132  channels_)[ichan], values);
133  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
134  wfunc_pmaxlev[ichan][iloc] = nlevs - values[iloc] + 1;
135  }
136  }
137 
138  // Get variables from ObsSpace
139  // Get effective observation error and convert it to inverse of the error variance
140  const float missing = util::missingValue(missing);
141  std::vector<int> qcflag(nlocs, 0);
142  std::vector<std::vector<float>> varinv_use(nchans, std::vector<float>(nlocs, 0.0));
143  for (size_t ichan = 0; ichan < nchans; ++ichan) {
144  in.get(Variable("brightness_temperature@"+errgrp, channels_)[ichan], values);
145  in.get(Variable("brightness_temperature@"+flaggrp, channels_)[ichan], qcflag);
146  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
147  if (flaggrp == "PreQC") values[iloc] == missing ? qcflag[iloc] = 100 : qcflag[iloc] = 0;
148  (qcflag[iloc] == 0) ? (values[iloc] = 1.0 / pow(values[iloc], 2)) : (values[iloc] = 0.0);
149  if (use_flag_clddet[ichan] > 0) varinv_use[ichan][iloc] = values[iloc];
150  }
151  }
152 
153  // Get bias corrected innovation (tbobs - hofx) (hofx includes bias correction)
154  std::vector<std::vector<float>> innovation(nchans, std::vector<float>(nlocs));
155  for (size_t ichan = 0; ichan < nchans; ++ichan) {
156  in.get(Variable("brightness_temperature@ObsValue", channels_)[ichan], innovation[ichan]);
157  in.get(Variable("brightness_temperature@"+hofxgrp, channels_)[ichan], values);
158  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
159  innovation[ichan][iloc] = innovation[ichan][iloc] - values[iloc];
160  }
161  }
162 
163  // Get original observation error (uninflated) from ObsSpaec
164  std::vector<std::vector<float>> obserr(nchans, std::vector<float>(nlocs));
165  for (size_t ichan = 0; ichan < nchans; ++ichan) {
166  in.get(Variable("brightness_temperature@ObsError", channels_)[ichan], obserr[ichan]);
167  }
168 
169  // Get variables from GeoVaLS
170  // Get tropopause pressure [Pa]
171  std::vector<float> tropprs(nlocs);
172  in.get(Variable("tropopause_pressure@GeoVaLs"), tropprs);
173 
174  // Get average surface temperature within FOV
175  std::vector<float> tsavg(nlocs);
176  in.get(Variable("average_surface_temperature_within_field_of_view@GeoVaLs"), tsavg);
177 
178  // Get area fraction of each surface type
179  std::vector<float> water_frac(nlocs);
180  std::vector<float> land_frac(nlocs);
181  std::vector<float> ice_frac(nlocs);
182  std::vector<float> snow_frac(nlocs);
183  in.get(Variable("water_area_fraction@GeoVaLs"), water_frac);
184  in.get(Variable("land_area_fraction@GeoVaLs"), land_frac);
185  in.get(Variable("ice_area_fraction@GeoVaLs"), ice_frac);
186  in.get(Variable("surface_snow_area_fraction@GeoVaLs"), snow_frac);
187 
188  // Determine dominant surface type in each FOV
189  std::vector<bool> land(nlocs, false);
190  std::vector<bool> sea(nlocs, false);
191  std::vector<bool> ice(nlocs, false);
192  std::vector<bool> snow(nlocs, false);
193  std::vector<bool> mixed(nlocs, false);
194  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
195  sea[iloc] = water_frac[iloc] >= 0.99;
196  land[iloc] = land_frac[iloc] >= 0.99;
197  ice[iloc] = ice_frac[iloc] >= 0.99;
198  snow[iloc] = snow_frac[iloc] >= 0.99;
199  mixed[iloc] = (!sea[iloc] && !land[iloc] && !ice[iloc] && !snow[iloc]);
200  }
201 
202  // Setup weight given to each surface type
203  std::vector<float> dtempf(nlocs);
204  for (size_t iloc = 0; iloc < nlocs; ++iloc) {
205  if (sea[iloc]) {
206  dtempf[iloc] = dtempf_in[0];
207  } else if (land[iloc]) {
208  dtempf[iloc] = dtempf_in[1];
209  } else if (ice[iloc]) {
210  dtempf[iloc] = dtempf_in[2];
211  } else if (snow[iloc]) {
212  dtempf[iloc] = dtempf_in[3];
213  } else {
214  dtempf[iloc] = dtempf_in[4];
215  }
216  }
217 
218  // Get air pressure [Pa]
219  std::vector<std::vector<float>> prsl(nlevs, std::vector<float>(nlocs));
220  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
221  const size_t level = nlevs - ilev - 1;
222  in.get(Variable("air_pressure@GeoVaLs"), level, prsl[ilev]);
223  }
224 
225  // Get air temperature
226  std::vector<std::vector<float>> tair(nlevs, std::vector<float>(nlocs));
227  for (size_t ilev = 0; ilev < nlevs; ++ilev) {
228  const size_t level = nlevs - ilev - 1;
229  in.get(Variable("air_temperature@GeoVaLs"), level, tair[ilev]);
230  }
231 
232  // Minimum Residual Method (MRM) for Cloud Detection:
233  // Determine model level index of the cloud top (lcloud)
234  // Find pressure of the cloud top (cldprs)
235  // Estimate cloud fraction (cldfrac)
236  // output: out = 0 clear channel
237  // out = 1 cloudy channel
238  // out = 2 clear channel but too sensitive to surface condition
239 
240  // Set vectors to hold cloud infomation from cloud detection (cab be part of the output)
241  // std::vector<float> cloud_prsl(nlocs);
242  // std::vector<float> cloud_frac(nlocs);
243  // std::vector<int> cloud_lev(nlocs);
244 
245  // Loop through locations
246  const float btmax = 550.f, btmin = 50.f;
247  for (size_t iloc=0; iloc < nlocs; ++iloc) {
248  // Initialize at each location
249  // cloud_lev[iloc] = 0;
250  // cloud_prsl[iloc] = 0.0;
251  // cloud_frac[iloc] = 0.0;
252  float sum = 0.0, sum2 = 0.0, sum3 = 0.0;
253  float tmp = 0.0;
254  float cloudp = 0.0;
255  std::vector<float> dbt(nchans);
256  for (size_t ichan=0; ichan < nchans; ++ichan) {
257  if (varinv_use[ichan][iloc] > 0) {
258  sum3 = sum3 + innovation[ichan][iloc] * innovation[ichan][iloc] * varinv_use[ichan][iloc];
259  }
260  }
261  sum3 = 0.75 * sum3;
262 
263  // Set initial cloud condition
264  int lcloud = 0;
265  float cldfrac = 0.0;
266  float cldprs = prsl[0][iloc] * 0.01; // convert from [Pa] to [hPa]
267 
268  // Loop through vertical layer from surface to model top
269  for (size_t k = 0 ; k < nlevs ; ++k) {
270  // Perform cloud detection within troposphere
271  if (prsl[k][iloc] * 0.01 > tropprs[iloc] * 0.01) {
272  for (size_t ichan = 0; ichan < nchans; ++ichan) {
273  dbt[ichan] = (tair[k][iloc] - tsavg[iloc]) * dbtdts[ichan][iloc];
274  }
275  for (size_t kk = 0; kk < k; ++kk) {
276  for (size_t ichan = 0; ichan < nchans; ++ichan) {
277  dbt[ichan] = dbt[ichan] + (tair[k][iloc] - tair[kk][iloc]) * dbtdt[ichan][kk][iloc];
278  }
279  }
280  sum = 0.0;
281  sum2 = 0.0;
282  for (size_t ichan = 0; ichan < nchans; ++ichan) {
283  if (varinv_use[ichan][iloc] > 0.0) {
284  sum = sum + innovation[ichan][iloc] * dbt[ichan] * varinv_use[ichan][iloc];
285  sum2 = sum2 + dbt[ichan] * dbt[ichan] * varinv_use[ichan][iloc];
286  }
287  }
288  if (fabs(sum2) < FLT_MIN) sum2 = copysign(1.0e-12, sum2);
289  cloudp = std::min(std::max((sum/sum2), 0.f), 1.f);
290  sum = 0.0;
291  for (size_t ichan = 0; ichan < nchans; ++ichan) {
292  if (varinv_use[ichan][iloc] > 0.0) {
293  tmp = innovation[ichan][iloc] - cloudp * dbt[ichan];
294  sum = sum + tmp * tmp * varinv_use[ichan][iloc];
295  }
296  }
297  if (sum < sum3) {
298  sum3 = sum;
299  lcloud = k + 1; // array index + 1 -> model coordinate index
300  cldfrac = cloudp;
301  cldprs = prsl[k][iloc] * 0.01;
302  }
303  }
304  // end of vertical loop
305  }
306  // If more than 2% of the transmittance comes from the cloud layer,
307  // reject the channel (marked as cloudy channel)
308  float tao_cld = -999.0;
309  for (size_t ichan = 0; ichan < nchans; ++ichan) out[ichan][iloc] = 0;
310  if (lcloud > 0) {
311  for (size_t ichan = 0; ichan < nchans; ++ichan) {
312  // Get cloud top transmittance
313  tao_cld = tao[ichan][lcloud-1][iloc];
314  // Passive channels
315  if (use_flag[ichan] < 0 && lcloud >= wfunc_pmaxlev[ichan][iloc]) out[ichan][iloc] = 1;
316  // Active channels
317  if (out[ichan][iloc] < 1 && tao_cld > 0.02) out[ichan][iloc] = 1;
318  }
319  // cloud infomation output at model level
320  // cloud_lev[iloc] = lcloud;
321  // cloud_prsl[iloc] = cldprs;
322  // cloud_frac[iloc] = cldfrac;
323  } else {
324  // If no clouds is detected, do sensivity to surface temperature check
325  // Initialize at each location
326  float sum = 0.0, sum2 = 0.0;
327  float dts = 0.0;
328  float delta = 0.0;
329  const float dts_threshold = 3.0;
330  for (size_t ichan=0; ichan < nchans; ++ichan) {
331  delta = 0.0;
332  sum = sum + innovation[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc];
333  sum2 = sum2 + dbtdts[ichan][iloc] * dbtdts[ichan][iloc] * varinv_use[ichan][iloc];
334  }
335  if (fabs(sum2) < FLT_MIN) sum2 = copysign(1.0e-12, sum2);
336  dts = std::fabs(sum / sum2);
337  if (std::abs(dts) > 1.0) {
338  if (sea[iloc] == false) {
339  dts = std::min(dtempf[iloc], dts);
340  } else {
341  dts = std::min(dts_threshold, dts);
342  }
343  for (size_t ichan=0; ichan < nchans; ++ichan) {
344  delta = std::max(0.05 * obserr[ichan][iloc], 0.02);
345  if (std::abs(dts * dbtdts[ichan][iloc]) > delta) out[ichan][iloc] = 2;
346  }
347  }
348  }
349  // end of location loop
350  }
351 }
352 
353 // -----------------------------------------------------------------------------
354 
356  return invars_;
357 }
358 
359 // -----------------------------------------------------------------------------
360 
361 } // namespace ufo
CloudDetectMinResidualIRParameters options_
const ufo::Variables & requiredVariables() const
geovals required to compute the function
CloudDetectMinResidualIR(const eckit::LocalConfiguration &)
void compute(const ObsFilterData &, ioda::ObsDataVector< float > &) const
compute the result of the function
oops::Parameter< std::string > testQCflag
Name of the data group to which the QC flag is applied (default is QCflagsData)
oops::Parameter< std::string > testHofX
Name of the HofX group used to replace the default group (default is HofX)
oops::RequiredParameter< std::vector< int > > useflagChannel
Useflag (-1: not used; 0: monitoring; 1: used) for each channel in channelList.
oops::RequiredParameter< std::string > channelList
List of channels available for assimilation.
oops::Parameter< std::string > testObserr
Name of the data group to which the observation error is applied (default: ObsErrorData)
oops::RequiredParameter< std::vector< int > > useflagCloudDetect
Useflag (-1: not used; 1: used) indicating if the channel is used for cloud detection.
oops::RequiredParameter< std::vector< float > > obserrScaleFactorTsfc
ObsFilterData provides access to all data related to an ObsFilter.
size_t nlocs() const
Returns the number of locations in the associated ObsSpace.
size_t nlevs(const Variable &) const
Returns the number of levels in the specified variable.
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
constexpr int missing
Definition: QCflags.h:20
real(kind_real), parameter, public sea
integer function nlocs(this)
Return the number of observational locations in this Locations object.
Definition: RunCRTM.h:27
static ObsFunctionMaker< CloudDetectMinResidualIR > makerCloudDetectMinResidualIR_("CloudDetectMinResidualIR")
util::Duration abs(const util::Duration &duration)