12 #include "oops/util/Logger.h"
18 #include <boost/make_unique.hpp>
25 return 180.0f / numBands;
31 const std::vector<float> &latitudes,
32 const std::vector<float> &longitudes,
33 const std::vector<util::DateTime> &datetimes,
34 const std::vector<float> *pressures,
35 const std::vector<int> &stationIds)
36 : options_(options), latitudes_(latitudes), longitudes_(longitudes),
37 datetimes_(datetimes), pressures_(pressures), stationIds_(stationIds)
41 const std::vector<size_t> & validObsIds) {
42 std::vector<int> validObsIdsInSortOrder;
43 std::vector<int> bandLbounds;
49 std::vector<int> &validObsIdsInSortOrder,
50 std::vector<int> &bandLbounds)
54 validObsIdsInSortOrder.clear();
55 validObsIdsInSortOrder.reserve(validObsIds.size());
61 std::vector<int> bandIndices(validObsIds.size());
62 for (
size_t validObsIndex = 0; validObsIndex < validObsIds.size(); ++validObsIndex) {
63 const size_t obsId = validObsIds[validObsIndex];
66 bandIndices[validObsIndex] =
67 std::max(0,
static_cast<int>((89.999f -
latitudes_[obsId]) / bandWidth));
75 [
this, &validObsIds](
size_t obsIndex)
77 size_t obsId = validObsIds[obsIndex];
84 int currentBandIndex = -1;
85 for (
auto group : splitter.
groups()) {
86 for (
size_t validObsIndex : group) {
87 const size_t obsId = validObsIds[validObsIndex];
88 for (
int band = currentBandIndex + 1; band <= bandIndices[validObsIndex]; ++band) {
89 bandLbounds[band] = validObsIdsInSortOrder.size();
91 currentBandIndex = bandIndices[validObsIndex];
92 validObsIdsInSortOrder.push_back(obsId);
95 for (
int band = currentBandIndex + 1; band < bandLbounds.size(); ++band) {
96 bandLbounds[band] = validObsIds.size();
99 oops::Log::trace() <<
"Buddy check: " << validObsIds.size() <<
" input observations" << std::endl;
103 const std::vector<int> &validObsIdsInSortOrder,
104 const std::vector<int> &bandLbounds) {
106 std::vector<MetOfficeBuddyPair> pairs;
112 const float searchDLatB = searchDLat + 0.5f * bandWidth;
113 const int numSearchBands =
static_cast<int>(searchDLat / bandWidth) + 1;
115 typedef std::vector<int>::const_iterator ObsIdIt;
116 typedef std::vector<int>::const_reverse_iterator ObsIdRevIt;
122 bandBegins[bandIndex] = validObsIdsInSortOrder.begin() + bandLbounds[bandIndex];
123 bandEnds[bandIndex] = validObsIdsInSortOrder.begin() + bandLbounds[bandIndex + 1];
136 const int firstBandToSearch = jBandA;
138 jBandA + numSearchBands);
140 firstObsToCheckInBands = bandBegins;
143 for (ObsIdIt obsIdItA = bandBegins[jBandA]; obsIdItA != bandEnds[jBandA]; ++obsIdItA) {
144 const int obsIdA = *obsIdItA;
146 const float minLonToCheck =
longitudes_[obsIdA] - lonSearchRangeHalfWidth;
147 const float maxLonToCheck =
longitudes_[obsIdA] + lonSearchRangeHalfWidth;
149 buddyCollector->reset(obsIdA);
152 for (
int jBandB = firstBandToSearch; jBandB <= lastBandToSearch; ++jBandB) {
153 float midBandLatB = 90.0f - bandWidth * (jBandB + 0.5f);
157 ObsIdIt firstObsIdItB = firstObsToCheckInBands[jBandB];
158 if (jBandA == jBandB)
159 firstObsIdItB = obsIdItA + 1;
162 bool firstObsInSearchRangeFound =
false;
163 for (ObsIdIt obsIdItB = firstObsIdItB; obsIdItB != bandEnds[jBandB]; ++obsIdItB) {
164 const int obsIdB = *obsIdItB;
169 if (!firstObsInSearchRangeFound) {
170 firstObsToCheckInBands[jBandB] = obsIdItB;
171 firstObsInSearchRangeFound =
true;
174 buddyCollector->examinePotentialBuddy(obsIdB);
175 if (buddyCollector->foundEnoughBuddies())
176 goto FinishProcessingObsA;
177 if (buddyCollector->foundEnoughBuddiesInCurrentBand())
178 goto FinishProcessingBandB;
182 if (maxLonToCheck > 180 && (jBandA != jBandB || lonSearchRangeHalfWidth < 180)) {
184 float wrappedMaxLonToCheck = maxLonToCheck - 360;
185 for (ObsIdIt obsIdItB = bandBegins[jBandB]; obsIdItB != bandEnds[jBandB]; ++obsIdItB) {
186 const int obsIdB = *obsIdItB;
191 buddyCollector->examinePotentialBuddy(obsIdB);
192 if (buddyCollector->foundEnoughBuddies())
193 goto FinishProcessingObsA;
194 if (buddyCollector->foundEnoughBuddiesInCurrentBand())
195 goto FinishProcessingBandB;
200 if (minLonToCheck < -180 && jBandA != jBandB) {
202 float wrappedMinLonToCheck = minLonToCheck + 360;
203 for (ObsIdRevIt obsIdItB(bandEnds[jBandB]), reverseBandEnd(bandBegins[jBandB]);
204 obsIdItB != reverseBandEnd; ++obsIdItB) {
205 const int obsIdB = *obsIdItB;
210 buddyCollector->examinePotentialBuddy(obsIdB);
211 if (buddyCollector->foundEnoughBuddies())
212 goto FinishProcessingObsA;
213 if (buddyCollector->foundEnoughBuddiesInCurrentBand())
214 goto FinishProcessingBandB;
218 FinishProcessingBandB:
219 buddyCollector->startProcessingNextBand();
221 FinishProcessingObsA:
222 buddyCollector->appendBuddyPairsTo(pairs);
226 oops::Log::trace() <<
"Found " << pairs.size() <<
" buddy pairs.\n";
241 float bandWidth)
const {
246 const float midBandLatA = 90.0f - bandWidth * (bandIndex + 0.5f);
248 const float rad = earthRadius * std::cos((
std::abs(midBandLatA) + bandWidth * 0.5f) *
deg2rad);
Options controlling the operation of the MetOfficeBuddyCheck filter.
oops::Parameter< float > searchRadius
Maximum distance between two observations that may be classified as buddies, in km.
oops::Parameter< bool > useLegacyBuddyCollector
oops::Parameter< int > numZonalBands
const std::vector< float > & latitudes_
const std::vector< float > * pressures_
std::unique_ptr< MetOfficeBuddyCollector > makeBuddyCollector() const
std::vector< MetOfficeBuddyPair > pairObservations(const std::vector< int > &validObsIdsInSortOrder, const std::vector< int > &bandLbounds)
Finds pairs of observations to be considered as buddies. Calculates the distance and mutual orientati...
const std::vector< util::DateTime > & datetimes_
MetOfficeBuddyPairFinder(const MetOfficeBuddyCheckParameters &options, const std::vector< float > &latitudes, const std::vector< float > &longitudes, const std::vector< util::DateTime > &datetimes, const std::vector< float > *pressures, const std::vector< int > &stationIds)
Constructor.
float getLongitudeSearchRangeHalfWidth(int bandIndex, float bandWidth) const
std::vector< MetOfficeBuddyPair > findBuddyPairs(const std::vector< size_t > &validObsIds)
Returns a list of MetOfficeBuddyPair objects representing pairs of "buddy" observations that should b...
const std::vector< int > & stationIds_
const MetOfficeBuddyCheckParameters & options_
const std::vector< float > & longitudes_
void sortObservations(const std::vector< size_t > &validObsIds, std::vector< int > &validObsIdsInSortOrder, std::vector< int > &bandLbounds)
Sorts observations in an order facilitating rapid search for buddies.
Partitions an array into groups of elements equivalent according to certain criteria.
GroupRange groups() const
Return the range of equivalence classes.
void groupBy(const std::vector< size_t > &categories)
Split existing equivalence classes according to a new criterion.
void sortGroupsBy(const UnaryOperation &key)
Sort the elements in each equivalence class in ascending order.
float zonalBandWidth(int numBands)
real(kind_real), parameter, public deg2rad
real(kind_real), parameter, public rad2deg
util::Duration abs(const util::Duration &duration)
static constexpr double deg2rad
static constexpr double rad2deg
static constexpr double mean_earth_rad