Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
b31efd8
added FDB compare to CmakeList tool is WIP and currently only loads t…
stefaniereuter Jun 17, 2024
140be9b
Create FDB from path in tool.cc directly
stefaniereuter Jun 19, 2024
7f471fc
comparison of mars keys
stefaniereuter Jun 21, 2024
7e0d412
can compare FDB but not optimized and exceptiions are missing
stefaniereuter Jul 9, 2024
ea7200f
Bitexact Memcmp, bitexact eccodes, data compare numerical
stefaniereuter Jul 12, 2024
1e5404a
move from map to vector in md5compare, add usage exampl and command l…
stefaniereuter Jul 16, 2024
86e5c38
changed the way the mars message keys are compared,added option to ig…
stefaniereuter Jul 23, 2024
69e135a
Adding option to ignore and select grib keys
stefaniereuter Jul 25, 2024
fa60969
added gribcompare eccodes_detail to allow direct use of detailed check
stefaniereuter Jul 25, 2024
5752bc5
removed annoying debug output
stefaniereuter Jul 26, 2024
14e7b3a
Added eccodes hack for buffer size adaption
stefaniereuter Jul 27, 2024
9009bdb
remove eccodes 'hack' and use grib_string_length instead
stefaniereuter Jul 29, 2024
10b7177
deleted buffer before return to avoid memleak
stefaniereuter Jul 31, 2024
3efbf62
Bugfixes after rebase
stefaniereuter Aug 12, 2025
2bff5dc
CLI improvments
stefaniereuter Aug 19, 2025
564257a
Adding comparison tests
stefaniereuter Aug 19, 2025
e59d7ac
Feature, single fdb divergent experiment
stefaniereuter Aug 24, 2025
56c24d5
Add more tests, bugfix grib comparison header only
stefaniereuter Aug 25, 2025
f1603af
Refactor Mars key compare
stefaniereuter Aug 26, 2025
fb1a7da
Mars refactor cleanup, Test fix
stefaniereuter Aug 26, 2025
b0b4e40
refactoring start
stefaniereuter Jan 8, 2026
5a4a728
formatting
stefaniereuter Jan 14, 2026
b6071ff
Misc naminging and small structure fixes
pgeier Jan 16, 2026
178997f
Rename and delete unused files
pgeier Jan 16, 2026
81cab14
Use metkit/codes/api instead of directly using eccodes\.h
pgeier Jan 20, 2026
871c092
Clean up compare/grib structure
pgeier Jan 21, 2026
fea7aeb
Rework handling difference in mars requests
pgeier Jan 22, 2026
15b1167
Small restructures, remove comments
pgeier Jan 27, 2026
58cdc31
Remove IComporator und move directories
pgeier Jan 27, 2026
8367a32
Restructure tests
pgeier Jan 28, 2026
aab54c3
Fix fdb-compare tests and add FP data checks
pgeier Jan 28, 2026
ef574d0
Add licensce to .h and .cc files
pgeier Jan 28, 2026
6dc8d26
Add more documentation; remove grib/Message
pgeier Jan 29, 2026
d42befa
Address PR comments
pgeier Feb 3, 2026
ce404ce
Avoid duplicating data file
pgeier Feb 4, 2026
0436ed8
Fix bitwise comparison; add tests for methods
pgeier Feb 4, 2026
059fce7
Finalize PR
pgeier Feb 4, 2026
bc344c8
Use eckit::Log; Fix headers in CMakeLists; Refactor grib compare Result
pgeier Feb 6, 2026
615b645
Check return code in test; Minor changes
pgeier Feb 6, 2026
d549af6
Use fdb5::Key and fdb5::ListElement instead of custom types
pgeier Feb 9, 2026
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
24 changes: 24 additions & 0 deletions src/fdb5/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,30 @@ ecbuild_add_executable(
remote/fdb-server.cc
LIBS fdb5 )

