Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/sla do not assimilate #56

Merged
merged 4 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 54 additions & 75 deletions src/nemo-feedback/NemoFeedback.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

#include "nemo-feedback/NemoFeedback.h"

#include <string>
#include <string_view>
#include <string>

#include <algorithm>
#include <utility>
Expand Down Expand Up @@ -46,35 +46,6 @@ namespace nemo_feedback {
constexpr std::string_view defaultDepthGroup{"MetaData"};
constexpr std::string_view defaultDepthVariable{"depthBelowWaterSurface"};

/// \brief create whole report variables from a diagnostic flag
void wholeReportQCFromDiagnosticFlags(const
NemoFeedbackDataCreator& creator, const std::string& group, const
std::string& name, feedback_io::Data<int32_t>& wholeReportQCData) {
if (wholeReportQCData.n_obs() == 0) {
wholeReportQCData = feedback_io::Data<int32_t>(creator.indexer(),
std::vector<int32_t>(creator.indexer()->n_source_data(), 0));
}
feedback_io::Data<int32_t> QCData =
creator.create(group, name,
ufo::DiagnosticFlag(0), 4, 1);
for (size_t iProfile = 0;
iProfile < QCData.n_obs(); ++iProfile) {
size_t nBadObs = 0;
for (size_t iLevel = 0;
iLevel < QCData.length(iProfile);
++iLevel) {
if (QCData(iProfile, iLevel) == 4) {
++nBadObs;
}
}
if (wholeReportQCData[iProfile] == 0 ||
wholeReportQCData[iProfile] == 4) {
wholeReportQCData[iProfile] =
(nBadObs == QCData.length(iProfile) ? 4 : 1);
}
}
}


NemoFeedback::NemoFeedback(
ioda::ObsSpace & obsdb,
Expand Down Expand Up @@ -222,10 +193,11 @@ template <typename T>
void NemoFeedback::write_all_data(feedback_io::Writer<T>& writer,
const NemoFeedbackDataCreator& creator) const
{
feedback_io::Data<int32_t> wholeReportQCData(creator.indexer(),
std::vector<int32_t>(creator.indexer()->n_source_data(), 0));
feedback_io::Data<int32_t> wholeReportPositionQCData;
feedback_io::Data<int32_t> wholeReportTimeQCData;
feedback_io::Data<feedback_io::QC::Level> wholeReportQCData(creator.indexer(),
std::vector<feedback_io::QC::Level>(creator.indexer()->n_source_data(),
feedback_io::QC::Level::None));
feedback_io::Data<feedback_io::QC::Level> wholeReportPositionQCData;
feedback_io::Data<feedback_io::QC::Level> wholeReportTimeQCData;
std::vector<ufo::DiagnosticFlag> do_not_assimilate;
for (const NemoFeedbackVariableParameters& nemoVariableParams :
parameters_.variables.value()) {
Expand Down Expand Up @@ -270,70 +242,78 @@ void NemoFeedback::write_all_data(feedback_io::Writer<T>& writer,

// Quality control rank variables
if (obsdb_.has("DiagnosticFlags/FinalReject", ufo_name)) {

feedback_io::Data<int32_t> variableFinalQCData =
feedback_io::Data<feedback_io::QC::Level> variableFinalQCData =
creator.create("DiagnosticFlags/FinalReject", ufo_name,
ufo::DiagnosticFlag(0), 4, 1);
ufo::DiagnosticFlag(0), feedback_io::QC::Level::Bad,
feedback_io::QC::Level::Good);

// Add do not assimilate flag if required.
// Add do not assimilate flag if required to the final QC information.
if (obsdb_.has("DiagnosticFlags/DoNotAssimilate", ufo_name)) {
feedback_io::Data<int32_t> variableDoNotAssimilateData(
feedback_io::Data<feedback_io::QC::Level> variableDoNotAssimilateData(
creator.create("DiagnosticFlags/DoNotAssimilate", ufo_name,
ufo::DiagnosticFlag(0), 128, 0));
ufo::DiagnosticFlag(0), feedback_io::QC::Level::DoNotAssimilate,
feedback_io::QC::Level::None));
for (size_t iProfile = 0;
iProfile < variableDoNotAssimilateData.n_obs(); ++iProfile) {
for (size_t iLevel = 0;
iLevel < variableDoNotAssimilateData.length(iProfile);
++iLevel) {
variableFinalQCData(iProfile, iLevel) +=
variableDoNotAssimilateData(iProfile, iLevel);
if (variableDoNotAssimilateData(iProfile, iLevel) ==
feedback_io::QC::Level::DoNotAssimilate) {
variableFinalQCData(iProfile, iLevel) =
static_cast<feedback_io::QC::Level>(
static_cast<int32_t>(variableFinalQCData(iProfile, iLevel)) +
static_cast<int32_t>(feedback_io::QC::Level::DoNotAssimilate));
}
}
}
}

// Per-profile quality rank
feedback_io::Data<int32_t> wholeVariableQCData(creator.indexer(),
std::vector<int32_t>(creator.indexer()->n_source_data(), 0));
for (size_t iProfile = 0;
iProfile < variableFinalQCData.n_obs(); ++iProfile) {
size_t nBadObs = 0;
for (size_t iLevel = 0;
iLevel < variableFinalQCData.length(iProfile);
++iLevel) {
if (variableFinalQCData(iProfile, iLevel) == 4) {
++nBadObs;
}
}
wholeVariableQCData[iProfile] =
(nBadObs == variableFinalQCData.length(iProfile) ? 4 : 1);
}
// set per-profile quality rank for the variable
feedback_io::Data<feedback_io::QC::Level> wholeVariableQCData(
creator.indexer(), std::vector<feedback_io::QC::Level>(
creator.indexer()->n_source_data(), feedback_io::QC::Level::None));
feedback_io::wholeReportFromPerProfile(variableFinalQCData,
wholeVariableQCData);
// update set per-profile quality rank for the whole report
feedback_io::wholeReportFromPerProfile(variableFinalQCData,
wholeReportQCData);

writer.write_variable_surf_qc(nemo_name + "_QC",
wholeVariableQCData);
writer.write_variable_level_qc(nemo_name + "_LEVEL_QC",
variableFinalQCData);

// Whole Observation report QC
// Whole Observation Position report QC
const std::string positionQCGroup("DiagnosticFlags/PositionReject");
if (obsdb_.has(positionQCGroup, ufo_name)) {
wholeReportQCFromDiagnosticFlags(creator, positionQCGroup, ufo_name,
feedback_io::Data<feedback_io::QC::Level> QCData = creator.create(
positionQCGroup, ufo_name, ufo::DiagnosticFlag(0),
feedback_io::QC::Level::Bad, feedback_io::QC::Level::Good);
if (wholeReportPositionQCData.n_obs() == 0) {
wholeReportPositionQCData = feedback_io::Data<feedback_io::QC::Level>(
creator.indexer(), std::vector<feedback_io::QC::Level>(
creator.indexer()->n_source_data(),
feedback_io::QC::Level::None));
}
feedback_io::wholeReportFromPerProfile(QCData,
wholeReportPositionQCData);
}

// Whole Observation time report QC
const std::string timeQCGroup("DiagnosticFlags/TimeReject");
if (obsdb_.has(timeQCGroup, ufo_name)) {
wholeReportQCFromDiagnosticFlags(creator, timeQCGroup, ufo_name,
wholeReportTimeQCData);
}

{
for (size_t iProfile = 0; iProfile < variableData.n_obs(); ++iProfile) {
if (wholeReportQCData[iProfile] == 0 ||
wholeReportQCData[iProfile] == 4) {
wholeReportQCData[iProfile] =
(4 == wholeVariableQCData[iProfile] ? 4 : 1);
}
feedback_io::Data<feedback_io::QC::Level> QCData =
creator.create(timeQCGroup, ufo_name, ufo::DiagnosticFlag(0),
feedback_io::QC::Level::Bad, feedback_io::QC::Level::Good);
if (wholeReportTimeQCData.n_obs() == 0) {
wholeReportTimeQCData = feedback_io::Data<feedback_io::QC::Level>(
creator.indexer(), std::vector<feedback_io::QC::Level>(
creator.indexer()->n_source_data(),
feedback_io::QC::Level::None));
}
feedback_io::wholeReportFromPerProfile(QCData,
wholeReportTimeQCData);
}
}

Expand All @@ -356,9 +336,9 @@ void NemoFeedback::write_all_data(feedback_io::Writer<T>& writer,
const std::string depthVariable = parameters_.depthVariable.value()
.value_or(static_cast<std::string>(defaultDepthVariable));
if (obsdb_.has(depthQCGroup, depthVariable)) {
feedback_io::Data<int32_t> depthQCData(
creator.create(depthQCGroup, depthVariable,
ufo::DiagnosticFlag(0), 4, 1));
feedback_io::Data<feedback_io::QC::Level> depthQCData(
creator.create(depthQCGroup, depthVariable, ufo::DiagnosticFlag(0),
feedback_io::QC::Level::Bad, feedback_io::QC::Level::Good));
writer.write_variable_level_qc("DEPTH_QC", depthQCData);
}
if (wholeReportPositionQCData.n_obs() != 0) {
Expand Down Expand Up @@ -424,7 +404,6 @@ std::tuple<feedback_io::Data<std::string>, feedback_io::Data<std::string>>
stationIdentificationAvailable = true;
}

const int32_t buoyIDMissingValueInt = util::missingValue<int32_t>();
constexpr size_t buoyIDWidth = 8;
const std::string missingStringFeedback =
feedback_io::typeToFill::value<std::string>();
Expand Down
20 changes: 11 additions & 9 deletions src/nemo-feedback/NemoFeedbackDataCreator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -144,14 +144,15 @@ feedback_io::Data<std::string> NemoFeedbackDataCreator::create_from_obsdb(
return feedback_io::Data<std::string>(indexer_, std::move(data));
}

feedback_io::Data<int32_t> NemoFeedbackDataCreator::create_from_obsdb(const
std::string& obsGroup, const std::string& ufoName, const
ufo::DiagnosticFlag TypeInstance, int32_t whenTrue,
int32_t whenFalse) const {
feedback_io::Data<feedback_io::QC::Level>
NemoFeedbackDataCreator::create_from_obsdb(
const std::string& obsGroup, const std::string& ufoName, const
ufo::DiagnosticFlag TypeInstance, feedback_io::QC::Level whenTrue,
feedback_io::QC::Level whenFalse) const {
oops::Log::trace() << NemoFeedbackDataCreator::className()
<< ":create_from_obsdb ufo::DiagnosticFlag "
<< obsGroup << "/" << ufoName << std::endl;
std::vector<int32_t> data;
std::vector<feedback_io::QC::Level> data;
std::vector<ufo::DiagnosticFlag> flagData(obsdb_.nlocs(), 0);
obsdb_.get_db(obsGroup, ufoName, flagData);
for (ufo::DiagnosticFlag flag : flagData) {
Expand All @@ -161,7 +162,7 @@ feedback_io::Data<int32_t> NemoFeedbackDataCreator::create_from_obsdb(const
throw eckit::BadValue(NemoFeedbackDataCreator::className()
+ ":create_from_obsdb ufo::DiagnosticFlag no data.", Here());

return feedback_io::Data<int32_t>(indexer_, std::move(data));
return feedback_io::Data<feedback_io::QC::Level>(indexer_, std::move(data));
}

template<typename T>
Expand Down Expand Up @@ -304,9 +305,10 @@ NemoFeedbackDataCreator::create_altimeter_IDs() const {
feedback_io::Data<std::string>(indexer_, std::move(station_types)));
}

feedback_io::Data<int32_t> NemoFeedbackDataCreator::create(const std::string&
obsGroup, const std::string& ufoName, const ufo::DiagnosticFlag
typeInstance, int32_t whenTrue, int32_t whenFalse) const {
feedback_io::Data<feedback_io::QC::Level> NemoFeedbackDataCreator::create(
const std::string& obsGroup, const std::string& ufoName, const
ufo::DiagnosticFlag typeInstance, feedback_io::QC::Level whenTrue,
feedback_io::QC::Level whenFalse) const {
if (obsGroup == "hofx" || obsGroup == "HofX") {
std::ostringstream err_stream;
err_stream << NemoFeedbackDataCreator::className()
Expand Down
13 changes: 7 additions & 6 deletions src/nemo-feedback/NemoFeedbackDataCreator.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ class NemoFeedbackDataCreator {
create_altimeter_IDs() const;

/// \brief create DiagnosticFlags feedbackData
feedback_io::Data<int32_t> create(const std::string& obsGroup,
feedback_io::Data<feedback_io::QC::Level> create(const std::string& obsGroup,
const std::string& ufoName,
const ufo::DiagnosticFlag typeInstance,
const int32_t whenTrue,
const int32_t whenFalse) const;
const feedback_io::QC::Level whenTrue,
const feedback_io::QC::Level whenFalse) const;

/// \brief create feedbackData
template<typename T>
Expand All @@ -91,11 +91,12 @@ std::shared_ptr<feedback_io::DataIndexer> indexer() const {return indexer_;}
size_t width, bool leftJustify = false) const;

/// \brief create DiagnosticFlags feedbackData from obsdb
feedback_io::Data<int32_t> create_from_obsdb(const std::string& obsGroup,
feedback_io::Data<feedback_io::QC::Level> create_from_obsdb(
const std::string& obsGroup,
const std::string& ufoName,
const ufo::DiagnosticFlag TypeInstance,
int32_t whenTrue,
int32_t whenFalse) const;
feedback_io::QC::Level whenTrue,
feedback_io::QC::Level whenFalse) const;

/// \brief create feedbackData from obsdb
template<typename T>
Expand Down
64 changes: 64 additions & 0 deletions src/nemo-feedback/feedback_io/Data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,71 @@ std::vector<T> Data<T>::raw_profile(size_t iProfile) const {
template class Data<std::string>;
template class Data<size_t>;
template class Data<int32_t>;
template class Data<feedback_io::QC::Level>;
template class Data<float>;
template class Data<double>;

/// \brief update the whole report QC information for a profile based on the
/// current whole variable QC information
feedback_io::QC::Level wholeReportUpdate(feedback_io::QC::Level current, const
size_t length, const size_t nGoodObs, const size_t nBadObs, const size_t
nBadDoNotAssimilate, const size_t nDoNotAssimilate) {
// If good obs were previously found, the report is good
if (current == QC::Level::Good) {
return QC::Level::Good;
}
// Upgrade any reports if they contain any good obs (goodDoNotAssimilate ->
// good)
if (nGoodObs > 0) {
return QC::Level::Good;
}
// Check currently bad, do-not-assimilate or not-yet-checked reports and
// downgrade where necessary
if (nBadObs == length) {
return QC::Level::Bad;
} else if (nBadDoNotAssimilate == length) {
// No good observations, all bad-do-not-assimilate
return QC::Level::BadDoNotAssimilate;
} else if (nDoNotAssimilate == length) {
// No good observations, some mix of good and bad do-not-assimilate types
// -> GoodDoNotAssimilate
return QC::Level::GoodDoNotAssimilate;
} else {
// No good observations, some mix of bad and do-not-assimilate -> Bad
return QC::Level::Bad;
}
}

void wholeReportFromPerProfile(const Data<feedback_io::QC::Level>& QCData,
Data<feedback_io::QC::Level>& wholeReportQCData) {
for (size_t iProfile = 0;
iProfile < QCData.n_obs(); ++iProfile) {
size_t nGoodObs = 0;
size_t nBadObs = 0;
size_t nBadDoNotAssimilate = 0;
size_t nDoNotAssimilate = 0;
for (size_t iLevel = 0;
iLevel < QCData.length(iProfile);
++iLevel) {
if (QCData(iProfile, iLevel) == QC::Level::Bad) {
++nBadObs;
} else if (QCData(iProfile, iLevel) ==
QC::Level::BadDoNotAssimilate) {
++nBadDoNotAssimilate;
}
if (QCData(iProfile, iLevel) == QC::Level::BadDoNotAssimilate ||
QCData(iProfile, iLevel) == QC::Level::GoodDoNotAssimilate ||
QCData(iProfile, iLevel) == QC::Level::DoNotAssimilate) {
++nDoNotAssimilate;
}
if (QCData(iProfile, iLevel) == QC::Level::Good) {
++nGoodObs;
}
}
wholeReportQCData[iProfile] = wholeReportUpdate(
wholeReportQCData[iProfile], QCData.length(iProfile), nGoodObs,
nBadObs, nBadDoNotAssimilate, nDoNotAssimilate);
}
}
} // namespace feedback_io
} // namespace nemo_feedback
8 changes: 7 additions & 1 deletion src/nemo-feedback/feedback_io/Data.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@

#include <nemo-feedback/feedback_io/DataIndexer.h>

#include <vector>
#include <string>
#include <vector>
#include <memory>

#include <nemo-feedback/feedback_io/Utils.h>

namespace nemo_feedback {
namespace feedback_io {

Expand Down Expand Up @@ -119,5 +121,9 @@ class Data {
std::shared_ptr<DataIndexer> indexer_;
std::shared_ptr<std::vector<T>> data_;
};

/// \brief create whole report data from per-profile QC data
void wholeReportFromPerProfile(const Data<feedback_io::QC::Level>& QCData,
Data<feedback_io::QC::Level>& wholeReportQCData);
} // namespace feedback_io
} // namespace nemo_feedback
9 changes: 9 additions & 0 deletions src/nemo-feedback/feedback_io/Utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
#include "oops/util/Logger.h"
#include "oops/util/Duration.h"

#define DOUBLE_FILLVALUE 99999.0
#define FLOAT_FILLVALUE 99999.0
#define INT32_FILLVALUE 0
#define STRING_FILLVALUE "MISSING "


namespace nemo_feedback {
namespace feedback_io {

Expand All @@ -41,6 +47,9 @@ template <> const float typeToFill::value<float>() {
template <> const int32_t typeToFill::value<int32_t>() {
return INT32_FILLVALUE;
}
template <> const QC::Level typeToFill::value<QC::Level>() {
return QC::Level::None;
}
template <> const std::string typeToFill::value<std::string>() {
return STRING_FILLVALUE;
}
Expand Down
Loading