20 #include <boost/format.hpp>
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"
29 #include "oops/util/sqr.h"
46 const boost::optional<Eigen::ArrayXXi> &profileIndex) {
48 return flattenedArray;
51 res.resize((*profileIndex).rows());
52 for (
size_t row=0; row < (*profileIndex).rows(); row++) {
53 res[row] = flattenedArray[(*profileIndex)(row, 0)];
59 Eigen::ArrayXXf
unravel(
const std::vector<float> &flattenedArray,
60 const boost::optional<Eigen::ArrayXXi> &profileIndex) {
62 Eigen::ArrayXXf res((*profileIndex).rows(), (*profileIndex).cols());
63 for (
size_t row=0; row < (*profileIndex).rows(); row++)
64 for (
size_t col=0; col < (*profileIndex).cols(); col++)
65 res(row, col) = flattenedArray[(*profileIndex)(row, col)];
68 Eigen::ArrayXXf res(flattenedArray.size(), 1);
69 for (
size_t row=0; row < flattenedArray.size(); row++)
70 res(row, 0) = flattenedArray[row];
76 void updateFlatData(std::vector<float> & flatArray,
const Eigen::ArrayXXf &array,
77 const boost::optional<Eigen::ArrayXXi> &profileIndex) {
79 for (
size_t row=0; row < (*profileIndex).rows(); row++)
80 for (
size_t col=0; col < (*profileIndex).cols(); col++)
81 flatArray[(*profileIndex)(row, col)] = array(row, col);
83 ASSERT(array.cols() == 1);
84 for (
size_t row=0; row < array.rows(); row++)
85 flatArray[row] = array(row, 0);
91 const int numLevels) {
93 boost::optional<std::vector<int>> extended_obs_space;
94 if (obsdb.has(
"MetaData",
"extended_obs_space")) {
95 extended_obs_space = std::vector<int>(obsdb.nlocs());
96 obsdb.get_db(
"MetaData",
"extended_obs_space", *extended_obs_space);
98 Eigen::ArrayXXi profileIndex {obsdb.nrecs(), numLevels};
101 for (ioda::ObsSpace::RecIdxIter irec = obsdb.recidx_begin(); irec != obsdb.recidx_end(); ++irec) {
103 const std::vector<std::size_t> &rSort = obsdb.recidx_vector(irec);
104 for (
size_t ilocs = 0; ilocs < rSort.size(); ++ilocs) {
105 if (extended_obs_space && ((*extended_obs_space)[rSort[ilocs]] != 1))
107 profileIndex(recnum, levnum) = rSort[ilocs];
110 if (levnum != numLevels) {
111 std::stringstream msg;
112 msg <<
"Record (profile): " << recnum <<
" length: " << levnum+1 <<
" does not match the "
113 <<
"number of levels expected: " << numLevels;
114 throw eckit::UserError(msg.str(), Here());
123 if (var.
group().empty())
129 template <
typename T>
132 std::vector<T> result(v);
133 std::sort(result.begin(), result.end());
134 const auto newEnd = std::unique(result.begin(), result.end());
135 result.erase(newEnd, result.end());
139 template <
typename T>
143 std::map<T, int> intByValue;
146 for (
const T& value : uniqueValues)
147 intByValue[value] = index++;
150 std::vector<int> ints;
151 ints.reserve(values.size());
152 std::transform(values.begin(), values.end(), std::back_inserter(ints),
153 [&](
const T& value) { return intByValue.at(value); });
185 :
FilterBase(obsdb, parameters, std::move(flags), std::move(obserr)), options_(parameters)
201 std::vector<std::vector<bool>> & flagged)
const {
206 boost::optional<Eigen::ArrayXXi> profileIndex;
221 const std::vector<float> *pressures =
226 const std::vector<MetOfficeBuddyPair> buddyPairs = buddyPairFinder.
findBuddyPairs(validObsIds);
229 const oops::Variables &observedVars =
obsdb_.obsvariables();
232 auto getFilterVariableName = [&] (
size_t filterVarIndex) {
236 auto getScalarVariableData = [&] (
size_t filterVarIndex) {
238 const size_t observedVarIndex = observedVars.find(getFilterVariableName(filterVarIndex));
240 std::vector<float> bgValues;
242 std::vector<float> bgErrors;
246 ScalarVariableData data;
248 data.flatGrossErrorProbabilities);
252 data.obsValues =
unravel(obsValues[filterVarIndex], profileIndex);
253 data.obsErrors =
unravel((*
obserr_)[observedVarIndex], profileIndex);
254 data.bgValues =
unravel(bgValues, profileIndex);
255 data.bgErrors =
unravel(bgErrors, profileIndex);
256 data.grossErrorProbabilities =
unravel(data.flatGrossErrorProbabilities, profileIndex);
264 std::map<std::string, std::vector<float>> calculatedGrossErrProbsByVarName;
266 bool previousVariableWasFirstComponentOfTwo =
false;
267 for (
size_t filterVarIndex = 0; filterVarIndex < filtervars.
size(); ++filterVarIndex) {
268 if (previousVariableWasFirstComponentOfTwo) {
270 ScalarVariableData firstComponentData =
271 getScalarVariableData(filterVarIndex - 1);
272 ScalarVariableData secondComponentData =
273 getScalarVariableData(filterVarIndex);
275 for (Eigen::Index i = 0; i < firstComponentData.grossErrorProbabilities.rows(); ++i) {
276 for (Eigen::Index j = 0; j < firstComponentData.grossErrorProbabilities.cols(); ++j) {
277 firstComponentData.grossErrorProbabilities(i, j) =
278 std::max(firstComponentData.grossErrorProbabilities(i, j),
279 secondComponentData.grossErrorProbabilities(i, j));
285 obsData.
pressuresML.get_ptr(), firstComponentData.obsValues,
286 secondComponentData.obsValues, firstComponentData.obsErrors,
287 firstComponentData.bgValues, secondComponentData.bgValues,
288 firstComponentData.bgErrors, firstComponentData.grossErrorProbabilities);
297 firstComponentData.grossErrorProbabilities, profileIndex);
298 calculatedGrossErrProbsByVarName[getFilterVariableName(filterVarIndex - 1)] =
299 firstComponentData.flatGrossErrorProbabilities;
300 calculatedGrossErrProbsByVarName[getFilterVariableName(filterVarIndex)] =
301 std::move(firstComponentData.flatGrossErrorProbabilities);
302 previousVariableWasFirstComponentOfTwo =
false;
304 if (filtervars[filterVarIndex].options().getBool(
"first_component_of_two",
false)) {
305 previousVariableWasFirstComponentOfTwo =
true;
308 ScalarVariableData data =
309 getScalarVariableData(filterVarIndex);
310 checkScalarData(buddyPairs, data.varFlags, verbose, bgErrorHorizCorrScales,
312 data.obsValues, data.obsErrors,
313 data.bgValues, data.bgErrors,
314 data.grossErrorProbabilities);
317 updateFlatData(data.flatGrossErrorProbabilities, data.grossErrorProbabilities,
319 calculatedGrossErrProbsByVarName[getFilterVariableName(filterVarIndex)] =
320 std::move(data.flatGrossErrorProbabilities);
327 for (
const auto &varNameAndGrossErrProbs : calculatedGrossErrProbsByVarName)
328 obsdb_.put_db(
"GrossErrorProbability", varNameAndGrossErrProbs.first,
329 varNameAndGrossErrProbs.second);
339 const boost::optional<Eigen::ArrayXXi> & profileIndex)
const {
351 if (
obsdb_.has(
"MetaData",
"air_pressure")) {
363 if (
obsdb_.has(
"MetaData",
"air_pressure")) {
372 if (stationIdVariable == boost::none) {
373 if (
obsdb_.obs_group_vars().empty()) {
376 return std::vector<int>(
obsdb_.nlocs(), 0);
378 const std::vector<size_t> &recordNumbers =
obsdb_.recnum();
379 return std::vector<int>(recordNumbers.begin(), recordNumbers.end());
382 switch (
obsdb_.dtype(stationIdVariable->group(), stationIdVariable->variable())) {
383 case ioda::ObsDtype::Integer:
385 std::vector<int> stationIds(
obsdb_.nlocs());
386 obsdb_.get_db(stationIdVariable->group(), stationIdVariable->variable(), stationIds);
390 case ioda::ObsDtype::String:
392 std::vector<std::string> stringIds(
obsdb_.nlocs());
393 obsdb_.get_db(stationIdVariable->group(), stationIdVariable->variable(), stringIds);
398 throw eckit::UserError(
"Only integer and string variables may be used as station IDs",
405 const std::vector<size_t> &validObsIds,
const std::vector<float> &latitudes)
const {
407 std::vector<double> abscissas, ordinates;
408 for (
const std::pair<const float, float> &xy :
410 abscissas.push_back(xy.first);
411 ordinates.push_back(xy.second);
414 std::move(ordinates));
416 std::vector<float> scales(latitudes.size());
417 for (
size_t obsId : validObsIds) {
418 scales[obsId] = horizontalCorrelationScaleInterp(latitudes[obsId]);
425 const std::vector<size_t> &validObsIds,
426 const std::vector<float> &latitudes,
427 const std::vector<float> &longitudes,
428 const std::vector<util::DateTime> &datetimes,
429 const std::vector<float> *pressures,
430 const std::vector<int> &stationIds,
431 const std::vector<float> &bgErrorHorizCorrScales)
const {
433 size_t numVerboseObs = 0;
434 std::vector<bool> verbose(latitudes.size(),
false);
436 for (
size_t obsId : validObsIds) {
437 verbose[obsId] = std::any_of(
441 { return box.contains(latitudes[obsId], longitudes[obsId]); });
443 if (verbose[obsId]) {
444 if (numVerboseObs == 0) {
445 oops::Log::trace() <<
"Obs StationId Lat Lon Pressure "
446 "Datetime CorrScale\n";
450 const float pressure = pressures !=
nullptr ? (*pressures)[obsId] : 0;
451 oops::Log::trace() << boost::format(
"%5d %9d %7.2f %7.2f %8.0f %s %9.1f\n") %
452 obsId % stationIds[obsId] % latitudes[obsId] % longitudes[obsId] %
453 pressure % datetimes[obsId] % bgErrorHorizCorrScales[obsId];
457 oops::Log::trace() <<
"Buddy check: " << numVerboseObs <<
" verbose observations" << std::endl;
463 const std::vector<int> &flags,
464 const std::vector<bool> &verbose,
465 const std::vector<float> &bgErrorHorizCorrScales,
466 const std::vector<int> &stationIds,
467 const std::vector<util::DateTime> &datetimes,
468 const Eigen::ArrayXXf *pressures,
469 const Eigen::ArrayXXf &obsValues,
470 const Eigen::ArrayXXf &obsErrors,
471 const Eigen::ArrayXXf &bgValues,
472 const Eigen::ArrayXXf &bgErrors,
473 Eigen::ArrayXXf &pges)
const {
476 const int numLevels = nolevs ? *nolevs : 0;
477 const Eigen::Index cols = obsValues.cols();
479 const bool isMaster =
obsdb_.comm().rank() == 0;
481 oops::Log::trace() <<
"ObsA ObsB StatIdA StatIdB lev DiffA DiffB "
482 "Dist Corr Agree PgeA PgeB Mult\n";
486 const float missing = util::missingValue(1.0f);
490 const size_t jA = pair.obsIdA;
491 const size_t jB = pair.obsIdB;
500 const double hcScale = 0.5 * (bgErrorHorizCorrScales[jA] + bgErrorHorizCorrScales[jB]);
501 const double scaledDist = pair.distanceInKm / hcScale;
506 if (numLevels == 1) {
508 corr = (1.0 + scaledDist) *
509 std::exp(-scaledDist -
511 sqr(std::log((*pressures)(jA, 0)/(*pressures)(jB, 0))) -
512 sqr((datetimes[jA] - datetimes[jB]).toSeconds() * invTemporalCorrScale));
515 corr = (1.0 + scaledDist) *
516 std::exp(-scaledDist -
517 sqr((datetimes[jA] - datetimes[jB]).toSeconds() * invTemporalCorrScale));
523 for (Eigen::Index jlev=0; jlev < cols; jlev++) {
530 const double diffA = obsValues(jA, jlev) - bgValues(jA, jlev);
531 const double diffB = obsValues(jB, jlev) - bgValues(jB, jlev);
533 const double errVarA = sqr(obsErrors(jA, jlev)) + sqr(bgErrors(jA, jlev));
534 const double errVarB = sqr(obsErrors(jB, jlev)) + sqr(bgErrors(jB, jlev));
536 const double covar = corr * bgErrors(jA, jlev) * bgErrors(jB, jlev);
538 const double rho2 = sqr(covar) / (errVarA * errVarB);
540 double expArg = -(0.5 * rho2 / (1.0 - rho2)) *
541 (sqr(diffA) / errVarA + sqr(diffB) / errVarB - 2.0 * diffA * diffB / covar);
545 double z = 1.0 / (1.0 - (1.0 - pges(jA, jlev)) * (1.0 - pges(jB, jlev)) *
546 (1.0 - std::exp(expArg)));
552 if (isMaster && (verbose[jA] || verbose[jB])) {
553 oops::Log::trace() << boost::format(
"%5d %5d %8d %8d %5d "
555 "%5.3f %6.3f %6.3f %6.3f %6.3f\n") %
556 jA % jB % stationIds[jA] % stationIds[jB] % jlev %
557 diffA % diffB % pair.distanceInKm %
558 corr % std::exp(expArg) % pges(jA, jlev) % pges(jB, jlev) % z;
566 const std::vector<int> &flags,
567 const std::vector<bool> &verbose,
568 const std::vector<float> &bgErrorHorizCorrScales,
569 const std::vector<int> &stationIds,
570 const std::vector<util::DateTime> &datetimes,
571 const Eigen::ArrayXXf *pressures,
572 const Eigen::ArrayXXf &uObsValues,
573 const Eigen::ArrayXXf &vObsValues,
574 const Eigen::ArrayXXf &obsErrors,
575 const Eigen::ArrayXXf &uBgValues,
576 const Eigen::ArrayXXf &vBgValues,
577 const Eigen::ArrayXXf &bgErrors,
578 Eigen::ArrayXXf &pges)
const {
581 const int numLevels = nolevs ? *nolevs : 0;
582 const Eigen::Index cols = uObsValues.cols();
584 const bool isMaster =
obsdb_.comm().rank() == 0;
586 oops::Log::trace() <<
"ObsA ObsB StatIdA StatIdB lev LDiffA LDiffB TDiffA TDiffB "
587 "Dist Corr Agree PgeA PgeB Mult\n";
590 const float missing = util::missingValue(1.0f);
594 const size_t jA = pair.obsIdA;
595 const size_t jB = pair.obsIdB;
604 const double hcScale = 0.5 * (bgErrorHorizCorrScales[jA] + bgErrorHorizCorrScales[jB]);
605 const double scaledDist = pair.distanceInKm / hcScale;
610 if (numLevels == 1) {
611 lCorr = std::exp(-scaledDist -
613 sqr(std::log((*pressures)(jA, 0)/(*pressures)(jB, 0))) -
614 sqr((datetimes[jA] - datetimes[jB]).toSeconds() * invTemporalCorrScale));
617 lCorr = std::exp(-scaledDist -
618 sqr((datetimes[jA] - datetimes[jB]).toSeconds() * invTemporalCorrScale));
620 if ((1.0 + scaledDist) * lCorr < 0.1)
624 const double sinRotA = std::sin(pair.rotationAInRad);
625 const double cosRotA = std::cos(pair.rotationAInRad);
626 const double sinRotB = std::sin(pair.rotationBInRad);
627 const double cosRotB = std::cos(pair.rotationBInRad);
630 for (Eigen::Index jlev=0; jlev < cols; jlev++) {
637 const double lDiffA = cosRotA * (uObsValues(jA, jlev) - uBgValues(jA, jlev))
638 + sinRotA * (vObsValues(jA, jlev) - vBgValues(jA, jlev));
640 const double tDiffA = - sinRotA * (uObsValues(jA, jlev) - uBgValues(jA, jlev))
641 + cosRotA * (vObsValues(jA, jlev) - vBgValues(jA, jlev));
643 const double lDiffB = cosRotB * (uObsValues(jB, jlev) - uBgValues(jB, jlev))
644 + sinRotB * (vObsValues(jB, jlev) - vBgValues(jB, jlev));
646 const double tDiffB = - sinRotB * (uObsValues(jB, jlev) - uBgValues(jB, jlev))
647 + cosRotB * (vObsValues(jB, jlev) - vBgValues(jB, jlev));
650 const double errVarA = sqr(obsErrors(jA, jlev)) + sqr(bgErrors(jA, jlev));
651 const double errVarB = sqr(obsErrors(jB, jlev)) + sqr(bgErrors(jB, jlev));
654 const double lCovar = lCorr * bgErrors(jA, jlev) * bgErrors(jB, jlev);
657 const double lRho2 = sqr(lCovar) / (errVarA * errVarB);
658 const double tRho2 = sqr(tCovar) / (errVarA * errVarB);
664 expArg = -(0.5 * tRho2 / (1.0 - tRho2)) *
665 (sqr(tDiffA) / errVarA + sqr(tDiffB) / errVarB - 2.0 * tDiffA * tDiffB / tCovar);
666 expArg = expArg - (0.5 * lRho2 / (1.0 - lRho2)) *
667 (sqr(lDiffA) / errVarA + sqr(lDiffB) / errVarB - 2.0 * lDiffA * lDiffB / lCovar);
671 double z = 1.0 / (1.0 - (1.0 - pges(jA, jlev)) * (1.0 - pges(jB, jlev)) *
672 (1.0 - std::exp(expArg)));
679 if (isMaster && (verbose[jA] || verbose[jB])) {
680 oops::Log::trace() << boost::format(
"%5d %5d %8d %8d %5d "
681 "%6.1f %6.1f %6.1f %6.1f %6.1f "
682 "%5.3f %6.3f %6.3f %6.3f %6.3f\n") %
683 jA % jB % stationIds[jA] % stationIds[jB] % jlev %
684 lDiffA % lDiffB % tDiffA % tDiffB % pair.distanceInKm %
685 lCorr % std::exp(expArg) % pges(jA, jlev) % pges(jB, jlev) % z;
693 const std::vector<bool> & apply,
const boost::optional<Eigen::ArrayXXi> & profileIndex)
const {
694 std::vector<size_t> validObsIds;
697 const std::vector<bool> lev1apply =
extract1stLev(apply, profileIndex);
698 for (
size_t obsId = 0; obsId < lev1flags.size(); ++obsId)
703 validObsIds.push_back(obsId);
709 const std::map<std::string, std::vector<float>> &grossErrProbsByVarName,
710 std::vector<std::vector<bool>> &flagged)
const {
711 ASSERT(filtervars.
size() == flagged.size());
713 for (
size_t varIndex = 0; varIndex < filtervars.
size(); ++varIndex) {
715 const std::vector<float> &grossErrProbs = grossErrProbsByVarName.at(varName);
716 std::vector<bool> &variableFlagged = flagged[varIndex];
717 ASSERT(grossErrProbs.size() == variableFlagged.size());
719 for (
size_t obsId = 0; obsId < grossErrProbs.size(); ++obsId)
721 variableFlagged[obsId] =
true;
726 os <<
"MetOfficeBuddyCheck: config = " <<
options_ << std::endl;
Base class for UFO QC filters.
ufo::Variables filtervars_
A box covering a specified (closed) interval of latitudes and longitudes.
std::vector< bool > flagAndPrintVerboseObservations(const std::vector< size_t > &validObsIds, const std::vector< float > &latitudes, const std::vector< float > &longitudes, const std::vector< util::DateTime > ×, const std::vector< float > *pressures, const std::vector< int > &stationIds, const std::vector< float > &bgErrorHorizCorrScales) const
Identifies observations whose buddy checks should be logged.
void print(std::ostream &) const override
~MetOfficeBuddyCheck() override
MetOfficeBuddyCheck(ioda::ObsSpace &obsdb, const Parameters_ ¶meters, std::shared_ptr< ioda::ObsDataVector< int > > flags, std::shared_ptr< ioda::ObsDataVector< float > > obserr)
std::vector< float > calcBackgroundErrorHorizontalCorrelationScales(const std::vector< size_t > &validObsIds, const std::vector< float > &latitudes) const
Calculates and returns background error correlation scales at observation locations.
void checkVectorData(const std::vector< MetOfficeBuddyPair > &pairs, const std::vector< int > &flags, const std::vector< bool > &verbose, const std::vector< float > &bgErrorHorizCorrScales, const std::vector< int > &stationIds, const std::vector< util::DateTime > &datetimes, const Eigen::ArrayXXf *pressures, const Eigen::ArrayXXf &uObsValues, const Eigen::ArrayXXf &vObsValues, const Eigen::ArrayXXf &obsErrors, const Eigen::ArrayXXf &uBgValues, const Eigen::ArrayXXf &vBgValues, const Eigen::ArrayXXf &bgErrors, Eigen::ArrayXXf &pges) const
Buddy check for vector (two-dimensional) quantities.
Variable backgroundErrorVariable(const Variable &filterVariable) const
Return the name of the variable containing the background error estimate of the specified filter vari...
MetaData collectMetaData(const boost::optional< Eigen::ArrayXXi > &profileIndex) const
Collects and returns metadata of all observations.
void checkScalarData(const std::vector< MetOfficeBuddyPair > &pairs, const std::vector< int > &flags, const std::vector< bool > &verbose, const std::vector< float > &bgErrorHorizCorrScales, const std::vector< int > &stationIds, const std::vector< util::DateTime > &datetimes, const Eigen::ArrayXXf *pressures, const Eigen::ArrayXXf &obsValues, const Eigen::ArrayXXf &obsErrors, const Eigen::ArrayXXf &bgValues, const Eigen::ArrayXXf &bgErrors, Eigen::ArrayXXf &pges) const
Buddy check for scalar quantities.
std::vector< int > getStationIds() const
Returns a vector of integer-valued station IDs, obtained from the source indicated by the filter para...
void applyFilter(const std::vector< bool > &, const Variables &, std::vector< std::vector< bool >> &) const override
std::vector< size_t > getValidObservationIds(const std::vector< bool > &apply, const boost::optional< Eigen::ArrayXXi > &profileIndex) const
Returns a vector of IDs of all observations that should be buddy-checked.
void flagRejectedObservations(const Variables &filtervars, const std::map< std::string, std::vector< float >> &grossErrProbsByVarName, std::vector< std::vector< bool >> &flagged) const
Options controlling the operation of the MetOfficeBuddyCheck filter.
oops::Parameter< double > verticalCorrelationScale
Vertical correlation scale (relates to the ratio of pressures).
oops::Parameter< bool > sortByPressure
oops::Parameter< util::Duration > temporalCorrelationScale
Temporal correlation scale.
oops::Parameter< std::map< float, float > > horizontalCorrelationScaleInterpolationPoints
oops::Parameter< double > dampingFactor1
oops::OptionalParameter< int > numLevels
oops::Parameter< double > nonDivergenceConstraint
Non-divergence constraint. Used only for vector variables.
oops::Parameter< double > dampingFactor2
oops::OptionalParameter< Variable > stationIdVariable
oops::Parameter< float > rejectionThreshold
Observations will be rejected if the gross error probability lies at or above this threshold.
oops::Parameter< std::vector< LatLonBoxParameters > > tracedBoxes
Tracing information will be output for observations lying within any of the specified boxes.
Finds pairs of close observations ("buddies") to check against each other.
std::vector< MetOfficeBuddyPair > findBuddyPairs(const std::vector< size_t > &validObsIds)
Returns a list of MetOfficeBuddyPair objects representing pairs of "buddy" observations that should b...
void get(const Variable &varname, std::vector< float > &values) const
Fills a std::vector with values of the specified variable.
std::shared_ptr< ioda::ObsDataVector< float > > obserr_
std::shared_ptr< ioda::ObsDataVector< int > > flags_
Represents a piecewise linear interpolation of a set of data points.
const std::string & variable() const
const std::string & group() const
Variable variable(const size_t) const
Return a given constituent "primitive" (single-channel) variable.
oops::Variables toOopsVariables() const
size_t size() const
Return the number of constituent Variable objects (some of which may contain multiple channels).
void updateFlatData(std::vector< float > &flatArray, const Eigen::ArrayXXf &array, const boost::optional< Eigen::ArrayXXi > &profileIndex)
Update the provided flat array with values from the provided eigen array.
Eigen::ArrayXXf unravel(const std::vector< float > &flattenedArray, const boost::optional< Eigen::ArrayXXi > &profileIndex)
Return a 2D eigen array from the provided flattened vector.
Eigen::ArrayXXi deriveIndices(const ioda::ObsSpace &obsdb, const int numLevels)
Derive a mapping between the ObsSpace flat data and an unraveled 2D eigen array representation.
const float maxGrossErrorProbability
std::vector< int > mapDistinctValuesToDistinctInts(const std::vector< T > &values)
std::string fullVariableName(const Variable &var)
std::vector< T > uniqueElements(const std::vector< T > &v)
std::vector< T > extract1stLev(const std::vector< T > &flattenedArray, const boost::optional< Eigen::ArrayXXi > &profileIndex)
brief Extract 1st level data from the provided flattened vector.
util::Duration abs(const util::Duration &duration)
Properties of a pair of observations checked against each other during buddy check.
std::vector< float > flatGrossErrorProbabilities
std::vector< int > varFlags
Eigen::ArrayXXf obsErrors
Eigen::ArrayXXf grossErrorProbabilities
Eigen::ArrayXXf obsValues
Eigen::ArrayXXf obsBiases