16 #include "ioda/distribution/Accumulator.h"
17 #include "ioda/ObsDataVector.h"
18 #include "ioda/ObsSpace.h"
20 #include "oops/util/FloatCompare.h"
21 #include "oops/util/Logger.h"
23 #include "ufo/GeoVaLs.h"
33 :
FilterBase(obsdb, parameters, flags, obserr),
34 parameters_(parameters)
36 oops::Log::trace() <<
"ModelBestFitPressure contructor starting" << std::endl;
48 oops::Log::trace() <<
"ModelBestFitPressure destructed" << std::endl;
77 std::vector<std::vector<bool>> & flagged)
const {
78 oops::Log::trace() <<
"ModelBestFitPressure applyFilter" << std::endl;
93 const std::string model_eastvec_name =
"eastward_wind";
94 const std::string model_northvec_name =
"northward_wind";
101 std::vector<float> satwind_best_fit_press(
nlocs,
missing);
102 std::vector<float> satwind_best_fit_eastward_wind;
103 std::vector<float> satwind_best_fit_northward_wind;
104 if (calculate_best_fit_winds) {
110 std::vector<float> obs_pressure(
nlocs);
114 std::vector<float> obs_eastward(
nlocs);
115 obsdb_.get_db(
"ObsValue", model_eastvec_name, obs_eastward);
116 std::vector<float> obs_northward(
nlocs);
117 obsdb_.get_db(
"ObsValue", model_northvec_name, obs_northward);
120 std::vector<int> u_flags(
obsdb_.nlocs());
121 std::vector<int> v_flags(
obsdb_.nlocs());
122 if (
obsdb_.has(
"QCFlags", model_eastvec_name) &&
123 obsdb_.has(
"QCFlags", model_northvec_name)) {
124 obsdb_.get_db(
"QCFlags", model_eastvec_name, u_flags);
125 obsdb_.get_db(
"QCFlags", model_northvec_name, v_flags);
127 throw eckit::Exception(
"eastward_wind@QCFlags or northward_wind@QCFlags not initialised");
131 std::vector <float> model_pressure_profile(gvals->
nlevs(model_pressure_name), 0.0);
132 std::vector <float> model_eastvec_profile(gvals->
nlevs(model_eastvec_name), 0.0);
133 std::vector <float> model_northvec_profile(gvals->
nlevs(model_northvec_name), 0.0);
134 std::vector <float> vec_diff(num_level);
136 std::unique_ptr<ioda::Accumulator<size_t>> countAccumulator =
137 obsdb_.distribution()->createAccumulator<
size_t>();
139 for (
size_t idata = 0; idata <
nlocs; ++idata) {
142 gvals->
getAtLocation(model_pressure_profile, model_pressure_name, idata);
143 gvals->
getAtLocation(model_eastvec_profile, model_eastvec_name, idata);
144 gvals->
getAtLocation(model_northvec_profile, model_northvec_name, idata);
148 if (std::is_sorted(model_pressure_profile.begin(),
149 model_pressure_profile.end()) ||
150 model_pressure_profile.front() < model_pressure_profile.back()) {
151 std::reverse(model_pressure_profile.begin(), model_pressure_profile.end());
152 std::reverse(model_eastvec_profile.begin(), model_eastvec_profile.end());
153 std::reverse(model_northvec_profile.begin(), model_northvec_profile.end());
157 ASSERT(!std::is_sorted(model_pressure_profile.begin(),
158 model_pressure_profile.end()));
160 float min_vector_diff = std::numeric_limits<float>::max();
161 const size_t UNINITIALIZED = std::numeric_limits<size_t>::max();
162 size_t imin = UNINITIALIZED;
168 for (
size_t ilev = 0; ilev < num_level - 1; ++ilev) {
169 vec_diff[ilev] = std::hypot(obs_eastward[idata] - model_eastvec_profile[ilev],
170 obs_northward[idata] - model_northvec_profile[ilev]);
171 if (model_pressure_profile[ilev] < top_pressure)
continue;
172 if (vec_diff[ilev] < min_vector_diff) {
173 min_vector_diff = vec_diff[ilev];
178 if (imin == UNINITIALIZED) {
179 throw eckit::Exception(
"No model level pressure above top_pressure",
185 const float pressure_2 = model_pressure_profile[imin];
188 const float vec_diff_2 = vec_diff[imin];
193 satwind_best_fit_press[idata] = pressure_2;
195 pressure_1 = model_pressure_profile[imin - 1];
196 pressure_3 = model_pressure_profile[imin + 1];
197 vec_diff_1 = vec_diff[imin - 1];
198 vec_diff_3 = vec_diff[imin + 1];
201 if (pressure_3 < top_pressure) {
202 satwind_best_fit_press[idata] = pressure_2;
204 }
else if (!(std::fabs(vec_diff_2 - vec_diff_3) <= tolerance_vector_diff)) {
205 const float top = (((pressure_2 - pressure_1) *
206 (pressure_2 - pressure_1) *
207 (vec_diff_2 - vec_diff_3)) -
208 ((pressure_2 - pressure_3) *
209 (pressure_2 - pressure_3) *
210 (vec_diff_2 - vec_diff_1)));
211 const float bottom = (((pressure_2 - pressure_1) *
212 (vec_diff_2 - vec_diff_3)) -
213 ((pressure_2 - pressure_3) *
214 (vec_diff_2 - vec_diff_1)));
215 satwind_best_fit_press[idata] = pressure_2 -
216 (0.5f * (top / bottom));
219 satwind_best_fit_press[idata] = pressure_2;
225 if (calculate_best_fit_winds) {
229 if (std::fabs(pressure_2 - satwind_best_fit_press[idata]) <=
230 tolerance_pressure) {
231 satwind_best_fit_eastward_wind[idata] = model_eastvec_profile[imin];
232 satwind_best_fit_northward_wind[idata] = model_northvec_profile[imin];
234 if (pressure_2 < satwind_best_fit_press[idata]) {
235 lev_below = imin - 1;
237 prop = (satwind_best_fit_press[idata] - pressure_1) /
238 (pressure_2 - pressure_1);
241 lev_above = imin + 1;
242 prop = (satwind_best_fit_press[idata] - pressure_2) /
243 (pressure_3 - pressure_2);
245 ASSERT(prop >= 0 && prop <= 1);
246 satwind_best_fit_eastward_wind[idata] = model_eastvec_profile[lev_below] *
247 (1.0f - prop) + model_eastvec_profile[lev_above] * prop;
248 satwind_best_fit_northward_wind[idata] = model_northvec_profile[lev_below] *
249 (1.0f - prop) + model_northvec_profile[lev_above] * prop;
254 if (min_vector_diff <= upper_vector_diff) {
255 for (
size_t ilev = 0; ilev < num_level - 1; ++ilev) {
256 if (model_pressure_profile[ilev] < top_pressure)
continue;
257 if ((model_pressure_profile[ilev] <
258 satwind_best_fit_press[idata] - pressure_band_half_width ||
259 model_pressure_profile[ilev] >
260 satwind_best_fit_press[idata] + pressure_band_half_width) &&
261 vec_diff[ilev] <= min_vector_diff + lower_vector_diff) {
262 countAccumulator->addTerm(idata, 1);
269 countAccumulator->addTerm(idata, 1);
276 const std::size_t iconstraint = countAccumulator->computeResult();
277 if (iconstraint > 0) {
278 oops::Log::info() <<
"Satwind Poor constraint: "<< iconstraint
279 <<
" observations with modified pressure" << std::endl;
282 obsdb_.put_db(
"QCFlags", model_eastvec_name, u_flags);
283 obsdb_.put_db(
"QCFlags", model_northvec_name, v_flags);
284 obsdb_.put_db(
"DerivedValue",
"model_bestfit_pressure", satwind_best_fit_press);
285 if (calculate_best_fit_winds) {
286 obsdb_.put_db(
"DerivedValue",
"model_bestfit_eastward_wind",
287 satwind_best_fit_eastward_wind);
288 obsdb_.put_db(
"DerivedValue",
"model_bestfit_northward_wind",
289 satwind_best_fit_northward_wind);
296 os <<
"ModelBestFitPressure filter" <<
parameters_ << std::endl;
Base class for UFO QC filters.
GeoVaLs: geophysical values at locations.
size_t nlevs(const std::string &var) const
Return number of levels for a specified variable.
void getAtLocation(std::vector< double > &, const std::string &, const int) const
Get GeoVaLs at a specified location.
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
A filter that calculates the pressure at which the AMV wind vector (u,v) is a best match to the model...
void print(std::ostream &) const override
ModelBestFitPressure(ioda::ObsSpace &, const Parameters_ &, std::shared_ptr< ioda::ObsDataVector< int > >, std::shared_ptr< ioda::ObsDataVector< float > >)
Parameters controlling the operation of the ModelBestFitPressure filter.
oops::RequiredParameter< Variable > obs_pressure
Name of the observation pressure variable to correct.
oops::Parameter< float > upper_vector_diff
Maximum vector difference allowed, for calculating constraint.
oops::Parameter< bool > calculate_best_fit_winds
To calculate bestfit eastward/northward winds by linear interpolation.
oops::Parameter< float > pressure_band_half_width
oops::Parameter< float > tolerance_vector_diff
oops::Parameter< float > tolerance_pressure
oops::Parameter< float > top_pressure
oops::Parameter< float > lower_vector_diff
Minimum vector difference allowed, for calculating constraint.
const GeoVaLs * getGeoVaLs() const
Returns reference to GeoVaLs required by 1DVar.
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.
@ SatwindPoorConstraint
Best-fit pressure is not well constrained.
ObsPair reverse(const ObsPair &pair)
integer function nlocs(this)
Return the number of observational locations in this Locations object.