ecbuild_add_executable( TARGET fdb-compare
CONDITION HAVE_FDB_BUILD_TOOLS
SOURCES tools/compare/fdb-compare.cc
tools/compare/common/Types.cc
tools/compare/common/Types.h
tools/compare/common/Util.cc
tools/compare/common/Util.h
tools/compare/mars/Compare.cc
tools/compare/mars/Compare.h
tools/compare/grib/Compare.cc
tools/compare/grib/Compare.h
tools/compare/grib/CompareKeys.cc
tools/compare/grib/CompareKeys.h
tools/compare/grib/CompareHash.cc
tools/compare/grib/CompareHash.h
tools/compare/grib/CompareDatasection.cc
tools/compare/grib/CompareDatasection.h
tools/compare/grib/CompareBitwise.cc
tools/compare/grib/CompareBitwise.h
tools/compare/grib/Utils.cc
tools/compare/grib/Utils.h
INCLUDES ${ECCODES_INCLUDE_DIRS} # Please don't remove me, I am needed
LIBS fdb5 )

if ( HAVE_FDB_BUILD_TOOLS )
target_sources( fdb-lock PRIVATE tools/FDBLock.cc tools/FDBLock.h )
target_sources( fdb-unlock PRIVATE tools/FDBLock.cc tools/FDBLock.h )
Expand Down
258 changes: 258 additions & 0 deletions src/fdb5/tools/compare/common/Types.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* (C) Copyright 2025- ECMWF.
*
* This software is licensed under the terms of the Apache Licence Version 2.0
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
* In applying this licence, ECMWF does not waive the privileges and immunities
* granted to it by virtue of its status as an intergovernmental organisation nor
* does it submit to any jurisdiction.
*/
#include "Types.h"

#include "eckit/exception/Exceptions.h"
#include "eckit/log/Log.h"

#include <sstream>

