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"
44 assert(
options_->maxSpeed.value() > 0);
57 util::Duration temporalDistance =
abs(obs1.
getTime() -
62 double speedEst = 0.0;
63 if (dist > spatialRes) {
64 speedEst = (dist - 0.5 * spatialRes) /
65 std::max(temporalDistance.toSeconds(),
66 (tempRes.toSeconds()));
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()));
83 return std::round(retValue * 10.0f) * 0.1f;
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) {}
102 checkCounter_->registerCheckResult(result);
107 return observationNumber_;
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;
138 os <<
"TrackCheckShip: config = " <<
config_ << std::endl;
153 std::vector<std::vector<bool>> & flagged)
const {
163 std::vector<bool> isRejected(apply.size(),
false);
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),
172 return std::ref<TrackObservation>(obs); });
174 CalculationMethod::FIRSTITERATION);
175 if (!trackObservationsReferences.empty() &&
176 this->options_->earlyBreakCheck &&
180 if (
options_->deferredCheckSimultaneous.value()) {
183 bool firstIterativeRemoval =
true;
184 while (trackObservationsReferences.size() >= 3) {
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())) {
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) {
204 trackObservationsReferences, maxSpeedReferenceIterator, firstIterativeRemoval);
205 firstIterativeRemoval =
false;
209 validObsIds, trackObservations, isRejected);
218 std::vector<size_t>::const_iterator trackObsIndicesBegin,
219 std::vector<size_t>::const_iterator trackObsIndicesEnd,
220 const std::vector<size_t> &validObsIds,
222 std::vector<TrackObservation> trackObservations;
223 trackObservations.reserve(trackObsIndicesEnd - trackObsIndicesBegin);
224 std::shared_ptr<TrackStatistics> trackStatistics(
new TrackStatistics());
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];
232 obsLocTime.
datetimes[obsId], trackStatistics,
237 return trackObservations;
248 bool breakResult =
false;
249 const auto& trackStats = *(trackObs[0].get().getFullTrackStatistics());
253 if ((2 * ((
options_->inputCategory.value() != 1)
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();
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;
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),
281 return !(obs.getObservationStatistics().simultaneous);});
283 return reducedTrackObs;
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;
297 auto fail = [&rejectedObservation](
298 const std::vector<std::reference_wrapper<TrackObservation>>::iterator &iter) {
300 iter->get().registerSweepOutcome(
true);
301 rejectedObservation = iter;
303 auto neighborObservationStatistics = [&observationAfterFastestSegment](
int index) ->
305 return (observationAfterFastestSegment + index)->get().getObservationStatistics();
307 auto meanSpeed = observationAfterFastestSegment->get().getFullTrackStatistics()->meanSpeed_;
308 if (observationAfterFastestSegment ==
track.begin() + 1) {
310 if (neighborObservationStatistics(0).speedAveraged <=
312 (neighborObservationStatistics(1).speed >
314 neighborObservationStatistics(1).angle > 45.0)) {
315 fail(observationAfterFastestSegment);
318 fail(observationAfterFastestSegment - 1);
321 }
else if (observationAfterFastestSegment ==
track.end() - 1) {
322 if (neighborObservationStatistics(-1).speedAveraged <=
324 (neighborObservationStatistics(-1).speed >
326 neighborObservationStatistics(-2).
angle > 45.0)) {
327 fail(observationAfterFastestSegment - 1);
330 fail(observationAfterFastestSegment);
333 }
else if (neighborObservationStatistics(-1).speed >
335 fail(observationAfterFastestSegment - 1);
338 }
else if (neighborObservationStatistics(1).speed >
340 fail(observationAfterFastestSegment);
342 }
else if (neighborObservationStatistics(0).speedAveraged >
344 fail(observationAfterFastestSegment - 1);
348 }
else if (neighborObservationStatistics(-1).speedAveraged >
350 fail(observationAfterFastestSegment);
354 }
else if (neighborObservationStatistics(-1).
angle >
355 (45.0 + neighborObservationStatistics(0).
angle)) {
356 fail(observationAfterFastestSegment - 1);
358 }
else if (neighborObservationStatistics(0).
angle >
359 45.0 + neighborObservationStatistics(-1).
angle) {
360 fail(observationAfterFastestSegment);
362 }
else if (neighborObservationStatistics(-2).
angle > 45.0 &&
363 neighborObservationStatistics(-2).
angle >
364 neighborObservationStatistics(1).
angle) {
365 fail(observationAfterFastestSegment - 1);
367 }
else if (neighborObservationStatistics(1).
angle > 45.0) {
368 fail(observationAfterFastestSegment);
370 }
else if (neighborObservationStatistics(-1).speed <
372 neighborObservationStatistics(1).speed,
374 fail(observationAfterFastestSegment - 1);
376 }
else if (neighborObservationStatistics(1).speed <
377 0.5 * std::min(neighborObservationStatistics(-1).speed, meanSpeed)) {
378 fail(observationAfterFastestSegment);
381 double distanceSum = 0.0;
382 distanceSum = std::accumulate(observationAfterFastestSegment - 1,
383 observationAfterFastestSegment + 2, distanceSum,
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()) {
398 diagnostics_->storeDistancePrevObsOmitted(distancePrevObsOmitted);
399 diagnostics_->storeDistanceCurrentObsOmitted(distanceCurrentObsOmitted);
400 double timeDouble = timeSum.toSeconds();
403 if (distancePrevObsOmitted < distanceCurrentObsOmitted - std::max(
404 options_->spatialResolution.value(), 0.1 * distanceSum)) {
405 fail(observationAfterFastestSegment - 1);
407 }
else if (distanceCurrentObsOmitted < (
408 distancePrevObsOmitted - std::max(
409 options_->spatialResolution.value(), 0.1 * distanceSum))) {
410 fail(observationAfterFastestSegment);
412 }
else if (timeSum <= four_days && timeSum.toSeconds() > 0 &&
413 std::min(distancePrevObsOmitted, distanceCurrentObsOmitted) > 0.0) {
414 double previousSegmentDistanceProportion =
416 neighborObservationStatistics(-1).distance /
417 distanceCurrentObsOmitted;
418 double previousObservationDistanceAveragedProportion =
420 neighborObservationStatistics(-1).
421 distanceAveraged / distancePrevObsOmitted;
422 double previousSegmentTimeProportion =
423 static_cast<double>(((observationAfterFastestSegment - 1)->get().getTime() -
424 (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
426 double previousAndFastestSegmentTimeProportion =
427 static_cast<double>(((observationAfterFastestSegment->get().getTime()) -
428 (observationAfterFastestSegment - 2)->get().getTime()).toSeconds()) /
430 if (
options_->testingMode.value()) {
431 diagnostics_->storePreviousSegmentDistanceProportion(previousSegmentDistanceProportion);
432 diagnostics_->storePreviousObservationDistanceAveragedProportion(
433 previousObservationDistanceAveragedProportion);
434 diagnostics_->storePreviousSegmentTimeProportion(previousSegmentTimeProportion);
435 diagnostics_->storePreviousAndFastestSegmentTimeProportion(
436 previousAndFastestSegmentTimeProportion);
438 if (
std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion) >
439 0.1 +
std::abs(previousObservationDistanceAveragedProportion -
440 previousAndFastestSegmentTimeProportion)) {
444 fail(observationAfterFastestSegment - 1);
446 }
else if (
std::abs(previousObservationDistanceAveragedProportion -
447 previousAndFastestSegmentTimeProportion) > 0.1 +
448 std::abs(previousSegmentDistanceProportion - previousSegmentTimeProportion)) {
451 fail(observationAfterFastestSegment);
454 fail(observationAfterFastestSegment);
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;
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(),
474 0.001 <<
"[km]" << std::endl;
477 if (errorCategory == 0 || ((rejectedObservation->get().getObservationStatistics().
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()};
488 std::make_pair(observationNumbersAroundFastest, errorCategory));
490 if (rejectedObservation == observationAfterFastestSegment) {
491 fail(observationAfterFastestSegment - 1);
493 fail(observationAfterFastestSegment);
495 track.erase(observationAfterFastestSegment - 1, observationAfterFastestSegment);
497 if (
options_->testingMode.value() && firstIterativeRemoval) {
498 std::vector<size_t> rejectedObservationNumber{rejectedObservation->get().
499 getObservationNumber()};
501 std::make_pair(rejectedObservationNumber,
504 track.erase(rejectedObservation);
506 oops::Log::trace() <<
"Error category: " << errorCategory << std::endl;
527 this->setTimeDifference(getTime() - prevObs.
getTime());
528 if (!(this->observationStatistics_.timeDifference.toSeconds())) {
529 if (this->observationStatistics_.distance >
533 setSimultaneous(
true);
535 if (firstIteration) {
536 adjustTwoObservationStatistics(options);
552 this->setSpeedAveraged(
speedEstimate(prevObs, nextObs, options));
553 if (std::min(this->observationStatistics_.distance,
556 this->observationStatistics_.angle =
angle(prevObs, *
this, nextObs);
558 if (firstIteration) {
559 adjustThreeObservationStatistics();
568 const std::vector<std::reference_wrapper<TrackObservation>> &trackObservations,
570 if (trackObservations.size()) {
571 for (
size_t obsIdx = 1; obsIdx < trackObservations.size(); obsIdx++) {
580 if (calculationMethod ==
FIRSTITERATION && (obsIdx == trackObservations.size() - 1)) {
581 int potentialDenominator = trackObservations.size() - 1 -
584 std::max(1, potentialDenominator);
588 std::vector<TrackCheckShip::ObservationStatistics> obsStats;
589 for (
size_t obsIdx = 0; obsIdx < trackObservations.size(); ++obsIdx) {
590 obsStats.push_back(trackObservations[obsIdx].get().getObservationStatistics());
592 auto trackStats = *(trackObservations[0].get().getFullTrackStatistics());
594 diagnostics_->storeInitialCalculationResults(std::make_pair(obsStats, trackStats));
596 diagnostics_->storeCalculatedResultsSimultaneousDeferred(obsStats);
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_++;
613 getFullTrackStatistics()->sumSpeed_ += getObservationStatistics().speed;
619 if (getObservationStatistics().
angle >= 90.0)
620 getFullTrackStatistics()->numBends_++;
626 return observationStatistics_;
629 const std::shared_ptr<TrackCheckShip::TrackStatistics>
632 return fullTrackStatistics_;
639 if (!this->observationStatistics_.simultaneous)
640 this->fullTrackStatistics_->numSimultaneous_++;
641 this->observationStatistics_.simultaneous = simul;
644 this->observationStatistics_.distance = dist;
647 this->observationStatistics_.timeDifference = tDiff;
650 this->observationStatistics_.speed = speed;
653 this->observationStatistics_.angle =
angle;
656 this->observationStatistics_.distanceAveraged = distAvg;
659 this->observationStatistics_.speedAveraged = speedAvg;