UFO
TrackCheckShip.cc
Go to the documentation of this file.
1 /*
2  * (C) Copyright 2020 Met Office UK
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 <Eigen/Core> // for easy array addition/subtraction
11 
12 #include <algorithm>
13 #include <cassert>
14 #include <cmath>
15 #include <functional>
16 #include <map>
17 #include <string>
18 #include <tuple>
19 #include <utility>
20 #include <vector>
21 
22 #include "eckit/config/Configuration.h"
23 #include "ioda/ObsDataVector.h"
24 #include "ioda/ObsSpace.h"
25 #include "oops/base/Variables.h"
26 #include "oops/util/DateTime.h"
27 #include "oops/util/Duration.h"
28 #include "oops/util/Logger.h"
31 #include "ufo/utils/Constants.h"
33 
34 namespace ufo {
35 
36 TrackCheckShip::TrackCheckShip(ioda::ObsSpace &obsdb, const eckit::Configuration &config,
37  std::shared_ptr<ioda::ObsDataVector<int> > flags,
38  std::shared_ptr<ioda::ObsDataVector<float> > obserr)
39  : FilterBase(obsdb, config, flags, obserr)
40 {
41  oops::Log::debug() << "TrackCheckShip: config = " << config << std::endl;
43  options_->deserialize(config);
44  assert(options_->maxSpeed.value() > 0);
45  if (options_->testingMode)
47 }
48 
49 /// \brief Estimate of speed as calculated in Ops_CheckShipTrack.
50 ///
51 /// Speed is calculated between \p obs1 and \p obs2, accounting for spatial/temporal
52 /// uncertainty using the resolution values stored in \p options.
56  const TrackCheckShipParameters& options) {
57  util::Duration temporalDistance = abs(obs1.getTime() -
58  obs2.getTime());
59  util::Duration tempRes = options.temporalResolution;
60  auto dist = distance(obs1, obs2);
61  auto spatialRes = options.spatialResolution;
62  double speedEst = 0.0;
63  if (dist > spatialRes) {
64  speedEst = (dist - 0.5 * spatialRes) /
65  std::max(temporalDistance.toSeconds(),
66  (tempRes.toSeconds()));
67  }
68  return speedEst;
69 }
70 
71 /// \brief Returns the angle in degrees (rounded to the nearest .1 degree)
72 /// formed by the track segment going from observation \p a to \p b to \p c.
76  auto locA = a.getLocation();
77  auto locB = b.getLocation();
78  auto locC = c.getLocation();
79  Eigen::Vector3f disp1{locB[0]-locA[0], locB[1]-locA[1], locB[2]-locA[2]};
80  Eigen::Vector3f disp2{locC[0]-locB[0], locC[1]-locB[1], locC[2]-locB[2]};
81  auto retValue = std::acos(disp1.dot(disp2) / (disp1.norm() * disp2.norm()));
82  retValue *= static_cast<float>(Constants::rad2deg);
83  return std::round(retValue * 10.0f) * 0.1f;
84 }
85 
87 {
88  return diagnostics_.get();
89 }
90 
92  double latitude, double longitude,
93  const util::DateTime &time,
94  std::shared_ptr<TrackStatistics> const& ts,
95  std::shared_ptr<TrackCheckUtils::CheckCounter> const &checkCounter,
96  size_t observationNumber)
97  : obsLocationTime_(latitude, longitude, time), fullTrackStatistics_(ts),
98  checkCounter_(checkCounter), observationNumber_(observationNumber) {}
99 
101  const TrackCheckUtils::CheckResult &result) {
102  checkCounter_->registerCheckResult(result);
103 }
104 
106 {
107  return observationNumber_;
108 }
109 
110 // Required for the correct destruction of options_.
112 {}
113 
114 /// Detects whether each observation in a track has been rejected, and marks
115 /// the corresponding space in \p isRejected as true if so.
116 /// \param trackObsIndicesBegin the begin iterator of the track
117 /// \param trackObsIndicesEnd the end iterator
118 /// \param validObsIds the vector marking all of the observations' global
119 /// positions
120 /// \param trackObservations the full vector of observations within
121 /// the single track
122 /// \param isRejected the boolean vector whose indices correspond to all of
123 /// the rejected observations in the full input dataset
125  std::vector<size_t>::const_iterator trackObsIndicesBegin,
126  std::vector<size_t>::const_iterator trackObsIndicesEnd,
127  const std::vector<size_t> &validObsIds,
128  const std::vector<TrackObservation> &trackObservations,
129  std::vector<bool> &isRejected) const {
130  auto trackObsIndexIt = trackObsIndicesBegin;
131  auto trackObsIt = trackObservations.begin();
132  for (; trackObsIndexIt != trackObsIndicesEnd; ++trackObsIndexIt, ++trackObsIt)
133  if (trackObsIt->rejected())
134  isRejected[validObsIds[*trackObsIndexIt]] = true;
135 }
136 
137 void TrackCheckShip::print(std::ostream & os) const {
138  os << "TrackCheckShip: config = " << config_ << std::endl;
139 }
140 
141 
142 /// \todo This section is not yet fully implemented. Current implementation includes separating
143 /// observations into tracks based on \p Station_Id, calculating distances, speeds, and
144 /// angles between observations, and incrementing track-specific counters should the
145 /// calculations produce unexpected values. Based on the counter values, the filter
146 /// may be stopped early. Simultaneous observations are then optionally ignored for later
147 /// re-inclusion or rejection. Using the above calculations and additional ones if necessary,
148 /// the observation directly before and/or after the fastest segment is chosen for rejection.
149 /// Re-checking of simultaneous observations is not yet implemented.
150 
151 void TrackCheckShip::applyFilter(const std::vector<bool> & apply,
152  const Variables & filtervars,
153  std::vector<std::vector<bool>> & flagged) const {
154  const std::vector<size_t> validObsIds = TrackCheckUtils::getValidObservationIds(apply, flags_);
155 
156  RecursiveSplitter splitter(validObsIds.size());
158  TrackCheckUtils::sortTracksChronologically(validObsIds, splitter, obsdb_);
159 
162 
163  std::vector<bool> isRejected(apply.size(), false);
164  for (auto track : splitter.multiElementGroups()) {
165  std::vector<TrackObservation> trackObservations = collectTrackObservations(
166  track.begin(), track.end(), validObsIds, obsLocTime);
167  std::vector<std::reference_wrapper<TrackObservation>> trackObservationsReferences;
168  trackObservationsReferences.reserve(trackObservations.size());
169  std::transform(trackObservations.begin(), trackObservations.end(),
170  std::back_inserter(trackObservationsReferences),
171  [](TrackObservation &obs) {
172  return std::ref<TrackObservation>(obs); });
173  calculateTrackSegmentProperties(trackObservationsReferences,
174  CalculationMethod::FIRSTITERATION);
175  if (!trackObservationsReferences.empty() &&
176  this->options_->earlyBreakCheck &&
177  TrackCheckShip::earlyBreak(trackObservationsReferences)) {
178  continue;
179  }
180  if (options_->deferredCheckSimultaneous.value()) {
181  trackObservationsReferences = removeSimultaneousObservations(trackObservationsReferences);
182  } else { // reinstating deferred observation is not yet implemented
183  bool firstIterativeRemoval = true;
184  while (trackObservationsReferences.size() >= 3) {
185  // Initial loop: fastest (as determined by set of comparisons) observation removed
186  // until all segments show slower speed than max threshold
187  auto maxSpeedReferenceIterator = std::max_element(
188  trackObservationsReferences.begin(), trackObservationsReferences.end(),
190  return a.getObservationStatistics().speed <
191  b.getObservationStatistics().speed;});
192  auto maxSpeedValue = maxSpeedReferenceIterator->get().getObservationStatistics().speed;
193  if (maxSpeedValue <= (0.8 * options_->maxSpeed.value())) {
194  break;
195  } else if (maxSpeedValue < options_->maxSpeed.value()) {
196  auto maxSpeedAngle = std::max(
197  (maxSpeedReferenceIterator - 1)->get().getObservationStatistics().angle,
198  maxSpeedReferenceIterator->get().getObservationStatistics().angle);
199  if (maxSpeedAngle <= 90.0) {
200  break;
201  }
202  }
204  trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval);
205  firstIterativeRemoval = false;
206  calculateTrackSegmentProperties(trackObservationsReferences, CalculationMethod::MAINLOOP);
207  }
209  validObsIds, trackObservations, isRejected);
210  }
211  }
212  TrackCheckUtils::flagRejectedObservations(isRejected, flagged);
213 }
214 
215 /// \returns a \p vector of \p TrackObservations that all hold a \p shared_ptr to an instance
216 /// of \p TrackStatistics, which holds all of the track-specific counters.
217 std::vector<TrackCheckShip::TrackObservation> TrackCheckShip::collectTrackObservations(
218  std::vector<size_t>::const_iterator trackObsIndicesBegin,
219  std::vector<size_t>::const_iterator trackObsIndicesEnd,
220  const std::vector<size_t> &validObsIds,
221  const TrackCheckUtils::ObsGroupLocationTimes &obsLocTime) const {
222  std::vector<TrackObservation> trackObservations;
223  trackObservations.reserve(trackObsIndicesEnd - trackObsIndicesBegin);
224  std::shared_ptr<TrackStatistics> trackStatistics(new TrackStatistics());
225  std::shared_ptr<TrackCheckUtils::CheckCounter> checkCounter(new TrackCheckUtils::CheckCounter);
226  size_t observationNumber = 0;
227  for (std::vector<size_t>::const_iterator it = trackObsIndicesBegin;
228  it != trackObsIndicesEnd; ++it) {
229  const size_t obsId = validObsIds[*it];
230  trackObservations.push_back(TrackObservation(obsLocTime.latitudes[obsId],
231  obsLocTime.longitudes[obsId],
232  obsLocTime.datetimes[obsId], trackStatistics,
233  checkCounter,
234  observationNumber));
235  observationNumber++;
236  }
237  return trackObservations;
238 }
239 
240 /// \brief \returns true if at least half of the track segments have
241 /// incremented the relevant rejection counters
242 ///
243 /// This filter is best for mostly-accurate observation tracks. If the track has
244 /// many "errors" (as indicated by the counters that are incremented before
245 /// any observations are removed), it will stop early by default.
246 bool TrackCheckShip::earlyBreak(const std::vector<std::reference_wrapper<TrackObservation>>
247  &trackObs) const {
248  bool breakResult = false;
249  const auto& trackStats = *(trackObs[0].get().getFullTrackStatistics());
250  // if at least half of the track segments have a time difference of less than an hour
251  // (if non-buoy), are faster than a configured maximum speed, or exhibit at least a 90
252  // degree bend
253  if ((2 * ((options_->inputCategory.value() != 1) // 1 is input category of buoy
254  * trackStats.numShort_ + trackStats.numFast_) + trackStats.numBends_)
255  >= (trackObs.size() - 1)) {
256  std::string stationId = "no station id provided";
257  if (options_->stationIdVariable.value() != boost::none) {
258  stationId = (options_->stationIdVariable.value().get()).variable();
259  }
260  oops::Log::warning() << "ShipTrackCheck: " << stationId << "\n" <<
261  "Time difference < 1 hour: " << trackStats.numShort_ << "\n" <<
262  "Fast: " << trackStats.numFast_ << "\n" <<
263  "Bends: " << trackStats.numBends_ << "\n" <<
264  "Track was not checked." << std::endl;
265 
266  breakResult = true;
267  }
268  if (options_->testingMode)
269  diagnostics_->storeEarlyBreakResult(breakResult);
270  return breakResult;
271 }
272 
273 /// \brief Filters out all observations that have been marked simultaneous for
274 /// rechecking at the end.
275 std::vector<std::reference_wrapper<TrackCheckShip::TrackObservation>>
277  const std::vector<std::reference_wrapper<TrackObservation>> &trackObs) const {
278  std::vector<std::reference_wrapper<TrackObservation>> reducedTrackObs;
279  std::copy_if(trackObs.begin(), trackObs.end(), std::back_inserter(reducedTrackObs),
280  [](const TrackObservation &obs){
281  return !(obs.getObservationStatistics().simultaneous);});
282  calculateTrackSegmentProperties(reducedTrackObs, CalculationMethod::SIMULTANEOUSDEFERRAL);
283  return reducedTrackObs;
284 }
285 
286 /// \brief Chooses which of the observations surrounding the speediest segment to remove,
287 /// flagging it accordingly.
289  std::vector<std::reference_wrapper<TrackObservation>> &track,
290  const std::vector<std::reference_wrapper<TrackObservation>>::iterator
291  &observationAfterFastestSegment,
292  bool firstIterativeRemoval) const {
293  int errorCategory = 0;
294  util::Duration four_days{"P4D"};
295  auto rejectedObservation = observationAfterFastestSegment;
296  // lambda function to "fail" an observation that should be rejected
297  auto fail = [&rejectedObservation](
298  const std::vector<std::reference_wrapper<TrackObservation>>::iterator &iter) {
299  iter->get().registerCheckResult(TrackCheckUtils::CheckResult::FAILED);
300  iter->get().registerSweepOutcome(true);
301  rejectedObservation = iter;
302  };
303  auto neighborObservationStatistics = [&observationAfterFastestSegment](int index) ->
304  const ObservationStatistics & {
305  return (observationAfterFastestSegment + index)->get().getObservationStatistics();
306  };
307  auto meanSpeed = observationAfterFastestSegment->get().getFullTrackStatistics()->meanSpeed_;
308  if (observationAfterFastestSegment == track.begin() + 1) {
309  // Decide whether ob 0 or 1 agrees best with ob 2
310  if (neighborObservationStatistics(0).speedAveraged <=
311  options_->maxSpeed &&
312  (neighborObservationStatistics(1).speed >
313  options_->maxSpeed ||
314  neighborObservationStatistics(1).angle > 45.0)) {
315  fail(observationAfterFastestSegment);
316  errorCategory = 2;
317  } else {
318  fail(observationAfterFastestSegment - 1);
319  errorCategory = 1;
320  }
321  } else if (observationAfterFastestSegment == track.end() - 1) {
322  if (neighborObservationStatistics(-1).speedAveraged <=
323  options_->maxSpeed &&
324  (neighborObservationStatistics(-1).speed >
325  options_->maxSpeed ||
326  neighborObservationStatistics(-2).angle > 45.0)) {
327  fail(observationAfterFastestSegment - 1);
328  errorCategory = 2;
329  } else {
330  fail(observationAfterFastestSegment);
331  errorCategory = 1;
332  }
333  } else if (neighborObservationStatistics(-1).speed >
334  options_->maxSpeed) {
335  fail(observationAfterFastestSegment - 1);
336  errorCategory = 4;
337  // Category 4: both segments surrounding observation have excessive speed
338  } else if (neighborObservationStatistics(1).speed >
339  options_->maxSpeed) {
340  fail(observationAfterFastestSegment);
341  errorCategory = 4;
342  } else if (neighborObservationStatistics(0).speedAveraged >
343  options_->maxSpeed) {
344  fail(observationAfterFastestSegment - 1);
345  errorCategory = 5;
346  // Category 5: observation before fastest segment would still begin a fast segment
347  // if observation after fastest segment were removed
348  } else if (neighborObservationStatistics(-1).speedAveraged >
349  options_->maxSpeed) {
350  fail(observationAfterFastestSegment);
351  errorCategory = 5;
352  // Category 5: observation after fastest segment would still end a fast segment if
353  // observation before fastest segment were removed
354  } else if (neighborObservationStatistics(-1).angle >
355  (45.0 + neighborObservationStatistics(0).angle)) {
356  fail(observationAfterFastestSegment - 1);
357  errorCategory = 6;
358  } else if (neighborObservationStatistics(0).angle >
359  45.0 + neighborObservationStatistics(-1).angle) {
360  fail(observationAfterFastestSegment);
361  errorCategory = 6;
362  } else if (neighborObservationStatistics(-2).angle > 45.0 &&
363  neighborObservationStatistics(-2).angle >
364  neighborObservationStatistics(1).angle) {
365  fail(observationAfterFastestSegment - 1);
366  errorCategory = 7;
367  } else if (neighborObservationStatistics(1).angle > 45.0) {
368  fail(observationAfterFastestSegment);
369  errorCategory = 7;
370  } else if (neighborObservationStatistics(-1).speed <
371  0.5 * std::min(
372  neighborObservationStatistics(1).speed,
373  meanSpeed)) {
374  fail(observationAfterFastestSegment - 1);
375  errorCategory = 8;
376  } else if (neighborObservationStatistics(1).speed <
377  0.5 * std::min(neighborObservationStatistics(-1).speed, meanSpeed)) {
378  fail(observationAfterFastestSegment);
379  errorCategory = 8;
380  } else {
381  double distanceSum = 0.0;
382  distanceSum = std::accumulate(observationAfterFastestSegment - 1,
383  observationAfterFastestSegment + 2, distanceSum,
384  [](double summed, TrackObservation& obs) {
385  return summed + obs.getObservationStatistics().distance;
386  });
387 
388  double distancePrevObsOmitted =
389  neighborObservationStatistics(-1).distanceAveraged +
390  neighborObservationStatistics(1).distance;
391  double distanceCurrentObsOmitted =
392  neighborObservationStatistics(-1).distance +
393  neighborObservationStatistics(0).distanceAveraged;
394  util::Duration timeSum = (observationAfterFastestSegment + 1)->get().getTime() - (
395  observationAfterFastestSegment - 2)->get().getTime();
396  if (options_->testingMode.value()) {
397  diagnostics_->storeDistanceSum(distanceSum);
398  diagnostics_->storeDistancePrevObsOmitted(distancePrevObsOmitted);
399  diagnostics_->storeDistanceCurrentObsOmitted(distanceCurrentObsOmitted);
400  double timeDouble = timeSum.toSeconds();
401  diagnostics_->storeTimeSum(timeDouble);
402  }
403  if (distancePrevObsOmitted < distanceCurrentObsOmitted - std::max(
404  options_->spatialResolution.value(), 0.1 * distanceSum)) {
405  fail(observationAfterFastestSegment - 1);
406  errorCategory = 9;
407  } else if (distanceCurrentObsOmitted < (
408  distancePrevObsOmitted - std::max(
409  options_->spatialResolution.value(), 0.1 * distanceSum))) {
410  fail(observationAfterFastestSegment);
411  errorCategory = 9;
412  } else if (timeSum <= four_days && timeSum.toSeconds() > 0 &&
413  std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) > 0.0) {
414  double previousSegmentDistanceProportion =
415  // Prev segment dist/(prev segment distance + distAveragedCurrentObservation)
416  neighborObservationStatistics(-1).distance /
417  distanceCurrentObsOmitted;
418  double previousObservationDistanceAveragedProportion =
419  // Previous observation distAveraged / (prev obs distAveraged + next segment distance)
420  neighborObservationStatistics(-1).
421  distanceAveraged / distancePrevObsOmitted;
422  double previousSegmentTimeProportion =
423  static_cast<double>(((observationAfterFastestSegment - 1)->get().getTime() -
424  (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
425  timeSum.toSeconds();
426  double previousAndFastestSegmentTimeProportion =
427  static_cast<double>(((observationAfterFastestSegment->get().getTime()) -
428  (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
429  timeSum.toSeconds();
430  if (options_->testingMode.value()) {
431  diagnostics_->storePreviousSegmentDistanceProportion(previousSegmentDistanceProportion);
432  diagnostics_->storePreviousObservationDistanceAveragedProportion(
433  previousObservationDistanceAveragedProportion);
434  diagnostics_->storePreviousSegmentTimeProportion(previousSegmentTimeProportion);
435  diagnostics_->storePreviousAndFastestSegmentTimeProportion(
436  previousAndFastestSegmentTimeProportion);
437  }
438  if (std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion) >
439  0.1 + std::abs(previousObservationDistanceAveragedProportion -
440  previousAndFastestSegmentTimeProportion)) {
441  // previous segment's spatial and temporal lengths are significantly more disproportionate
442  // when compared to a larger portion of track
443  // than the equivalents for the post-fastest segment
444  fail(observationAfterFastestSegment - 1);
445  errorCategory = 10;
446  } else if (std::abs(previousObservationDistanceAveragedProportion -
447  previousAndFastestSegmentTimeProportion) > 0.1 +
448  std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion)) {
449  // next segment after fastest has spatial and temporal lengths that are significantly more
450  // disproportionate than the segment before the fastest
451  fail(observationAfterFastestSegment);
452  errorCategory = 10;
453  } else {
454  fail(observationAfterFastestSegment);
455  }
456  oops::Log::trace() << "CheckShipTrack: proportions " << previousSegmentDistanceProportion <<
457  " " << previousSegmentTimeProportion <<
458  " " << previousObservationDistanceAveragedProportion << " "
459  << previousAndFastestSegmentTimeProportion << " speeds: " << meanSpeed
460  << " " << neighborObservationStatistics(-1).speed << " " <<
461  neighborObservationStatistics(0).speed << " " <<
462  neighborObservationStatistics(1).speed << " [m/s]" << std::endl;
463  }
464  if (errorCategory == 9 || std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) == 0.0) {
465  oops::Log::trace() << "CheckShipTrack: Dist check, station id: " <<
466  options_->stationIdVariable.value().get().variable() << std::endl <<
467  " error category: " << errorCategory << std::endl <<
468  " distances: " << distanceSum * 0.001 << " " <<
469  distancePrevObsOmitted * 0.001 << " " <<
470  distanceCurrentObsOmitted * 0.001 << " " <<
471  (distancePrevObsOmitted - distanceCurrentObsOmitted) * 0.001 <<
472  " " << std::max(options_->spatialResolution.value(),
473  0.1 * distanceSum) *
474  0.001 << "[km]" << std::endl;
475  }
476  }
477  if (errorCategory == 0 || ((rejectedObservation->get().getObservationStatistics().
478  speedAveraged) >
479  options_->maxSpeed.value())) {
480  oops::Log::trace() << "CheckShipTrack: cannot decide between observation and previous, " <<
481  "rejecting both." << std::endl;
482  errorCategory += 100;
483  if (options_->testingMode.value() && firstIterativeRemoval) {
484  std::vector<size_t> observationNumbersAroundFastest{
485  (observationAfterFastestSegment - 1)->get().getObservationNumber(),
486  observationAfterFastestSegment->get().getObservationNumber()};
487  diagnostics_->storeFirstIterativeRemovalInfo(
488  std::make_pair(observationNumbersAroundFastest, errorCategory));
489  }
490  if (rejectedObservation == observationAfterFastestSegment) {
491  fail(observationAfterFastestSegment - 1);
492  } else {
493  fail(observationAfterFastestSegment);
494  }
495  track.erase(observationAfterFastestSegment - 1, observationAfterFastestSegment);
496  } else {
497  if (options_->testingMode.value() && firstIterativeRemoval) {
498  std::vector<size_t> rejectedObservationNumber{rejectedObservation->get().
499  getObservationNumber()};
500  diagnostics_->storeFirstIterativeRemovalInfo(
501  std::make_pair(rejectedObservationNumber,
502  errorCategory));
503  }
504  track.erase(rejectedObservation);
505  }
506  oops::Log::trace() << "Error category: " << errorCategory << std::endl;
507 }
508 /// \todo Trace output will need to be changed to match that of OPS (indices, LWin)
509 
510 /// \brief Calculates all of the statistics that require only two
511 /// adjacent \p TrackObservations, storing within the righthand observation.
512 ///
513 /// This includes
514 /// distance between the two observations,
515 /// time difference between the observations, speed between the
516 /// observations, and if the
517 /// observations are recorded for the same time. Calls function to
518 /// increment track-wise counters based on
519 /// results.
521  TrackObservation& prevObs, bool firstIteration,
522  const TrackCheckShipParameters &options) {
523  this->setDistance(TrackCheckShip::distance(prevObs, *this));
524  (this->observationStatistics_.distance > options.spatialResolution) ?
525  this->setSpeed(TrackCheckShip::speedEstimate(*this, prevObs, options)) :
526  this->setSpeed(0.0);
527  this->setTimeDifference(getTime() - prevObs.getTime());
528  if (!(this->observationStatistics_.timeDifference.toSeconds())) {
529  if (this->observationStatistics_.distance >
530  options.spatialResolution) {
531  prevObs.setSimultaneous(true);
532  }
533  setSimultaneous(true);
534  }
535  if (firstIteration) {
536  adjustTwoObservationStatistics(options);
537  }
538 }
539 
540 /// Calculates all of the statistics that require three
541 /// adjacent \p TrackObservations,
542 /// storing within the middle observation. This includes
543 /// distance between two alternating observations,
544 /// speed between these alternating observations (if the middle
545 /// observation was not recorded), and the
546 /// angle formed by the three-observation track segment.
547 /// Calls function to increment track-wise counters based on results.
549  const TrackObservation& prevObs, const TrackObservation& nextObs,
550  bool firstIteration, const TrackCheckShipParameters &options) {
551  this->setDistanceAveraged(TrackCheckShip::distance(prevObs, nextObs));
552  this->setSpeedAveraged(speedEstimate(prevObs, nextObs, options));
553  if (std::min(this->observationStatistics_.distance,
555  options.spatialResolution) {
556  this->observationStatistics_.angle = angle(prevObs, *this, nextObs);
557  }
558  if (firstIteration) {
559  adjustThreeObservationStatistics();
560  }
561 }
562 
563 /// This performs all of the necessary calculations based on the
564 /// observations' locations and timestamps by calling
565 /// \p calculateTwoObservationValues and \p calculateThreeObservationValues for the
566 /// non-edge-case observations.
568  const std::vector<std::reference_wrapper<TrackObservation>> &trackObservations,
569  CalculationMethod calculationMethod) const {
570  if (trackObservations.size()) {
571  for (size_t obsIdx = 1; obsIdx < trackObservations.size(); obsIdx++) {
572  TrackObservation &obs = trackObservations[obsIdx].get();
573  TrackObservation &prevObs = trackObservations[obsIdx - 1].get();
574  obs.calculateTwoObservationValues(prevObs, calculationMethod == FIRSTITERATION, *options_);
575  if (obsIdx > 1) {
576  const TrackObservation &prevPrevObs = trackObservations[obsIdx - 2].get();
577  prevObs.calculateThreeObservationValues(prevPrevObs, obs,
578  calculationMethod == FIRSTITERATION, *options_);
579  }
580  if (calculationMethod == FIRSTITERATION && (obsIdx == trackObservations.size() - 1)) {
581  int potentialDenominator = trackObservations.size() - 1 -
582  obs.getFullTrackStatistics()->numShort_ - obs.getFullTrackStatistics()->numFast_;
583  (obs.getFullTrackStatistics()->meanSpeed_) = (obs.getFullTrackStatistics()->sumSpeed_) /
584  std::max(1, potentialDenominator);
585  }
586  }
587  if (options_->testingMode.value() && calculationMethod != MAINLOOP) {
588  std::vector<TrackCheckShip::ObservationStatistics> obsStats;
589  for (size_t obsIdx = 0; obsIdx < trackObservations.size(); ++obsIdx) {
590  obsStats.push_back(trackObservations[obsIdx].get().getObservationStatistics());
591  }
592  auto trackStats = *(trackObservations[0].get().getFullTrackStatistics());
593  if (calculationMethod == FIRSTITERATION)
594  diagnostics_->storeInitialCalculationResults(std::make_pair(obsStats, trackStats));
595  else if (calculationMethod == SIMULTANEOUSDEFERRAL)
596  diagnostics_->storeCalculatedResultsSimultaneousDeferred(obsStats);
597  }
598  }
599 }
600 
601 /// \brief Keeps track of 0-distanced, short, and fast track segments,
602 /// as well as incrementing \p sumSpeed_ for normal track segments.
604 (const TrackCheckShipParameters &options) const {
605  util::Duration hour{"PT1H"};
606  if (getObservationStatistics().distance == 0.0)
607  getFullTrackStatistics()->numDist0_++;
608  if (getObservationStatistics().timeDifference < hour) {
609  getFullTrackStatistics()->numShort_++;
610  } else if (getObservationStatistics().speed >= options.maxSpeed) {
611  getFullTrackStatistics()->numFast_++;
612  } else {
613  getFullTrackStatistics()->sumSpeed_ += getObservationStatistics().speed;
614  }
615 }
616 
617 /// \brief Increments number of bends if angle is greater than or equal to 90 degrees
619  if (getObservationStatistics().angle >= 90.0)
620  getFullTrackStatistics()->numBends_++;
621 }
622 
625 {
626  return observationStatistics_;
627 }
628 
629 const std::shared_ptr<TrackCheckShip::TrackStatistics>
631 {
632  return fullTrackStatistics_;
633 }
634 
635 /// Sets the calling observation as simultaneous and increments the track-wise
636 /// \p numSimultaneous_ counter.
638  assert(simul); // reverting a simultaneous obs to normal is not yet implemented
639  if (!this->observationStatistics_.simultaneous)
640  this->fullTrackStatistics_->numSimultaneous_++;
641  this->observationStatistics_.simultaneous = simul;
642 }
644  this->observationStatistics_.distance = dist;
645 }
647  this->observationStatistics_.timeDifference = tDiff;
648 }
650  this->observationStatistics_.speed = speed;
651 }
653  this->observationStatistics_.angle = angle;
654 }
656  this->observationStatistics_.distanceAveraged = distAvg;
657 }
659  this->observationStatistics_.speedAveraged = speedAvg;
660 }
661 } // namespace ufo
ufo::TrackCheckShipDiagnostics
Definition: TrackCheckShipDiagnostics.h:17
ufo::TrackCheckShip::distance
static double distance(const TrackObservation &a, const TrackObservation &b)
Extends distance for TrackObservation inputs a and b.
Definition: src/ufo/filters/TrackCheckShip.h:184
ufo::TrackCheckShip::TrackObservation::getObservationStatistics
const ObservationStatistics & getObservationStatistics() const
Definition: TrackCheckShip.cc:624
ufo::TrackCheckShip::TrackObservation::calculateTwoObservationValues
void calculateTwoObservationValues(TrackObservation &prevObs, bool firstIteration, const TrackCheckShipParameters &options)
Calculates all of the statistics that require only two adjacent TrackObservations,...
Definition: TrackCheckShip.cc:520
ufo::Variables
Definition: src/ufo/filters/Variables.h:24
ufo::TrackCheckShip::SIMULTANEOUSDEFERRAL
@ SIMULTANEOUSDEFERRAL
Definition: src/ufo/filters/TrackCheckShip.h:214
ufo::TrackCheckShip::TrackObservation::getObservationNumber
size_t getObservationNumber() const
Definition: TrackCheckShip.cc:105
ufo::TrackCheckShip::calculateTrackSegmentProperties
void calculateTrackSegmentProperties(const std::vector< std::reference_wrapper< TrackObservation >> &trackObservations, CalculationMethod calculationMethod=MAINLOOP) const
Definition: TrackCheckShip.cc:567
ufo::TrackCheckUtils::getValidObservationIds
std::vector< size_t > getValidObservationIds(const std::vector< bool > &apply, const std::shared_ptr< ioda::ObsDataVector< int >> &flags)
Definition: TrackCheckUtils.cc:66
ufo::RecursiveSplitter::multiElementGroups
MultiElementGroupRange multiElementGroups() const
Return the range of equivalence classes consisting of more than one element.
Definition: src/ufo/utils/RecursiveSplitter.h:293
ufo_radiancerttov_utils_mod::debug
logical, public debug
Definition: ufo_radiancerttov_utils_mod.F90:100
ufo::FilterBase::obsdb_
ioda::ObsSpace & obsdb_
Definition: FilterBase.h:59
ufo::TrackCheckShip::collectTrackObservations
std::vector< TrackObservation > collectTrackObservations(std::vector< size_t >::const_iterator trackObsIndicesBegin, std::vector< size_t >::const_iterator trackObsIndicesEnd, const std::vector< size_t > &validObsIds, const TrackCheckUtils::ObsGroupLocationTimes &obsLoc) const
Definition: TrackCheckShip.cc:217
ufo::TrackCheckShip::diagnostics
const TrackCheckShipDiagnostics * diagnostics() const
Definition: TrackCheckShip.cc:86
ufo::TrackCheckUtils::CheckCounter
Definition: TrackCheckUtils.h:69
TrackCheckShip.h
ufo::TrackCheckShip::TrackObservation::getFullTrackStatistics
const std::shared_ptr< TrackStatistics > getFullTrackStatistics() const
Definition: TrackCheckShip.cc:630
ufo::FilterBase
FilterBase: Base class for UFO QC filters.
Definition: FilterBase.h:42
ufo::TrackCheckShip::TrackObservation::setDistance
void setDistance(double dist)
Definition: TrackCheckShip.cc:643
ufo::TrackCheckShip::TrackStatistics
A container for all track-wise counters and calculations that indicate the overall quality of the tra...
Definition: src/ufo/filters/TrackCheckShip.h:101
ufo::RecursiveSplitter
Partitions an array into groups of elements equivalent according to certain criteria.
Definition: src/ufo/utils/RecursiveSplitter.h:63
ufo::TrackCheckShip::speedEstimate
static double speedEstimate(const TrackCheckShip::TrackObservation &obs1, const TrackCheckShip::TrackObservation &obs2, const TrackCheckShipParameters &options)
Estimate of speed as calculated in Ops_CheckShipTrack.
Definition: TrackCheckShip.cc:53
ufo::TrackCheckShip::TrackObservation::setDistanceAveraged
void setDistanceAveraged(double distAvg)
Definition: TrackCheckShip.cc:655
ufo::TrackCheckShip::TrackObservation::registerCheckResult
void registerCheckResult(const TrackCheckUtils::CheckResult &result)
Definition: TrackCheckShip.cc:100
ufo::TrackCheckUtils::ObsGroupLocationTimes::datetimes
std::vector< util::DateTime > datetimes
Definition: TrackCheckUtils.h:55
ufo
Definition: RunCRTM.h:27
ufo::TrackCheckShip::TrackObservation
Definition: src/ufo/filters/TrackCheckShip.h:128
ufo::Constants::rad2deg
static constexpr double rad2deg
Definition: Constants.h:22
ufo::TrackCheckShip::print
void print(std::ostream &) const override
Definition: TrackCheckShip.cc:137
ufo::TrackCheckShip::earlyBreak
bool earlyBreak(const std::vector< std::reference_wrapper< TrackObservation >> &trackObs) const
Definition: TrackCheckShip.cc:246
ufo::TrackCheckShip::MAINLOOP
@ MAINLOOP
Definition: src/ufo/filters/TrackCheckShip.h:214
ufo::TrackCheckShip::options_
std::unique_ptr< TrackCheckShipParameters > options_
Definition: src/ufo/filters/TrackCheckShip.h:199
ufo::FilterBase::flags_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Definition: FilterBase.h:61
ufo::QCflags::track
constexpr int track
Definition: QCflags.h:26
ufo::TrackCheckShip::flagRejectedTrackObservations
void flagRejectedTrackObservations(std::vector< size_t >::const_iterator trackObsIndicesBegin, std::vector< size_t >::const_iterator trackObsIndicesEnd, const std::vector< size_t > &validObsIds, const std::vector< TrackObservation > &trackObservations, std::vector< bool > &isRejected) const
Definition: TrackCheckShip.cc:124
ufo::TrackCheckUtils::groupObservationsByStation
void groupObservationsByStation(const std::vector< size_t > &validObsIds, RecursiveSplitter &splitter, const eckit::Configuration &config, const ioda::ObsSpace &obsdb)
Definition: TrackCheckUtils.cc:75
ufo::TrackCheckShip::diagnostics_
std::unique_ptr< TrackCheckShipDiagnostics > diagnostics_
Definition: src/ufo/filters/TrackCheckShip.h:200
ufo::TrackCheckShip::ObservationStatistics
Relevant calculated values that are specific to each pair or triplet of adjacent/alternating observat...
Definition: src/ufo/filters/TrackCheckShip.h:71
ufo::TrackCheckShip::applyFilter
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
Definition: TrackCheckShip.cc:151
ufo::TrackCheckUtils::collectObservationsLocations
ObsGroupLocationTimes collectObservationsLocations(const ioda::ObsSpace &obsdb)
Definition: TrackCheckUtils.cc:145
ufo::TrackCheckShip::removeFaultyObservation
void removeFaultyObservation(std::vector< std::reference_wrapper< TrackObservation > > &track, const std::vector< std::reference_wrapper< TrackObservation > >::iterator &it, bool firstIterativeRemoval=false) const
Chooses which of the observations surrounding the speediest segment to remove, flagging it accordingl...
Definition: TrackCheckShip.cc:288
ufo::TrackCheckShip::~TrackCheckShip
~TrackCheckShip() override
Definition: TrackCheckShip.cc:111
ufo::TrackCheckUtils::ObsGroupLocationTimes::latitudes
std::vector< float > latitudes
Definition: TrackCheckUtils.h:53
ufo::abs
util::Duration abs(const util::Duration &duration)
Definition: TrackCheckUtils.h:31
TrackCheckShipParameters.h
TrackCheckShipDiagnostics.h
ufo::TrackCheckShip::TrackObservation::calculateThreeObservationValues
void calculateThreeObservationValues(const TrackObservation &prevObs, const TrackObservation &nextObs, bool firstIteration, const TrackCheckShipParameters &options)
Definition: TrackCheckShip.cc:548
ufo::TrackCheckShipParameters::temporalResolution
oops::Parameter< util::Duration > temporalResolution
Definition: TrackCheckShipParameters.h:30
ufo::TrackCheckShip::TrackCheckShip
TrackCheckShip(ioda::ObsSpace &obsdb, const eckit::Configuration &config, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
Definition: TrackCheckShip.cc:36
ufo::TrackCheckShip::removeSimultaneousObservations
std::vector< std::reference_wrapper< TrackObservation > > removeSimultaneousObservations(const std::vector< std::reference_wrapper< TrackObservation >> &trackObs) const
Filters out all observations that have been marked simultaneous for rechecking at the end.
Definition: TrackCheckShip.cc:276
ioda::ObsDataVector< int >
ufo::TrackCheckShip::TrackObservation::TrackObservation
TrackObservation(double latitude, double longitude, const util::DateTime &time, const std::shared_ptr< TrackStatistics > &trackStatistics, const std::shared_ptr< TrackCheckUtils::CheckCounter > &checkCounter, size_t observationNumber)
Definition: TrackCheckShip.cc:91
ufo::FilterBase::config_
const eckit::LocalConfiguration config_
Definition: FilterBase.h:60
ufo::TrackCheckShipParameters
Options controlling the operation of the ship track check filter.
Definition: TrackCheckShipParameters.h:26
ufo::TrackCheckShip::TrackObservation::adjustTwoObservationStatistics
void adjustTwoObservationStatistics(const TrackCheckShipParameters &options) const
Keeps track of 0-distanced, short, and fast track segments, as well as incrementing sumSpeed_ for nor...
Definition: TrackCheckShip.cc:604
ufo::TrackCheckShip::ObservationStatistics::distance
double distance
Definition: src/ufo/filters/TrackCheckShip.h:82
ufo::TrackCheckShip::TrackObservation::adjustThreeObservationStatistics
void adjustThreeObservationStatistics() const
Increments number of bends if angle is greater than or equal to 90 degrees.
Definition: TrackCheckShip.cc:618
ufo::TrackCheckShip::CalculationMethod
CalculationMethod
Definition: src/ufo/filters/TrackCheckShip.h:214
ufo::TrackCheckShip::TrackObservation::observationStatistics_
ObservationStatistics observationStatistics_
Definition: src/ufo/filters/TrackCheckShip.h:176
ufo::TrackCheckShip::TrackObservation::getLocation
const TrackCheckUtils::Point & getLocation() const
Definition: src/ufo/filters/TrackCheckShip.h:135
ufo::TrackCheckUtils::ObsGroupLocationTimes
Locations/times of all observations processed by the track checking filter.
Definition: TrackCheckUtils.h:51
ufo::TrackCheckShip::TrackObservation::setSimultaneous
void setSimultaneous(bool simul)
Definition: TrackCheckShip.cc:637
ufo::TrackCheckShip::TrackObservation::setTimeDifference
void setTimeDifference(util::Duration tDiff)
Definition: TrackCheckShip.cc:646
Constants.h
ufo::TrackCheckShip::TrackObservation::getTime
const util::DateTime & getTime() const
Definition: src/ufo/filters/TrackCheckShip.h:138
ufo::TrackCheckUtils::sortTracksChronologically
void sortTracksChronologically(const std::vector< size_t > &validObsIds, RecursiveSplitter &splitter, const ioda::ObsSpace &obsdb)
Definition: TrackCheckUtils.cc:135
ufo::TrackCheckShipParameters::spatialResolution
oops::Parameter< double > spatialResolution
Definition: TrackCheckShipParameters.h:41
ufo::TrackCheckShip::TrackObservation::setSpeed
void setSpeed(double speed)
Definition: TrackCheckShip.cc:649
ufo::TrackCheckShip::FIRSTITERATION
@ FIRSTITERATION
Definition: src/ufo/filters/TrackCheckShip.h:214
ufo::TrackCheckUtils::CheckResult::FAILED
@ FAILED
ufo::TrackCheckUtils::flagRejectedObservations
void flagRejectedObservations(const std::vector< bool > &isRejected, std::vector< std::vector< bool > > &flagged)
Definition: TrackCheckUtils.cc:160
RecursiveSplitter.h
ufo::TrackCheckShipParameters::maxSpeed
oops::Parameter< double > maxSpeed
Maximum speed (before marking as fast) in km/s.
Definition: TrackCheckShipParameters.h:46
ufo::TrackCheckUtils::ObsGroupLocationTimes::longitudes
std::vector< float > longitudes
Definition: TrackCheckUtils.h:54
ufo::TrackCheckShip::angle
static float angle(const TrackCheckShip::TrackObservation &a, const TrackCheckShip::TrackObservation &b, const TrackCheckShip::TrackObservation &c)
Returns the angle in degrees (rounded to the nearest .1 degree) formed by the track segment going fro...
Definition: TrackCheckShip.cc:73
ufo::TrackCheckUtils::CheckResult
CheckResult
Definition: TrackCheckUtils.h:42
ufo::TrackCheckShip::TrackObservation::setSpeedAveraged
void setSpeedAveraged(double speedAvg)
Definition: TrackCheckShip.cc:658
ufo::TrackCheckShip::TrackObservation::setAngle
void setAngle(double angle)
Definition: TrackCheckShip.cc:652