namespace compare {

//---------------------------------------------------------------------------------------------------------------------

std::ostream& operator<<(std::ostream& os, const KeyDiffMap& km) {
os << "{";
for (const auto& [k, v] : km) {
os << k << "=" << "(" << (v.first ? *v.first : "<MISSING>") << ", " << (v.second ? *v.second : "<MISSING>")
<< ")" << ", ";
}
os << "}";
return os;
}

std::ostream& operator<<(std::ostream& os, const KeySet& km) {
os << "{";
for (const auto& k : km) {
os << k << ", ";
}
os << "}";
return os;
}

std::ostream& operator<<(std::ostream& os, const DataIndex& idx) {
for (const auto& [km, loc] : idx) {
os << "Key: " << km << " -> Value: " << loc << "\n";
}
return os;
}

//---------------------------------------------------------------------------------------------------------------------

void parseKeySet(KeySet& container, const std::string& keyStr) {
std::istringstream stream(keyStr);
std::string entry;

while (std::getline(stream, entry, ',')) {
container.insert(entry); // Insert the whole entry as a single string
}
}

KeySet parseKeySet(const std::string& keyStr) {
KeySet container;
parseKeySet(container, keyStr);
return container;
}

compare::KeyDiffMap requestDiff(const fdb5::Key& l, const fdb5::Key& r) {
compare::KeyDiffMap res;
for (const auto& [lk, lv] : l) {
auto [search, isValid] = r.find(lk);
if (isValid) {
if (search->second != lv) {
res.insert({lk, {lv, search->second}});
}
}
else {
res.insert({lk, {lv, {}}});
}
}

for (const auto& [rk, rv] : r) {
auto [search, isValid] = l.find(rk);
if (!isValid) {
res.insert({rk, {{}, rv}});
}
}
return res;
}

//---------------------------------------------------------------------------------------------------------------------


fdb5::Key applyKeyDiff(fdb5::Key k, const KeyDiffMap& diff, bool swapDiff) {
for (const auto& [field, val_pair] : diff) {
const auto& val = swapDiff ? val_pair.first : val_pair.second;
if (val) {
k.set(field, *val);
}
else {
// Delete
k.unset(field);
}
}
return k; // return modified copy
};

Scope parseScope(const std::string& s) {
if (s == "header-only") {
return Scope::HeaderOnly;
}
else if (s == "all") {
return Scope::All;
}
else if (s == "mars") {
return Scope::Mars;
}
throw eckit::UserError("Unknown comparison scope " + s, Here());
}
std::ostream& operator<<(std::ostream& os, const Scope& scope) {
switch (scope) {
case Scope::HeaderOnly:
os << "header-only";
break;
case Scope::All:
os << "all";
break;
case Scope::Mars:
os << "mars";
break;
}
return os;
}

Method parseMethod(const std::string& s) {
if (s == "bit-identical") {
return Method::BitIdentical;
}
else if (s == "hash-keys") {
return Method::Hash;
}
else {
// default ("grib-keys") or "mars"
return Method::KeyByKey;
}
}
std::ostream& operator<<(std::ostream& os, const Method& method) {
switch (method) {
case Method::BitIdentical:
os << "bit-identical";
break;
case Method::Hash:
os << "hash-keys";
break;
case Method::KeyByKey:
os << "grib-keys";
break;
}
return os;
}

//---------------------------------------------------------------------------------------------------------------------

std::ostream& operator<<(std::ostream& os, const NumericError& err) {
os << " Average: \t" << err.avg() << std::endl;
os << " Min: \t\t" << err.min << std::endl;
os << " Max: \t\t" << err.max << std::endl;
os << " Count: \t" << err.count << std::endl;
return os;
}

std::ostream& operator<<(std::ostream& os, const Result& res) {
os << (res.match ? "SUCCESS" : "FAILURE") << std::endl;
if (res.relativeError) {
os << std::endl;
os << "Relative Error:" << std::endl;
os << *res.relativeError << std::endl;
}
if (res.absoluteError) {
os << std::endl;
os << "Absolute Error:" << std::endl;
os << *res.absoluteError << std::endl;
}
return os;
}

//---------------------------------------------------------------------------------------------------------------------


double NumericError::avg() const {
return sum / count;
}

void NumericError::update(const NumericError& other) {
this->sum += other.sum;
this->min = std::min(this->min, other.min);
this->max = std::max(this->max, other.max);
this->count += other.count;
}

void NumericError::update(double val) {
this->sum += val;
this->min = std::min(this->min, val);
this->max = std::max(this->max, val);
this->count += 1;
}

void Result::update(const Result& other) {
if (this->relativeError) {
if (other.relativeError) {
this->relativeError->update(*other.relativeError);
}
}
else {
this->relativeError = other.relativeError;
}

if (this->absoluteError) {
if (other.absoluteError) {
this->absoluteError->update(*other.absoluteError);
}
}
else {
this->absoluteError = other.absoluteError;
}

this->match = this->match && other.match;
}

//---------------------------------------------------------------------------------------------------------------------


bool isSubset(const fdb5::Key& a, const fdb5::Key& b) {
for (const auto& kv : a) {
auto [it, isValid] = b.find(kv.first);
if (!isValid || it->second != kv.second)
return false;
}
return true;
}


DataIndex assembleCompareMap(fdb5::FDB& fdb, const fdb5::FDBToolRequest& req, const fdb5::Key& ignore) {
DataIndex out;

auto list = fdb.list(req);
fdb5::ListElement elem;

while (list.next(elem)) {
fdb5::Key km = elem.combinedKey();
if (ignore.empty() || !isSubset(ignore, km)) {
out.emplace(km, elem);
}
}
eckit::Log::info() << "[LOG] FDB request: " << req << " resulted in " << out.size() << " entries.\n";
return out;
}

//---------------------------------------------------------------------------------------------------------------------


} // namespace compare
Loading
Loading