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"
40 :
FilterBase(obsdb, parameters, flags, obserr), options_(parameters)
56 util::Duration temporalDistance =
abs(obs1.
getTime() -
61 double speedEst = 0.0;
62 if (dist > spatialRes) {
63 speedEst = (dist - 0.5 * spatialRes) /
64 std::max(temporalDistance.toSeconds(),
65 (tempRes.toSeconds()));
80 Eigen::Vector3f disp1{locB[0]-locA[0], locB[1]-locA[1], locB[2]-locA[2]};
81 Eigen::Vector3f disp2{locC[0]-locB[0], locC[1]-locB[1], locC[2]-locB[2]};
82 auto retValue = std::acos(disp1.dot(disp2) / (disp1.norm() * disp2.norm()));
84 return std::round(retValue * 10.0f) * 0.1f;
93 double latitude,
double longitude,
94 const util::DateTime &time,
95 std::shared_ptr<TrackStatistics>
const& ts,
96 std::shared_ptr<TrackCheckUtils::CheckCounter>
const &checkCounter,
97 size_t observationNumber)
98 : obsLocationTime_(latitude, longitude, time), fullTrackStatistics_(ts),
99 observationNumber_(observationNumber), rejected_(false) {}
103 return observationNumber_;
121 std::vector<size_t>::const_iterator trackObsIndicesBegin,
122 std::vector<size_t>::const_iterator trackObsIndicesEnd,
123 const std::vector<size_t> &validObsIds,
124 const std::vector<TrackObservation> &trackObservations,
125 std::vector<bool> &isRejected)
const {
126 auto trackObsIndexIt = trackObsIndicesBegin;
127 auto trackObsIt = trackObservations.begin();
128 for (; trackObsIndexIt != trackObsIndicesEnd; ++trackObsIndexIt, ++trackObsIt)
129 isRejected[validObsIds[*trackObsIndexIt]] = trackObsIt->rejected();
133 os <<
"TrackCheckShip: config = " <<
options_ << std::endl;
157 std::vector<std::vector<bool>> & flagged)
const {
160 const std::vector<size_t> validObsIds
169 std::vector<bool> isRejected(obsLocTime.
latitudes.size(),
false);
170 size_t trackNumber = 0;
173 std::string stationId = std::to_string(trackNumber);
175 track.begin(),
track.end(), validObsIds, obsLocTime);
176 std::vector<std::reference_wrapper<TrackObservation>> trackObservationsReferences;
177 trackObservationsReferences.reserve(trackObservations.size());
178 std::transform(trackObservations.begin(), trackObservations.end(),
179 std::back_inserter(trackObservationsReferences),
181 return std::ref<TrackObservation>(obs); });
183 CalculationMethod::FIRSTITERATION);
184 if (!trackObservationsReferences.empty() &&
185 this->options_.core.earlyBreakCheck &&
189 bool firstIterativeRemoval =
true;
190 while (trackObservationsReferences.size() >= 3) {
193 auto maxSpeedReferenceIterator = std::max_element(
194 trackObservationsReferences.begin(), trackObservationsReferences.end(),
196 return a.getObservationStatistics().speed <
197 b.getObservationStatistics().speed;});
198 auto maxSpeedValue = maxSpeedReferenceIterator->get().getObservationStatistics().speed;
202 auto maxSpeedAngle = std::max(
203 (maxSpeedReferenceIterator - 1)->get().getObservationStatistics().
angle,
204 maxSpeedReferenceIterator->get().getObservationStatistics().angle);
205 if (maxSpeedAngle <= 90.0) {
210 trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval,
212 firstIterativeRemoval =
false;
215 auto rejectedCount = std::count_if(trackObservations.begin(), trackObservations.end(),
218 oops::Log::trace() <<
"CheckShipTrack: track " << stationId <<
" NumRej " <<
219 rejectedCount <<
" out of " << trackObservations.size() <<
220 " reports rejected. *** Reject whole track ***\n";
225 validObsIds, trackObservations, isRejected);
233 std::vector<size_t>::const_iterator trackObsIndicesBegin,
234 std::vector<size_t>::const_iterator trackObsIndicesEnd,
235 const std::vector<size_t> &validObsIds,
237 std::vector<TrackObservation> trackObservations;
238 trackObservations.reserve(trackObsIndicesEnd - trackObsIndicesBegin);
239 std::shared_ptr<TrackStatistics> trackStatistics(
new TrackStatistics());
241 size_t observationNumber = 0;
242 for (std::vector<size_t>::const_iterator it = trackObsIndicesBegin;
243 it != trackObsIndicesEnd; ++it) {
244 const size_t obsId = validObsIds[*it];
247 obsLocTime.
datetimes[obsId], trackStatistics,
252 return trackObservations;
266 &trackObs,
const std::string trackId)
const {
267 bool breakResult =
false;
268 const auto& trackStats = *(trackObs[0].get().getFullTrackStatistics());
274 * trackStats.numShort_ + trackStats.numFast_) + trackStats.numBends_)
275 >= (trackObs.size() - 1)) {
276 oops::Log::trace() <<
"ShipTrackCheck: " << trackId <<
"\n" <<
277 "Time difference < 1 hour: " << trackStats.numShort_ <<
"\n" <<
278 "Fast: " << trackStats.numFast_ <<
"\n" <<
279 "Bends: " << trackStats.numBends_ <<
"\n" <<
280 "Total observations: " << trackObs.size() <<
"\n" <<
281 "Track was not checked." << std::endl;
293 std::vector<std::reference_wrapper<TrackObservation>> &
track,
294 const std::vector<std::reference_wrapper<TrackObservation>>::iterator
295 &observationAfterFastestSegment,
296 bool firstIterativeRemoval,
const std::string trackId)
const {
297 int errorCategory = 0;
298 util::Duration four_days{
"P4D"};
299 auto rejectedObservation = observationAfterFastestSegment;
301 auto fail = [&rejectedObservation](
302 const std::vector<std::reference_wrapper<TrackObservation>>::iterator &iter) {
303 iter->get().setRejected(
true);
304 rejectedObservation = iter;
306 auto neighborObservationStatistics = [&observationAfterFastestSegment](
int index) ->
308 return (observationAfterFastestSegment + index)->get().getObservationStatistics();
310 auto meanSpeed = observationAfterFastestSegment->get().getFullTrackStatistics()->meanSpeed_;
311 if (observationAfterFastestSegment ==
track.begin() + 1) {
313 if (neighborObservationStatistics(0).speedAveraged <=
315 (neighborObservationStatistics(1).speed >
317 neighborObservationStatistics(1).angle > 45.0)) {
318 fail(observationAfterFastestSegment);
321 fail(observationAfterFastestSegment - 1);
324 }
else if (observationAfterFastestSegment ==
track.end() - 1) {
325 if (neighborObservationStatistics(-1).speedAveraged <=
327 (neighborObservationStatistics(-1).speed >
329 neighborObservationStatistics(-2).
angle > 45.0)) {
330 fail(observationAfterFastestSegment - 1);
333 fail(observationAfterFastestSegment);
336 }
else if (neighborObservationStatistics(-1).speed >
338 fail(observationAfterFastestSegment - 1);
341 }
else if (neighborObservationStatistics(1).speed >
343 fail(observationAfterFastestSegment);
345 }
else if (neighborObservationStatistics(0).speedAveraged >
347 fail(observationAfterFastestSegment - 1);
351 }
else if (neighborObservationStatistics(-1).speedAveraged >
353 fail(observationAfterFastestSegment);
357 }
else if (neighborObservationStatistics(-1).
angle >
358 (45.0 + neighborObservationStatistics(0).
angle)) {
359 fail(observationAfterFastestSegment - 1);
361 }
else if (neighborObservationStatistics(0).
angle >
362 45.0 + neighborObservationStatistics(-1).
angle) {
363 fail(observationAfterFastestSegment);
365 }
else if (neighborObservationStatistics(-2).
angle > 45.0 &&
366 neighborObservationStatistics(-2).
angle >
367 neighborObservationStatistics(1).
angle) {
368 fail(observationAfterFastestSegment - 1);
370 }
else if (neighborObservationStatistics(1).
angle > 45.0) {
371 fail(observationAfterFastestSegment);
373 }
else if (neighborObservationStatistics(-1).speed <
375 neighborObservationStatistics(1).speed,
377 fail(observationAfterFastestSegment - 1);
379 }
else if (neighborObservationStatistics(1).speed <
380 0.5 * std::min(neighborObservationStatistics(-1).speed, meanSpeed)) {
381 fail(observationAfterFastestSegment);
384 double distanceSum = 0.0;
385 distanceSum = std::accumulate(observationAfterFastestSegment - 1,
386 observationAfterFastestSegment + 2, distanceSum,
391 double distancePrevObsOmitted =
392 neighborObservationStatistics(-1).distanceAveraged +
393 neighborObservationStatistics(1).distance;
394 double distanceCurrentObsOmitted =
395 neighborObservationStatistics(-1).distance +
396 neighborObservationStatistics(0).distanceAveraged;
397 util::Duration timeSum = (observationAfterFastestSegment + 1)->get().getTime() - (
398 observationAfterFastestSegment - 2)->get().getTime();
401 diagnostics_->storeDistancePrevObsOmitted(distancePrevObsOmitted);
402 diagnostics_->storeDistanceCurrentObsOmitted(distanceCurrentObsOmitted);
403 double timeDouble = timeSum.toSeconds();
406 if (distancePrevObsOmitted < distanceCurrentObsOmitted - std::max(
408 fail(observationAfterFastestSegment - 1);
410 }
else if (distanceCurrentObsOmitted < (
411 distancePrevObsOmitted - std::max(
413 fail(observationAfterFastestSegment);
415 }
else if (timeSum <= four_days && timeSum.toSeconds() > 0 &&
416 std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) > 0.0) {
417 double previousSegmentDistanceProportion =
419 neighborObservationStatistics(-1).distance /
420 distanceCurrentObsOmitted;
421 double previousObservationDistanceAveragedProportion =
423 neighborObservationStatistics(-1).
424 distanceAveraged / distancePrevObsOmitted;
425 double previousSegmentTimeProportion =
426 static_cast<double>(((observationAfterFastestSegment - 1)->get().getTime() -
427 (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
429 double previousAndFastestSegmentTimeProportion =
430 static_cast<double>(((observationAfterFastestSegment->get().getTime()) -
431 (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
434 diagnostics_->storePreviousSegmentDistanceProportion(previousSegmentDistanceProportion);
435 diagnostics_->storePreviousObservationDistanceAveragedProportion(
436 previousObservationDistanceAveragedProportion);
437 diagnostics_->storePreviousSegmentTimeProportion(previousSegmentTimeProportion);
438 diagnostics_->storePreviousAndFastestSegmentTimeProportion(
439 previousAndFastestSegmentTimeProportion);
441 if (
std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion) >
442 0.1 +
std::abs(previousObservationDistanceAveragedProportion -
443 previousAndFastestSegmentTimeProportion)) {
447 fail(observationAfterFastestSegment - 1);
449 }
else if (
std::abs(previousObservationDistanceAveragedProportion -
450 previousAndFastestSegmentTimeProportion) > 0.1 +
451 std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion)) {
454 fail(observationAfterFastestSegment);
457 fail(observationAfterFastestSegment);
459 oops::Log::trace() <<
"CheckShipTrack: proportions " << previousSegmentDistanceProportion <<
460 " " << previousSegmentTimeProportion <<
461 " " << previousObservationDistanceAveragedProportion <<
" "
462 << previousAndFastestSegmentTimeProportion <<
" speeds: " << meanSpeed
463 <<
" " << neighborObservationStatistics(-1).speed <<
" " <<
464 neighborObservationStatistics(0).speed <<
" " <<
465 neighborObservationStatistics(1).speed <<
" [m/s]" << std::endl;
467 if (errorCategory == 9 || std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) == 0.0) {
468 oops::Log::trace() <<
"CheckShipTrack: Dist check, station id: " <<
469 trackId << std::endl <<
470 " error category: " << errorCategory << std::endl <<
471 " distances: " << distanceSum * 0.001 <<
" " <<
472 distancePrevObsOmitted * 0.001 <<
" " <<
473 distanceCurrentObsOmitted * 0.001 <<
" " <<
474 (distancePrevObsOmitted - distanceCurrentObsOmitted) * 0.001 <<
477 0.001 <<
"[km]" << std::endl;
480 if (errorCategory == 0 || ((rejectedObservation->get().getObservationStatistics().
483 oops::Log::trace() <<
"CheckShipTrack: cannot decide between station id " <<
484 trackId <<
" observations " <<
485 (observationAfterFastestSegment - 1)->get().getObservationNumber() <<
486 " " << observationAfterFastestSegment->get().getObservationNumber() <<
487 " rejecting both." << std::endl;
488 errorCategory += 100;
490 std::vector<size_t> observationNumbersAroundFastest{
491 (observationAfterFastestSegment - 1)->get().getObservationNumber(),
492 observationAfterFastestSegment->get().getObservationNumber()};
494 std::make_pair(observationNumbersAroundFastest, errorCategory));
496 if (rejectedObservation == observationAfterFastestSegment) {
497 fail(observationAfterFastestSegment - 1);
499 fail(observationAfterFastestSegment);
501 track.erase(observationAfterFastestSegment - 1, observationAfterFastestSegment);
504 std::vector<size_t> rejectedObservationNumber{rejectedObservation->get().
505 getObservationNumber()};
507 std::make_pair(rejectedObservationNumber,
510 oops::Log::trace() <<
"CheckShipTrack: rejecting station " << trackId <<
" observation " <<
511 rejectedObservation->get().getObservationNumber() <<
"\n" <<
512 "Error category: " << errorCategory <<
"\n" <<
513 "rejection candidates: " <<
514 (observationAfterFastestSegment - 1)->get().getObservationNumber() <<
515 " " << observationAfterFastestSegment->get().getObservationNumber() <<
516 "\n" <<
"speeds: " << (observationAfterFastestSegment - 1)->get().
517 getObservationStatistics().speed <<
" " <<
518 observationAfterFastestSegment->get().getObservationStatistics().speed <<
519 "\n" << (observationAfterFastestSegment - 1)->get().
520 getObservationStatistics().angle <<
" " <<
521 observationAfterFastestSegment->get().
522 getObservationStatistics().angle <<
"\n";
523 track.erase(rejectedObservation);
545 this->setTimeDifference(getTime() - prevObs.
getTime());
546 if (firstIteration) {
547 adjustTwoObservationStatistics(options);
552 this->setDistance(0.0);
554 this->setDistanceAveraged(0.0);
555 this->setSpeedAveraged(0.0);
571 this->setSpeedAveraged(
speedEstimate(prevObs, nextObs, options));
572 if (std::min(this->observationStatistics_.distance,
575 this->observationStatistics_.angle =
angle(prevObs, *
this, nextObs);
577 if (firstIteration) {
578 adjustThreeObservationStatistics();
587 const std::vector<std::reference_wrapper<TrackObservation>> &trackObservations,
589 if (trackObservations.size()) {
591 trackObservations[0].get().resetObservationCalculations();
592 for (
size_t obsIdx = 1; obsIdx < trackObservations.size(); obsIdx++) {
601 if (calculationMethod ==
FIRSTITERATION && (obsIdx == trackObservations.size() - 1)) {
602 int potentialDenominator = trackObservations.size() - 1 -
605 std::max(1, potentialDenominator);
609 std::vector<TrackCheckShip::ObservationStatistics> obsStats;
610 for (
size_t obsIdx = 0; obsIdx < trackObservations.size(); ++obsIdx) {
611 obsStats.push_back(trackObservations[obsIdx].get().getObservationStatistics());
613 auto trackStats = *(trackObservations[0].get().getFullTrackStatistics());
615 diagnostics_->storeInitialCalculationResults(std::make_pair(obsStats, trackStats));
624 util::Duration hour{
"PT1H"};
625 if (getObservationStatistics().timeDifference < hour) {
626 getFullTrackStatistics()->numShort_++;
627 }
else if (getObservationStatistics().speed >= options.
core.
maxSpeed) {
628 getFullTrackStatistics()->numFast_++;
630 getFullTrackStatistics()->sumSpeed_ += getObservationStatistics().speed;
636 if (getObservationStatistics().
angle >= 90.0)
637 getFullTrackStatistics()->numBends_++;
643 return observationStatistics_;
646 const std::shared_ptr<TrackCheckShip::TrackStatistics>
649 return fullTrackStatistics_;
653 this->observationStatistics_.distance = dist;
656 this->observationStatistics_.timeDifference = tDiff;
659 this->observationStatistics_.speed = speed;
662 this->observationStatistics_.angle =
angle;
665 this->observationStatistics_.distanceAveraged = distAvg;
668 this->observationStatistics_.speedAveraged = speedAvg;
Base class for UFO QC filters.
This class provides access to observations that may be held on multiple MPI ranks.
RecursiveSplitter splitObservationsIntoIndependentGroups(const std::vector< size_t > &validObsIds, bool opsCompatibilityMode=false) const
std::vector< size_t > getValidObservationIds(const std::vector< bool > &apply, const ioda::ObsDataVector< int > &flags, const Variables &filtervars, bool validIfAnyFilterVariablePassedQC=true) const
Return the IDs of observation locations that should be treated as valid by a filter.
void flagRejectedObservations(const std::vector< bool > &isRejected, std::vector< std::vector< bool > > &flagged) const
Update flags of observations held on the current MPI rank.
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Partitions an array into groups of elements equivalent according to certain criteria.
MultiElementGroupRange multiElementGroups() const
Return the range of equivalence classes consisting of more than one element.
const ObservationStatistics & getObservationStatistics() const
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)
void calculateThreeObservationValues(const TrackObservation &prevObs, const TrackObservation &nextObs, bool firstIteration, const TrackCheckShipParameters &options)
void resetObservationCalculations()
void setDistance(double dist)
size_t getObservationNumber() const
void setSpeedAveraged(double speedAvg)
const std::shared_ptr< TrackStatistics > getFullTrackStatistics() const
void adjustThreeObservationStatistics() const
Increments number of bends if angle is greater than or equal to 90 degrees.
void setTimeDifference(util::Duration tDiff)
void setRejected(bool rejectedInSweep)
void setSpeed(double speed)
const util::DateTime & getTime() const
ObservationStatistics observationStatistics_
void setAngle(double angle)
void calculateTwoObservationValues(TrackObservation &prevObs, bool firstIteration, const TrackCheckShipParameters &options)
Calculates all of the statistics that require only two adjacent TrackObservations,...
void adjustTwoObservationStatistics(const TrackCheckShipParameters &options) const
Keeps track of 0-distanced, short, and fast track segments, as well as incrementing sumSpeed_ for nor...
void setDistanceAveraged(double distAvg)
const TrackCheckUtils::Point & getLocation() const
oops::RequiredParameter< double > spatialResolution
oops::RequiredParameter< float > rejectionThreshold
oops::RequiredParameter< util::Duration > temporalResolution
oops::RequiredParameter< double > maxSpeed
Maximum speed (before marking as fast) in m/s.
~TrackCheckShip() override
void removeFaultyObservation(std::vector< std::reference_wrapper< TrackObservation > > &track, const std::vector< std::reference_wrapper< TrackObservation > >::iterator &it, bool firstIterativeRemoval, const std::string trackId) const
Chooses which of the observations surrounding the speediest segment to remove, flagging it accordingl...
void calculateTrackSegmentProperties(const std::vector< std::reference_wrapper< TrackObservation >> &trackObservations, CalculationMethod calculationMethod=MAINLOOP) const
TrackCheckShip(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
void print(std::ostream &) const override
static double distance(const TrackObservation &a, const TrackObservation &b)
Extends distance for TrackObservation inputs a and b.
static double speedEstimate(const TrackCheckShip::TrackObservation &obs1, const TrackCheckShip::TrackObservation &obs2, const TrackCheckShipParameters &options)
Estimate of speed as calculated in Ops_CheckShipTrack.
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
std::unique_ptr< TrackCheckShipDiagnostics > diagnostics_
bool earlyBreak(const std::vector< std::reference_wrapper< TrackObservation >> &trackObs, const std::string trackId) const
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
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) between displacement vectors going fr...
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
const TrackCheckShipDiagnostics * diagnostics() const
Options controlling the operation of the ship track check filter.
oops::Parameter< bool > testingMode
To be set to true if the filter's unit tests are being run.
TrackCheckShipCoreParameters core
oops::Parameter< SurfaceObservationSubtype > inputCategory
Locations/times of all observations processed by the track checking filter.
std::vector< float > longitudes
std::vector< float > latitudes
std::vector< util::DateTime > datetimes
oops::OptionalParameter< Variable > stationIdVariable
ObsGroupLocationTimes collectObservationsLocations(const ObsAccessor &obsAccessor)
ObsAccessor createObsAccessor(const boost::optional< Variable > &stationIdVariable, const ioda::ObsSpace &obsdb)
Create an ObsAccessor object providing access to observations that need to be checked by the current ...
void sortTracksChronologically(const std::vector< size_t > &validObsIds, const ObsAccessor &obsAccessor, RecursiveSplitter &splitter)
util::Duration abs(const util::Duration &duration)
static constexpr double rad2deg
Relevant calculated values that are specific to each pair or triplet of adjacent/alternating observat...
A container for all track-wise counters and calculations that indicate the overall quality of the tra...