diff --git a/src/nemo-feedback/NemoFeedback.cc b/src/nemo-feedback/NemoFeedback.cc index 56f8b0f..bfb9818 100644 --- a/src/nemo-feedback/NemoFeedback.cc +++ b/src/nemo-feedback/NemoFeedback.cc @@ -5,39 +5,37 @@ #include "nemo-feedback/NemoFeedback.h" #include -#include #include -#include -#include #include #include +#include +#include +#include +#include "eckit/exception/Exceptions.h" #include "eckit/mpi/Comm.h" #include "eckit/mpi/Parallel.h" -#include "eckit/exception/Exceptions.h" - #include "ioda/ObsDataVector.h" #include "ioda/ObsSpace.h" #include "ioda/ObsVector.h" -#include "oops/base/Variables.h" +#include "nemo-feedback/NemoFeedbackDataCreator.h" +#include "nemo-feedback/NemoFeedbackParameters.h" +#include "nemo-feedback/feedback_io/Data.h" +#include "nemo-feedback/feedback_io/DataIndexer.h" +#include "nemo-feedback/feedback_io/Utils.h" +#include "nemo-feedback/feedback_io/Writer.h" #include "oops/base/ObsVariables.h" +#include "oops/base/Variables.h" #include "oops/mpi/mpi.h" -#include "oops/util/Logger.h" #include "oops/util/DateTime.h" +#include "oops/util/Logger.h" #include "oops/util/missingValues.h" -#include "nemo-feedback/NemoFeedbackParameters.h" -#include "nemo-feedback/NemoFeedbackDataCreator.h" -#include "nemo-feedback/feedback_io/Utils.h" -#include "nemo-feedback/feedback_io/Writer.h" -#include "nemo-feedback/feedback_io/Data.h" -#include "nemo-feedback/feedback_io/DataIndexer.h" #include "ufo/GeoVaLs.h" #include "ufo/ObsDiagnostics.h" #include "ufo/filters/DiagnosticFlag.h" -#include "ufo/filters/processWhere.h" #include "ufo/filters/ObsAccessor.h" - +#include "ufo/filters/processWhere.h" namespace nemo_feedback { @@ -45,23 +43,19 @@ namespace nemo_feedback { constexpr std::string_view defaultDepthGroup{"MetaData"}; constexpr std::string_view defaultDepthVariable{"depthBelowWaterSurface"}; - NemoFeedback::NemoFeedback( - ioda::ObsSpace & obsdb, - const Parameters_ & params, - std::shared_ptr< ioda::ObsDataVector > flags, - std::shared_ptr< ioda::ObsDataVector > obsErrors) - : - obsdb_(obsdb), - data_(obsdb_), - geovars_(), - flags_(std::move(flags)), - obsErrors_(std::move(obsErrors)), - parameters_(params), - nameMap_(params.geoVaLsAliasFile.value()), - validityTime_(obsdb.windowStart() + - (obsdb.windowEnd() - obsdb.windowStart()) / 2) -{ + ioda::ObsSpace& obsdb, const Parameters_& params, + std::shared_ptr> flags, + std::shared_ptr> obsErrors) + : obsdb_(obsdb), + data_(obsdb_), + geovars_(), + flags_(std::move(flags)), + obsErrors_(std::move(obsErrors)), + parameters_(params), + nameMap_(params.geoVaLsAliasFile.value()), + validityTime_(obsdb.windowStart() + + (obsdb.windowEnd() - obsdb.windowStart()) / 2) { oops::Log::trace() << "NemoFeedback constructor starting" << std::endl; std::vector obsGeoNames; @@ -73,7 +67,7 @@ NemoFeedback::NemoFeedback( }; // Generate lists of the HofX variable name data for the filter for (const NemoFeedbackVariableParameters& nemoVariableParams : - parameters_.variables.value()) { + parameters_.variables.value()) { const std::string varname = nemoVariableParams.name.value(); if ((nemoVariableParams.iodaObsGroup.value().value_or("") == "HofX") && new_name(obsGeoNames, varname)) { @@ -97,7 +91,7 @@ NemoFeedback::NemoFeedback( bool isProfile = false; isAltimeter_ = false; for (const NemoFeedbackVariableParameters& nemoVariableParams : - parameters_.variables.value()) { + parameters_.variables.value()) { if (nemoVariableParams.nemoName.value() == "SLA") { isAltimeter_ = true; } @@ -106,15 +100,15 @@ NemoFeedback::NemoFeedback( isProfile = true; } nameData_.variable_names.emplace_back(nemoVariableParams.nemoName.value()); - nameData_.legacy_ops_qc_conventions.emplace_back(obsdb_.has("QCFlags", - nemoVariableParams.name.value())); + nameData_.legacy_ops_qc_conventions.emplace_back( + obsdb_.has("QCFlags", nemoVariableParams.name.value())); nameData_.long_names.emplace_back(nemoVariableParams.longName.value()); nameData_.unit_names.emplace_back(nemoVariableParams.units.value()); - isExtraVariable_.emplace_back(nemoVariableParams.extravar.value() - .value_or(false)); + isExtraVariable_.emplace_back( + nemoVariableParams.extravar.value().value_or(false)); auto additionalVariablesParams = nemoVariableParams.variables.value(); for (const NemoFeedbackAddVariableParameters& addVariableParams : - additionalVariablesParams) { + additionalVariablesParams) { auto add_suffix = addVariableParams.feedbackSuffix.value(); if (new_name(nameData_.additional_names, add_suffix)) { nameData_.additional_names.emplace_back(add_suffix); @@ -123,22 +117,22 @@ NemoFeedback::NemoFeedback( } if (isProfile && isAltimeter_) - throw eckit::BadValue(std::string("NemoFeedback::postFilter cannot write") - + " profile and altimeter data to the same file", Here()); + throw eckit::BadValue(std::string("NemoFeedback::postFilter cannot write") + + " profile and altimeter data to the same file", + Here()); } NemoFeedback::~NemoFeedback() { oops::Log::trace() << "NemoFeedback destructor " << std::endl; } -void NemoFeedback::priorFilter(const ufo::GeoVaLs & gv) { +void NemoFeedback::priorFilter(const ufo::GeoVaLs& gv) { oops::Log::trace() << "NemoFeedback priorFilter" << std::endl; } -void NemoFeedback::postFilter(const ufo::GeoVaLs & gv, - const ioda::ObsVector &ov, - const ioda::ObsVector &bv, - const ufo::ObsDiagnostics &dv) { +void NemoFeedback::postFilter(const ufo::GeoVaLs& gv, const ioda::ObsVector& ov, + const ioda::ObsVector& bv, + const ufo::ObsDiagnostics& dv) { oops::Log::trace() << "NemoFeedback postFilter" << std::endl; eckit::PathName testDataPath(parameters_.Filename); @@ -152,8 +146,8 @@ void NemoFeedback::postFilter(const ufo::GeoVaLs & gv, } // Handle the where option. - std::vector to_write = ufo::processWhere(parameters_.where, data_, - ufo::WhereOperator::AND); + std::vector to_write = + ufo::processWhere(parameters_.where, data_, ufo::WhereOperator::AND); if (to_write.size() != obsdb_.nlocs()) to_write.assign(obsdb_.nlocs(), true); // exclude all but the latest altimetry observations @@ -164,48 +158,48 @@ void NemoFeedback::postFilter(const ufo::GeoVaLs & gv, auto n_to_write = std::count(to_write.begin(), to_write.end(), true); oops::Log::trace() << "NemoFeedback postFilter : number of observations " << "to write = " << n_to_write << std::endl; + NemoFeedbackDataCreator creator(obsdb_, ov, to_write); + auto nLevelsLocal = creator.indexer()->n_levels(); + auto [juldReferenceGlobal, nLevelsGlobal] = // NOLINT(*) + mpiSync(nLevelsLocal); if (n_to_write > 0) { - NemoFeedbackDataCreator creator(obsdb_, ov, to_write); - - feedback_io::MetaData metaData(setupMetaData(creator)); + feedback_io::MetaData metaData( + setupMetaData(creator, juldReferenceGlobal, nLevelsGlobal)); OutputDtype dtype = parameters_.type.value().value_or(OutputDtype::Double); if (dtype == OutputDtype::Float) { - feedback_io::Writer writer(testDataPath, - metaData, - nameData_, + feedback_io::Writer writer(testDataPath, metaData, nameData_, isExtraVariable_); - write_all_data (writer, creator); + write_all_data(writer, creator); } else { - feedback_io::Writer writer(testDataPath, - metaData, - nameData_, + feedback_io::Writer writer(testDataPath, metaData, nameData_, isExtraVariable_); - write_all_data (writer, creator); + write_all_data(writer, creator); } } oops::Log::trace() << "NemoFeedback postFilter done" << std::endl; } template -void NemoFeedback::write_all_data(feedback_io::Writer& writer, - const NemoFeedbackDataCreator& creator) const -{ - feedback_io::Data wholeReportQCData(creator.indexer(), +void NemoFeedback::write_all_data( + feedback_io::Writer& writer, + const NemoFeedbackDataCreator& creator) const { + feedback_io::Data wholeReportQCData( + creator.indexer(), std::vector(creator.indexer()->n_source_data(), - feedback_io::QC::Level::None)); + feedback_io::QC::Level::None)); feedback_io::Data wholeReportPositionQCData; feedback_io::Data wholeReportTimeQCData; std::vector do_not_assimilate; for (const NemoFeedbackVariableParameters& nemoVariableParams : - parameters_.variables.value()) { + parameters_.variables.value()) { auto nemo_name = nemoVariableParams.nemoName.value(); auto ufo_name = nemoVariableParams.name.value(); - auto obs_group = nemoVariableParams.iodaObsGroup.value() - .value_or("ObsValue"); + auto obs_group = + nemoVariableParams.iodaObsGroup.value().value_or("ObsValue"); - feedback_io::Data variableData(creator.create(obs_group, ufo_name, - T(0))); + feedback_io::Data variableData( + creator.create(obs_group, ufo_name, T(0))); auto extra_var = nemoVariableParams.extravar.value().value_or(false); if (extra_var) { @@ -225,43 +219,46 @@ void NemoFeedback::write_all_data(feedback_io::Writer& writer, const size_t iv = flags_->varnames().find(ufo_name); std::vector variable_qcFlags; variable_qcFlags.assign((*flags_)[iv].begin(), (*flags_)[iv].end()); - variableQCFlagsData = feedback_io::Data(creator.indexer(), - variable_qcFlags); + variableQCFlagsData = + feedback_io::Data(creator.indexer(), variable_qcFlags); } // If profile data, write to level QC Flags if (variableData.n_levels() == 1) { - writer.write_variable_surf_qc( - nemo_name + "_QC_FLAGS", variableQCFlagsData, 0); + writer.write_variable_surf_qc(nemo_name + "_QC_FLAGS", + variableQCFlagsData, 0); } else { - writer.write_variable_level_qc( - nemo_name + "_LEVEL_QC_FLAGS", variableQCFlagsData, 0); + writer.write_variable_level_qc(nemo_name + "_LEVEL_QC_FLAGS", + variableQCFlagsData, 0); } // Quality control rank variables if (obsdb_.has("DiagnosticFlags/FinalReject", ufo_name)) { feedback_io::Data variableFinalQCData = - creator.create("DiagnosticFlags/FinalReject", ufo_name, - ufo::DiagnosticFlag(0), feedback_io::QC::Level::Bad, - feedback_io::QC::Level::Good); + creator.create("DiagnosticFlags/FinalReject", ufo_name, + ufo::DiagnosticFlag(0), feedback_io::QC::Level::Bad, + feedback_io::QC::Level::Good); // Add do not assimilate flag if required to the final QC information. if (obsdb_.has("DiagnosticFlags/DoNotAssimilate", ufo_name)) { feedback_io::Data variableDoNotAssimilateData( creator.create("DiagnosticFlags/DoNotAssimilate", ufo_name, - ufo::DiagnosticFlag(0), feedback_io::QC::Level::DoNotAssimilate, - feedback_io::QC::Level::None)); + ufo::DiagnosticFlag(0), + feedback_io::QC::Level::DoNotAssimilate, + feedback_io::QC::Level::None)); for (size_t iProfile = 0; - iProfile < variableDoNotAssimilateData.n_obs(); ++iProfile) { + iProfile < variableDoNotAssimilateData.n_obs(); ++iProfile) { for (size_t iLevel = 0; - iLevel < variableDoNotAssimilateData.length(iProfile); - ++iLevel) { + iLevel < variableDoNotAssimilateData.length(iProfile); + ++iLevel) { if (variableDoNotAssimilateData(iProfile, iLevel) == feedback_io::QC::Level::DoNotAssimilate) { variableFinalQCData(iProfile, iLevel) = - static_cast( - static_cast(variableFinalQCData(iProfile, iLevel)) + - static_cast(feedback_io::QC::Level::DoNotAssimilate)); + static_cast( + static_cast( + variableFinalQCData(iProfile, iLevel)) + + static_cast( + feedback_io::QC::Level::DoNotAssimilate)); } } } @@ -270,17 +267,17 @@ void NemoFeedback::write_all_data(feedback_io::Writer& writer, // set per-profile quality rank for the variable feedback_io::Data wholeVariableQCData( creator.indexer(), std::vector( - creator.indexer()->n_source_data(), feedback_io::QC::Level::None)); + creator.indexer()->n_source_data(), + feedback_io::QC::Level::None)); feedback_io::wholeReportFromPerProfile(variableFinalQCData, - wholeVariableQCData); + wholeVariableQCData); // update set per-profile quality rank for the whole report feedback_io::wholeReportFromPerProfile(variableFinalQCData, - wholeReportQCData); + wholeReportQCData); - writer.write_variable_surf_qc(nemo_name + "_QC", - wholeVariableQCData); + writer.write_variable_surf_qc(nemo_name + "_QC", wholeVariableQCData); writer.write_variable_level_qc(nemo_name + "_LEVEL_QC", - variableFinalQCData); + variableFinalQCData); // Whole Observation Position report QC const std::string positionQCGroup("DiagnosticFlags/PositionReject"); @@ -291,38 +288,37 @@ void NemoFeedback::write_all_data(feedback_io::Writer& writer, if (wholeReportPositionQCData.n_obs() == 0) { wholeReportPositionQCData = feedback_io::Data( creator.indexer(), std::vector( - creator.indexer()->n_source_data(), - feedback_io::QC::Level::None)); + creator.indexer()->n_source_data(), + feedback_io::QC::Level::None)); } feedback_io::wholeReportFromPerProfile(QCData, - wholeReportPositionQCData); + wholeReportPositionQCData); } // Whole Observation time report QC const std::string timeQCGroup("DiagnosticFlags/TimeReject"); if (obsdb_.has(timeQCGroup, ufo_name)) { - feedback_io::Data QCData = - creator.create(timeQCGroup, ufo_name, ufo::DiagnosticFlag(0), - feedback_io::QC::Level::Bad, feedback_io::QC::Level::Good); + feedback_io::Data 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( creator.indexer(), std::vector( - creator.indexer()->n_source_data(), - feedback_io::QC::Level::None)); + creator.indexer()->n_source_data(), + feedback_io::QC::Level::None)); } - feedback_io::wholeReportFromPerProfile(QCData, - wholeReportTimeQCData); + feedback_io::wholeReportFromPerProfile(QCData, wholeReportTimeQCData); } } // Write additional variables for this variable auto additionalVariablesParams = nemoVariableParams.variables.value(); for (const NemoFeedbackAddVariableParameters& addParams : - additionalVariablesParams) { + additionalVariablesParams) { auto add_name = nemo_name + "_" + addParams.feedbackSuffix.value(); std::string ioda_group = addParams.iodaGroup.value(); - feedback_io::Data variableAdditionalData(creator.create(ioda_group, - ufo_name, T(0))); + feedback_io::Data variableAdditionalData( + creator.create(ioda_group, ufo_name, T(0))); writer.write_variable(add_name, variableAdditionalData); } } // loop: variables @@ -331,12 +327,12 @@ void NemoFeedback::write_all_data(feedback_io::Writer& writer, writer.write_variable_surf_qc("OBSERVATION_QC", wholeReportQCData); const std::string depthQCGroup("DiagnosticFlags/DepthReject"); - const std::string depthVariable = parameters_.depthVariable.value() - .value_or(static_cast(defaultDepthVariable)); + const std::string depthVariable = parameters_.depthVariable.value().value_or( + static_cast(defaultDepthVariable)); if (obsdb_.has(depthQCGroup, depthVariable)) { - feedback_io::Data depthQCData( - creator.create(depthQCGroup, depthVariable, ufo::DiagnosticFlag(0), - feedback_io::QC::Level::Bad, feedback_io::QC::Level::Good)); + feedback_io::Data 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) { @@ -348,29 +344,28 @@ void NemoFeedback::write_all_data(feedback_io::Writer& writer, } feedback_io::MetaData NemoFeedback::setupMetaData( - const NemoFeedbackDataCreator& creator) const { - - feedback_io::Data lats(creator.create("MetaData", "latitude", - static_cast(0))); - feedback_io::Data lons(creator.create("MetaData", "longitude", - static_cast(0))); - const std::string depthGroup = parameters_.depthGroup.value() - .value_or(static_cast(defaultDepthGroup)); - const std::string depthVariable = parameters_.depthVariable.value() - .value_or(static_cast(defaultDepthVariable)); + const NemoFeedbackDataCreator& creator, util::DateTime juldReferenceGlobal, + size_t nLevelsGlobal) const { + feedback_io::Data lats( + creator.create("MetaData", "latitude", static_cast(0))); + feedback_io::Data lons( + creator.create("MetaData", "longitude", static_cast(0))); + const std::string depthGroup = parameters_.depthGroup.value().value_or( + static_cast(defaultDepthGroup)); + const std::string depthVariable = parameters_.depthVariable.value().value_or( + static_cast(defaultDepthVariable)); feedback_io::Data depths; if (obsdb_.has(depthGroup, depthVariable)) { - depths = feedback_io::Data(creator.create(depthGroup, depthVariable, - static_cast(0))); + depths = feedback_io::Data( + creator.create(depthGroup, depthVariable, static_cast(0))); } else { depths = feedback_io::Data(creator.indexer(), - std::vector(obsdb_.nlocs(), 0)); + std::vector(obsdb_.nlocs(), 0)); } - auto [juldReferenceGlobal, nLevelsGlobal] = mpiSync(lats.n_levels()); // NOLINT(*) - - auto [juldReference, julianDays] = creator.create_datetimes("MetaData", // NOLINT(*) - "dateTime", juldReferenceGlobal); + auto [juldReference, julianDays] = // NOLINT(*) + creator.create_datetimes("MetaData", + "dateTime", juldReferenceGlobal); feedback_io::Data stationIDs, stationTypes; if (isAltimeter_) { @@ -379,36 +374,29 @@ feedback_io::MetaData NemoFeedback::setupMetaData( std::tie(stationIDs, stationTypes) = setupIDs(creator); } - return feedback_io::MetaData(lats, - lons, - julianDays, - depths, - stationTypes, - stationIDs, - nLevelsGlobal, - juldReference); + return feedback_io::MetaData(lats, lons, julianDays, depths, stationTypes, + stationIDs, nLevelsGlobal, juldReference); } std::tuple, feedback_io::Data> - NemoFeedback::setupIDs(const NemoFeedbackDataCreator& creator) const { +NemoFeedback::setupIDs(const NemoFeedbackDataCreator& creator) const { oops::Log::trace() << "NemoFeedback::setupIDs: starting" << std::endl; constexpr size_t stationIDWidth = 8; feedback_io::Data stationIDs; bool stationIdentificationAvailable = false; if (obsdb_.has("MetaData", "stationIdentification")) { - stationIDs = feedback_io::Data(creator.create("MetaData", - "stationIdentification", - std::string(""), stationIDWidth)); + stationIDs = feedback_io::Data(creator.create( + "MetaData", "stationIdentification", std::string(""), stationIDWidth)); stationIdentificationAvailable = true; } constexpr size_t buoyIDWidth = 8; const std::string missingStringFeedback = - feedback_io::typeToFill::value(); + feedback_io::typeToFill::value(); if (obsdb_.has("MetaData", "buoyIdentifier")) { - feedback_io::Data buoyIDs(creator.create("MetaData", - "buoyIdentifier", int32_t(0), buoyIDWidth)); + feedback_io::Data buoyIDs( + creator.create("MetaData", "buoyIdentifier", int32_t(0), buoyIDWidth)); for (size_t iOb = 0; iOb < stationIDs.n_obs(); ++iOb) { if (stationIdentificationAvailable) { if (buoyIDs[iOb] != missingStringFeedback && @@ -416,7 +404,7 @@ std::tuple, feedback_io::Data> stationIDs[iOb] = buoyIDs[iOb]; } } else { - stationIDs[iOb] = buoyIDs[iOb]; + stationIDs[iOb] = buoyIDs[iOb]; } } stationIdentificationAvailable = true; @@ -424,27 +412,26 @@ std::tuple, feedback_io::Data> if (!stationIdentificationAvailable) { std::vector blankStationIDData(obsdb_.nlocs(), - std::string(8, ' ')); - stationIDs = feedback_io::Data(creator.indexer(), - blankStationIDData); + std::string(8, ' ')); + stationIDs = + feedback_io::Data(creator.indexer(), blankStationIDData); } feedback_io::Data stationTypes; constexpr size_t stationTypeWidth = 4; if (obsdb_.has("MetaData", "fdbk_station_type")) { - stationTypes = feedback_io::Data(creator.create("MetaData", - "fdbk_station_type", int32_t(0), stationTypeWidth, true)); + stationTypes = feedback_io::Data(creator.create( + "MetaData", "fdbk_station_type", int32_t(0), stationTypeWidth, true)); } else { std::vector blankStationTypeData(obsdb_.nlocs(), - std::string(4, ' ')); - stationTypes = feedback_io::Data(creator.indexer(), - blankStationTypeData); + std::string(4, ' ')); + stationTypes = + feedback_io::Data(creator.indexer(), blankStationTypeData); } - return std::make_tuple< - feedback_io::Data, - feedback_io::Data> ( - std::move(stationIDs), std::move(stationTypes)); + return std::make_tuple, + feedback_io::Data>( + std::move(stationIDs), std::move(stationTypes)); } // Filter to retrieve the most recent version of the data @@ -481,9 +468,8 @@ void NemoFeedback::updateAltimeterSelection(std::vector& to_write) const { for (int ymd_to_find : ymd_set) { for (int sid_to_find : sid_set) { latest_version = 0; - for (size_t i=0; i < n_obs; ++i) { - if (to_write[i] && - ymd[i] == ymd_to_find && + for (size_t i = 0; i < n_obs; ++i) { + if (to_write[i] && ymd[i] == ymd_to_find && satellite_ids[i] == sid_to_find && version[i] != version_missing_value && version[i] > latest_version) { @@ -495,11 +481,9 @@ void NemoFeedback::updateAltimeterSelection(std::vector& to_write) const { // as not to write already. Therefore, we only need to do anything // further if latest_version > 0. if (latest_version > 0) { - for (size_t i=0; i < n_obs; ++i) { - if (to_write[i] && - ymd[i] == ymd_to_find && - satellite_ids[i] == sid_to_find && - version[i] < latest_version) { + for (size_t i = 0; i < n_obs; ++i) { + if (to_write[i] && ymd[i] == ymd_to_find && + satellite_ids[i] == sid_to_find && version[i] < latest_version) { to_write[i] = false; } } @@ -508,14 +492,12 @@ void NemoFeedback::updateAltimeterSelection(std::vector& to_write) const { } } -std::tuple NemoFeedback::mpiSync(size_t nLevelsLocal) - const { +std::tuple NemoFeedback::mpiSync( + size_t nLevelsLocal) const { auto& comm = obsdb_.comm(); - util::DateTime juldReferenceLocal = parameters_.refDate.value() - .value_or(util::DateTime{"1950-01-01T00:00:00Z"}); std::vector datetimes; obsdb_.get_db("MetaData", "dateTime", datetimes); - juldReferenceLocal = parameters_.refDate.value().value_or(datetimes[0]); + util::DateTime juldReferenceLocal = parameters_.refDate.value().value_or(datetimes[0]); oops::Log::trace() << "NemoFeedback::mpiSync " << juldReferenceLocal << " and " << nLevelsLocal << " levels" << std::endl; @@ -523,8 +505,7 @@ std::tuple NemoFeedback::mpiSync(size_t nLevelsLocal) size_t nLevelsGlobal = nLevelsLocal; if (comm.size() == 0) { return std::make_tuple( - std::move(juldReferenceGlobal), - std::move(nLevelsGlobal)); + std::move(juldReferenceGlobal), std::move(nLevelsGlobal)); } std::vector allNLevels(comm.size(), 0); @@ -542,10 +523,10 @@ std::tuple NemoFeedback::mpiSync(size_t nLevelsLocal) } return std::make_tuple(std::move(juldReferenceGlobal), - std::move(nLevelsGlobal)); + std::move(nLevelsGlobal)); } -void NemoFeedback::print(std::ostream & os) const { +void NemoFeedback::print(std::ostream& os) const { os << "NemoFeedback: config = " << parameters_ << std::endl; } } // namespace nemo_feedback diff --git a/src/nemo-feedback/NemoFeedback.h b/src/nemo-feedback/NemoFeedback.h index c519da6..e5e0fd6 100644 --- a/src/nemo-feedback/NemoFeedback.h +++ b/src/nemo-feedback/NemoFeedback.h @@ -63,8 +63,9 @@ class NemoFeedback : public ufo::ObsFilterBase, /// the file during construction of the feedback file. This step should /// ensure that the number of levels and the reference Julian day are /// shared across MPI processes. - feedback_io::MetaData setupMetaData(const NemoFeedbackDataCreator& creator) - const; + feedback_io::MetaData setupMetaData(const NemoFeedbackDataCreator& creator, + const util::DateTime juldReferenceGlobal, + const size_t nLevelsGlobal) const; /// \brief Setup the NEMO STATION_TYPES and STATION_IDS netCDF variables /// In the case of altimetry data, this requires additional processing /// of the satelliteIdentifier to extract these variables. diff --git a/src/nemo-feedback/feedback_io/DataIndexer.cc b/src/nemo-feedback/feedback_io/DataIndexer.cc index aa9df79..c2a357b 100644 --- a/src/nemo-feedback/feedback_io/DataIndexer.cc +++ b/src/nemo-feedback/feedback_io/DataIndexer.cc @@ -29,6 +29,10 @@ DataIndexer::DataIndexer(const size_t nObs, const size_t nLevels, nObs_(nObs), nLevels_(nLevels), sourceDataSize_(sourceDataSize), indices_(indices), starts_(starts) { counts_.reserve(nObs); + if (nObs == 0) { + validate(); + return; + } for (size_t iProf = 0; iProf < nObs - 1; ++iProf) { counts_.push_back(starts[iProf +1] - starts[iProf]); } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4a974d8..121f4ac 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -55,3 +55,26 @@ ecbuild_add_test( TARGET test_nemo_feedback_hofx_profiles_two_vars_file COMMAND ./mains/compare_nc_cdl.sh TEST_DEPENDS test_nemo_feedback_hofx_profiles_two_vars_writer) +ecbuild_add_test( TARGET test_nemo_feedback_hofx_mpi_2_writer + OMP 1 + MPI 2 + ARGS testinput/hofx_mpi_2_writer.yaml + COMMAND test_NemoInputsFilters.x ) + +ecbuild_add_test( TARGET test_nemo_feedback_hofx_mpi_2_file + OMP 1 + ARGS testoutput/test_hofx_mpi_2_writer_out_00001.nc testoutput/test_hofx_mpi_2_writer_out_00001_ref.cdl + COMMAND ./mains/compare_nc_cdl.sh + TEST_DEPENDS test_nemo_feedback_hofx_mpi_2_writer) + +ecbuild_add_test( TARGET test_nemo_feedback_hofx_mpi_3_writer + OMP 1 + MPI 3 + ARGS testinput/hofx_mpi_3_writer.yaml + COMMAND test_NemoInputsFilters.x ) + +ecbuild_add_test( TARGET test_nemo_feedback_hofx_mpi_3_file + OMP 1 + ARGS testoutput/test_hofx_mpi_3_writer_out_00001.nc testoutput/test_hofx_mpi_3_writer_out_00001_ref.cdl + COMMAND ./mains/compare_nc_cdl.sh + TEST_DEPENDS test_nemo_feedback_hofx_mpi_3_writer) diff --git a/src/tests/Data/CMakeLists.txt b/src/tests/Data/CMakeLists.txt index 3c5db27..2892553 100644 --- a/src/tests/Data/CMakeLists.txt +++ b/src/tests/Data/CMakeLists.txt @@ -5,10 +5,12 @@ hofx_sic_obs.nc hofx_two_vars_obs.nc hofx_potm_obs.nc hofx_prof_2var_obs.nc +hofx_mpi_obs.nc test_hofx3d_nc_prof_2vars_writer_geovals.nc test_hofx_profiles_writer_geovals.nc test_hofx_sic_writer_geovals.nc test_hofx_two_vars_writer_geovals.nc +test_hofx_mpi_writer_geovals.nc ) foreach(FILENAME ${nemofeedback_test_data}) diff --git a/src/tests/Data/hofx_mpi_obs.nc b/src/tests/Data/hofx_mpi_obs.nc new file mode 100644 index 0000000..898387f --- /dev/null +++ b/src/tests/Data/hofx_mpi_obs.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f50015f21c6fd607f0e7c34b45bc6d390f723eada4b3d1f20f59c6d18868836 +size 92924 diff --git a/src/tests/Data/test_hofx_mpi_writer_geovals.nc b/src/tests/Data/test_hofx_mpi_writer_geovals.nc new file mode 100644 index 0000000..b3626d5 --- /dev/null +++ b/src/tests/Data/test_hofx_mpi_writer_geovals.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8c2203f16c37e89a88d39d83e3442848ac3e36df7f3f51cf2c877253f497fd1 +size 304 diff --git a/src/tests/testinput/CMakeLists.txt b/src/tests/testinput/CMakeLists.txt index 28070ee..f95952a 100644 --- a/src/tests/testinput/CMakeLists.txt +++ b/src/tests/testinput/CMakeLists.txt @@ -5,6 +5,8 @@ list( APPEND fdbk_writer_test_input hofx_two_vars_writer.yaml hofx_profiles_writer.yaml hofx_profiles_2var_writer.yaml + hofx_mpi_2_writer.yaml + hofx_mpi_3_writer.yaml test_name_map.yaml ) diff --git a/src/tests/testinput/hofx_mpi_2_writer.yaml b/src/tests/testinput/hofx_mpi_2_writer.yaml new file mode 100644 index 0000000..4f7b5a4 --- /dev/null +++ b/src/tests/testinput/hofx_mpi_2_writer.yaml @@ -0,0 +1,38 @@ +time window: + begin: 2024-06-28T12:00:00Z + end: 2025-07-18T12:00:00Z +observations: + - obs space: + name: Sea Surface Height Anomaly MPI + obsdatain: + engine: + type: H5File + obsfile: Data/hofx_mpi_obs.nc + obsdataout: + engine: + type: H5File + obsfile: testoutput/test_hofx_mpi_2_jopa_out.nc + simulated variables: [seaSurfaceHeightAnomaly] + obs filters: + - filter: NEMO Feedback Writer + reference date: 2021-06-30T12:00:00Z + filename: testoutput/test_hofx_mpi_2_writer_out.nc + observation alias file: testinput/test_name_map.yaml + where: + - variable: + name: DiagnosticFlags/ObsToWrite/seaSurfaceHeightAnomaly + value: is_true + variables: + - name: seaSurfaceHeightAnomaly + nemo name: SLA + long name: Sea Surface Height Anomaly + units: Fraction + additional variables: + - name: seaSurfaceHeightAnomaly + feedback suffix: Hx + ioda group: HofX + benchmarkFlag: 1000 # just to keep the ObsFilters test happy + flaggedBenchmark: 0 + HofX: hofx + geovals: + filename: Data/test_hofx_mpi_writer_geovals.nc diff --git a/src/tests/testinput/hofx_mpi_3_writer.yaml b/src/tests/testinput/hofx_mpi_3_writer.yaml new file mode 100644 index 0000000..5d057f0 --- /dev/null +++ b/src/tests/testinput/hofx_mpi_3_writer.yaml @@ -0,0 +1,34 @@ +time window: + begin: 2024-06-28T12:00:00Z + end: 2025-07-18T12:00:00Z +observations: + - obs space: + name: Sea Surface Height Anomaly MPI + obsdatain: + engine: + type: H5File + obsfile: Data/hofx_mpi_obs.nc + obsdataout: + engine: + type: H5File + obsfile: testoutput/test_hofx_mpi_3_jopa_out.nc + simulated variables: [seaSurfaceHeightAnomaly] + obs filters: + - filter: NEMO Feedback Writer + reference date: 2021-06-30T12:00:00Z + filename: testoutput/test_hofx_mpi_3_writer_out.nc + observation alias file: testinput/test_name_map.yaml + variables: + - name: seaSurfaceHeightAnomaly + nemo name: SLA + long name: Sea Surface Height Anomaly + units: Fraction + additional variables: + - name: seaSurfaceHeightAnomaly + feedback suffix: Hx + ioda group: HofX + benchmarkFlag: 1000 # just to keep the ObsFilters test happy + flaggedBenchmark: 0 + HofX: hofx + geovals: + filename: Data/test_hofx_mpi_writer_geovals.nc diff --git a/src/tests/testinput/test_name_map.yaml b/src/tests/testinput/test_name_map.yaml index d47a15e..3bf5a5f 100644 --- a/src/tests/testinput/test_name_map.yaml +++ b/src/tests/testinput/test_name_map.yaml @@ -11,4 +11,5 @@ variable maps: alias: sea_water_salinity - name: depthBelowWaterSurface alias: depth - + - name: seaSurfaceHeightAnomaly + alias: sea_surface_height_anomaly diff --git a/src/tests/testoutput/CMakeLists.txt b/src/tests/testoutput/CMakeLists.txt index 4fe35e9..d38f409 100644 --- a/src/tests/testoutput/CMakeLists.txt +++ b/src/tests/testoutput/CMakeLists.txt @@ -5,6 +5,8 @@ list( APPEND fdbk_writer_test_output test_hofx_two_vars_writer_out_ref.cdl test_hofx_profiles_writer_out_ref.cdl test_hofx3d_nc_prof_2vars_writer_out_ref.cdl + test_hofx_mpi_2_writer_out_00001_ref.cdl + test_hofx_mpi_3_writer_out_00001_ref.cdl ) foreach(FILENAME ${fdbk_writer_test_output}) diff --git a/src/tests/testoutput/test_hofx_mpi_2_writer_out_00001_ref.cdl b/src/tests/testoutput/test_hofx_mpi_2_writer_out_00001_ref.cdl new file mode 100644 index 0000000..ae49889 --- /dev/null +++ b/src/tests/testoutput/test_hofx_mpi_2_writer_out_00001_ref.cdl @@ -0,0 +1,172 @@ +netcdf test_hofx_mpi_2_writer_out_00001 { +dimensions: + N_QCF = 2 ; + STRINGGRID = 1 ; + STRINGJULD = 14 ; + STRINGNAM = 8 ; + STRINGTYP = 4 ; + STRINGWMO = 8 ; + N_OBS = 1 ; + N_LEVELS = 1 ; + N_VARS = 1 ; + N_ENTRIES = 1 ; +variables: + char JULD_REFERENCE(STRINGJULD) ; + JULD_REFERENCE:long_name = "Date of reference for julian days" ; + JULD_REFERENCE:Conventions = "YYYYMMDDHHMMSS" ; + char VARIABLES(N_VARS, STRINGNAM) ; + VARIABLES:long_name = "List of variables in feedback files" ; + char ENTRIES(N_ENTRIES, STRINGNAM) ; + ENTRIES:long_name = "List of additional entries for each variable in feedback files" ; + double LATITUDE(N_OBS) ; + LATITUDE:units = "degrees_north" ; + LATITUDE:long_name = "latitude" ; + double LONGITUDE(N_OBS) ; + LONGITUDE:units = "degrees_east" ; + LONGITUDE:long_name = "longitude" ; + double DEPTH(N_OBS, N_LEVELS) ; + DEPTH:_FillValue = 99999. ; + DEPTH:units = "metre" ; + DEPTH:long_name = "Depth" ; + double JULD(N_OBS) ; + JULD:units = "days since JULD_REFERENCE" ; + JULD:long_name = "Julian day" ; + char STATION_IDENTIFIER(N_OBS, STRINGWMO) ; + STATION_IDENTIFIER:long_name = "Station identifier" ; + char STATION_TYPE(N_OBS, STRINGTYP) ; + STATION_TYPE:long_name = "Code instrument type" ; + int DEPTH_QC(N_OBS, N_LEVELS) ; + DEPTH_QC:long_name = "Quality on depth" ; + DEPTH_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int DEPTH_QC_FLAGS(N_OBS, N_LEVELS, N_QCF) ; + DEPTH_QC_FLAGS:long_name = "Quality on depth" ; + DEPTH_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int OBSERVATION_QC(N_OBS) ; + OBSERVATION_QC:long_name = "Quality on observation" ; + OBSERVATION_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int OBSERVATION_QC_FLAGS(N_OBS, N_QCF) ; + OBSERVATION_QC_FLAGS:long_name = "Quality on observation" ; + OBSERVATION_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int POSITION_QC(N_OBS) ; + POSITION_QC:long_name = "Quality on position" ; + POSITION_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int POSITION_QC_FLAGS(N_OBS, N_QCF) ; + POSITION_QC_FLAGS:long_name = "Quality on position" ; + POSITION_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int JULD_QC(N_OBS) ; + JULD_QC:long_name = "Quality on date and time" ; + JULD_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int JULD_QC_FLAGS(N_OBS, N_QCF) ; + JULD_QC_FLAGS:long_name = "Quality on date and time" ; + JULD_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int ORIGINAL_FILE_INDEX(N_OBS) ; + ORIGINAL_FILE_INDEX:long_name = "Index in original data file" ; + double SLA_OBS(N_OBS, N_LEVELS) ; + SLA_OBS:long_name = "Sea Surface Height Anomaly" ; + SLA_OBS:units = "Fraction" ; + SLA_OBS:_FillValue = 99999. ; + double SLA_Hx(N_OBS, N_LEVELS) ; + SLA_Hx:long_name = "Sea Surface Height Anomaly Hx" ; + SLA_Hx:units = "Fraction" ; + SLA_Hx:_FillValue = 99999. ; + int SLA_QC_FLAGS(N_OBS, N_QCF) ; + SLA_QC_FLAGS:_FillValue = 0 ; + SLA_QC_FLAGS:long_name = "quality flags on Sea Surface Height Anomaly" ; + SLA_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int SLA_LEVEL_QC_FLAGS(N_OBS, N_LEVELS, N_QCF) ; + SLA_LEVEL_QC_FLAGS:_FillValue = 0 ; + SLA_LEVEL_QC_FLAGS:long_name = "quality flags for each level on Sea Surface Height Anomaly" ; + SLA_LEVEL_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int SLA_QC(N_OBS) ; + SLA_QC:long_name = "quality on Sea Surface Height Anomaly" ; + SLA_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int SLA_LEVEL_QC(N_OBS, N_LEVELS) ; + SLA_LEVEL_QC:_FillValue = 0 ; + SLA_LEVEL_QC:long_name = "quality for each level on Sea Surface Height Anomaly" ; + SLA_LEVEL_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int SLA_IOBSI(N_OBS) ; + SLA_IOBSI:long_name = "ORCA grid search I coordinate" ; + int SLA_IOBSJ(N_OBS) ; + SLA_IOBSJ:long_name = "ORCA grid search J coordinate" ; + int SLA_IOBSK(N_OBS, N_LEVELS) ; + SLA_IOBSK:long_name = "ORCA grid search K coordinate" ; + char SLA_GRID(STRINGGRID) ; + SLA_GRID:long_name = "ORCA grid search grid (T,U,V)" ; + +// global attributes: + :title = "NEMO observation operator output" ; + :Convention = "NEMO unified observation operator output" ; +data: + + JULD_REFERENCE = "20210630120000" ; + + VARIABLES = + "SLA " ; + + ENTRIES = + "Hx " ; + + LATITUDE = 28.26 ; + + LONGITUDE = 33.350002 ; + + DEPTH = + 0 ; + + JULD = 1461.1639 ; + + STATION_IDENTIFIER = + "1005 " ; + + STATION_TYPE = + "1005" ; + + DEPTH_QC = + _ ; + + DEPTH_QC_FLAGS = + _, _ ; + + OBSERVATION_QC = 0 ; + + OBSERVATION_QC_FLAGS = + _, _ ; + + POSITION_QC = _ ; + + POSITION_QC_FLAGS = + _, _ ; + + JULD_QC = _ ; + + JULD_QC_FLAGS = + _, _ ; + + ORIGINAL_FILE_INDEX = _ ; + + SLA_OBS = + 0.00099999993 ; + + SLA_Hx = + -0.072882071 ; + + SLA_QC_FLAGS = + _, _ ; + + SLA_LEVEL_QC_FLAGS = + _, _ ; + + SLA_QC = _ ; + + SLA_LEVEL_QC = + _ ; + + SLA_IOBSI = _ ; + + SLA_IOBSJ = _ ; + + SLA_IOBSK = + _ ; + + SLA_GRID = "T" ; +} diff --git a/src/tests/testoutput/test_hofx_mpi_3_writer_out_00001_ref.cdl b/src/tests/testoutput/test_hofx_mpi_3_writer_out_00001_ref.cdl new file mode 100644 index 0000000..4ffa9e2 --- /dev/null +++ b/src/tests/testoutput/test_hofx_mpi_3_writer_out_00001_ref.cdl @@ -0,0 +1,172 @@ +netcdf test_hofx_mpi_3_writer_out_00001 { +dimensions: + N_QCF = 2 ; + STRINGGRID = 1 ; + STRINGJULD = 14 ; + STRINGNAM = 8 ; + STRINGTYP = 4 ; + STRINGWMO = 8 ; + N_OBS = 1 ; + N_LEVELS = 1 ; + N_VARS = 1 ; + N_ENTRIES = 1 ; +variables: + char JULD_REFERENCE(STRINGJULD) ; + JULD_REFERENCE:long_name = "Date of reference for julian days" ; + JULD_REFERENCE:Conventions = "YYYYMMDDHHMMSS" ; + char VARIABLES(N_VARS, STRINGNAM) ; + VARIABLES:long_name = "List of variables in feedback files" ; + char ENTRIES(N_ENTRIES, STRINGNAM) ; + ENTRIES:long_name = "List of additional entries for each variable in feedback files" ; + double LATITUDE(N_OBS) ; + LATITUDE:units = "degrees_north" ; + LATITUDE:long_name = "latitude" ; + double LONGITUDE(N_OBS) ; + LONGITUDE:units = "degrees_east" ; + LONGITUDE:long_name = "longitude" ; + double DEPTH(N_OBS, N_LEVELS) ; + DEPTH:_FillValue = 99999. ; + DEPTH:units = "metre" ; + DEPTH:long_name = "Depth" ; + double JULD(N_OBS) ; + JULD:units = "days since JULD_REFERENCE" ; + JULD:long_name = "Julian day" ; + char STATION_IDENTIFIER(N_OBS, STRINGWMO) ; + STATION_IDENTIFIER:long_name = "Station identifier" ; + char STATION_TYPE(N_OBS, STRINGTYP) ; + STATION_TYPE:long_name = "Code instrument type" ; + int DEPTH_QC(N_OBS, N_LEVELS) ; + DEPTH_QC:long_name = "Quality on depth" ; + DEPTH_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int DEPTH_QC_FLAGS(N_OBS, N_LEVELS, N_QCF) ; + DEPTH_QC_FLAGS:long_name = "Quality on depth" ; + DEPTH_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int OBSERVATION_QC(N_OBS) ; + OBSERVATION_QC:long_name = "Quality on observation" ; + OBSERVATION_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int OBSERVATION_QC_FLAGS(N_OBS, N_QCF) ; + OBSERVATION_QC_FLAGS:long_name = "Quality on observation" ; + OBSERVATION_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int POSITION_QC(N_OBS) ; + POSITION_QC:long_name = "Quality on position" ; + POSITION_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int POSITION_QC_FLAGS(N_OBS, N_QCF) ; + POSITION_QC_FLAGS:long_name = "Quality on position" ; + POSITION_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int JULD_QC(N_OBS) ; + JULD_QC:long_name = "Quality on date and time" ; + JULD_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int JULD_QC_FLAGS(N_OBS, N_QCF) ; + JULD_QC_FLAGS:long_name = "Quality on date and time" ; + JULD_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int ORIGINAL_FILE_INDEX(N_OBS) ; + ORIGINAL_FILE_INDEX:long_name = "Index in original data file" ; + double SLA_OBS(N_OBS, N_LEVELS) ; + SLA_OBS:long_name = "Sea Surface Height Anomaly" ; + SLA_OBS:units = "Fraction" ; + SLA_OBS:_FillValue = 99999. ; + double SLA_Hx(N_OBS, N_LEVELS) ; + SLA_Hx:long_name = "Sea Surface Height Anomaly Hx" ; + SLA_Hx:units = "Fraction" ; + SLA_Hx:_FillValue = 99999. ; + int SLA_QC_FLAGS(N_OBS, N_QCF) ; + SLA_QC_FLAGS:_FillValue = 0 ; + SLA_QC_FLAGS:long_name = "quality flags on Sea Surface Height Anomaly" ; + SLA_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int SLA_LEVEL_QC_FLAGS(N_OBS, N_LEVELS, N_QCF) ; + SLA_LEVEL_QC_FLAGS:_FillValue = 0 ; + SLA_LEVEL_QC_FLAGS:long_name = "quality flags for each level on Sea Surface Height Anomaly" ; + SLA_LEVEL_QC_FLAGS:Conventions = "JEDI UFO QC flag conventions" ; + int SLA_QC(N_OBS) ; + SLA_QC:long_name = "quality on Sea Surface Height Anomaly" ; + SLA_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int SLA_LEVEL_QC(N_OBS, N_LEVELS) ; + SLA_LEVEL_QC:_FillValue = 0 ; + SLA_LEVEL_QC:long_name = "quality for each level on Sea Surface Height Anomaly" ; + SLA_LEVEL_QC:Conventions = "U.S. Integrated Ocean Observing System, 2017. Manual for the Use of Real-Time Oceanographic Data Quality Control Flags, Version 1.1" ; + int SLA_IOBSI(N_OBS) ; + SLA_IOBSI:long_name = "ORCA grid search I coordinate" ; + int SLA_IOBSJ(N_OBS) ; + SLA_IOBSJ:long_name = "ORCA grid search J coordinate" ; + int SLA_IOBSK(N_OBS, N_LEVELS) ; + SLA_IOBSK:long_name = "ORCA grid search K coordinate" ; + char SLA_GRID(STRINGGRID) ; + SLA_GRID:long_name = "ORCA grid search grid (T,U,V)" ; + +// global attributes: + :title = "NEMO observation operator output" ; + :Convention = "NEMO unified observation operator output" ; +data: + + JULD_REFERENCE = "20210630120000" ; + + VARIABLES = + "SLA " ; + + ENTRIES = + "Hx " ; + + LATITUDE = 28.26 ; + + LONGITUDE = 33.350002 ; + + DEPTH = + 0 ; + + JULD = 1461.1639 ; + + STATION_IDENTIFIER = + "1005 " ; + + STATION_TYPE = + "1005" ; + + DEPTH_QC = + _ ; + + DEPTH_QC_FLAGS = + _, _ ; + + OBSERVATION_QC = 0 ; + + OBSERVATION_QC_FLAGS = + _, _ ; + + POSITION_QC = _ ; + + POSITION_QC_FLAGS = + _, _ ; + + JULD_QC = _ ; + + JULD_QC_FLAGS = + _, _ ; + + ORIGINAL_FILE_INDEX = _ ; + + SLA_OBS = + 0.00099999993 ; + + SLA_Hx = + -0.072882071 ; + + SLA_QC_FLAGS = + _, _ ; + + SLA_LEVEL_QC_FLAGS = + _, _ ; + + SLA_QC = _ ; + + SLA_LEVEL_QC = + _ ; + + SLA_IOBSI = _ ; + + SLA_IOBSJ = _ ; + + SLA_IOBSK = + _ ; + + SLA_GRID = "T" ; +}