Select Git revision
ParallelChecker.hpp
ParallelChecker.hpp 60.47 KiB
#ifndef PARALLEL_CHECKER_HPP
#define PARALLEL_CHECKER_HPP
#include <utils/pugs_config.hpp>
#ifdef PUGS_HAS_HDF5
#include <highfive/highfive.hpp>
#endif // PUGS_HAS_HDF5
#include <mesh/Connectivity.hpp>
#include <mesh/ItemArrayVariant.hpp>
#include <mesh/ItemValueVariant.hpp>
#include <mesh/SubItemArrayPerItemVariant.hpp>
#include <mesh/SubItemValuePerItemVariant.hpp>
#include <scheme/DiscreteFunctionVariant.hpp>
#include <utils/Demangle.hpp>
#include <utils/Filesystem.hpp>
#include <utils/Messenger.hpp>
#include <utils/SourceLocation.hpp>
#include <fstream>
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void parallel_check(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void parallel_check(const ItemArray<DataType, item_type, ConnectivityPtr>& item_array,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void parallel_check(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void parallel_check(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_array_per_item,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
class ParallelChecker
{
public:
enum class Mode
{
automatic, // write in sequential, read in parallel
read,
write
};
// to allow special manipulations in tests
friend class ParallelCheckerTester;
private:
static ParallelChecker* m_instance;
Mode m_mode = Mode::automatic;
size_t m_tag = 0;
std::string m_filename = "parallel_checker.h5";
std::string m_path = "";
ParallelChecker() = default;
public:
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
friend void parallel_check(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value,
const std::string& name,
const SourceLocation& source_location);
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
friend void parallel_check(const ItemArray<DataType, item_type, ConnectivityPtr>& item_array,
const std::string& name,
const SourceLocation& source_location);
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
friend void parallel_check(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location);
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
friend void parallel_check(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_array_per_item,
const std::string& name,
const SourceLocation& source_location);
#ifdef PUGS_HAS_HDF5
private:
template <typename T>
struct TinyVectorDataType;
template <size_t Dimension, typename DataT>
struct TinyVectorDataType<TinyVector<Dimension, DataT>> : public HighFive::DataType
{
TinyVectorDataType()
{
hsize_t dim[] = {Dimension};
auto h5_data_type = HighFive::create_datatype<DataT>();
_hid = H5Tarray_create(h5_data_type.getId(), 1, dim);
}
};
template <typename T>
struct TinyMatrixDataType;
template <size_t M, size_t N, typename DataT>
struct TinyMatrixDataType<TinyMatrix<M, N, DataT>> : public HighFive::DataType
{
TinyMatrixDataType()
{
hsize_t dim[] = {M, N};
auto h5_data_type = HighFive::create_datatype<DataT>();
_hid = H5Tarray_create(h5_data_type.getId(), 2, dim);
}
};
HighFive::File
_createOrOpenFileRW() const
{
if (m_tag == 0) {
createDirectoryIfNeeded(m_filename);
return HighFive::File{m_filename, HighFive::File::Truncate};
} else {
return HighFive::File{m_filename, HighFive::File::ReadWrite};
}
}
void
_printHeader(const std::string& name, const SourceLocation& source_location) const
{
std::cout << rang::fg::cyan << " - " << rang::fgB::cyan << "parallel checker" << rang::fg::cyan << " for \""
<< rang::fgB::magenta << name << rang::fg::cyan << "\" tag " << rang::fgB::blue << m_tag
<< rang::fg::reset << '\n';
std::cout << rang::fg::cyan << " | from " << rang::fgB::blue << source_location.filename() << rang::fg::reset << ':'
<< rang::style::bold << source_location.line() << rang::style::reset << '\n';
}
template <typename DataType>
void
_writeArray(HighFive::Group& group, const std::string& name, const Array<DataType>& array) const
{
using data_type = std::remove_const_t<DataType>;
if constexpr (is_tiny_vector_v<data_type>) {
auto dataset = group.createDataSet(name, HighFive::DataSpace{std::vector<size_t>{array.size()}},
TinyVectorDataType<data_type>{});
dataset.template write_raw<typename data_type::data_type>(&(array[0][0]), TinyVectorDataType<data_type>{});
} else if constexpr (is_tiny_matrix_v<data_type>) {
auto dataset = group.createDataSet(name, HighFive::DataSpace{std::vector<size_t>{array.size()}},
TinyMatrixDataType<data_type>{});
dataset.template write_raw<typename data_type::data_type>(&(array[0](0, 0)), TinyMatrixDataType<data_type>{});
} else {
auto dataset = group.createDataSet<data_type>(name, HighFive::DataSpace{std::vector<size_t>{array.size()}});
dataset.template write_raw<data_type>(&(array[0]));
}
}
template <typename DataType>
void
_writeTable(HighFive::Group& group, const std::string& name, const Table<DataType>& table) const
{
using data_type = std::remove_const_t<DataType>;
if constexpr (is_tiny_vector_v<data_type>) {
auto dataset =
group.createDataSet(name,
HighFive::DataSpace{std::vector<size_t>{table.numberOfRows(), table.numberOfColumns()}},
TinyVectorDataType<data_type>{});
dataset.template write_raw<typename data_type::data_type>(&(table(0, 0)[0]), TinyVectorDataType<data_type>{});
} else if constexpr (is_tiny_matrix_v<data_type>) {
auto dataset =
group.createDataSet(name,
HighFive::DataSpace{std::vector<size_t>{table.numberOfRows(), table.numberOfColumns()}},
TinyMatrixDataType<data_type>{});
dataset.template write_raw<typename data_type::data_type>(&(table(0, 0)(0, 0)), TinyMatrixDataType<data_type>{});
} else {
auto dataset =
group.createDataSet<data_type>(name, HighFive::DataSpace{
std::vector<size_t>{table.numberOfRows(), table.numberOfColumns()}});
dataset.template write_raw<data_type>(&(table(0, 0)));
}
}
template <typename DataType>
Array<std::remove_const_t<DataType>>
_readArray(HighFive::Group& group, const std::string& name) const
{
using data_type = std::remove_const_t<DataType>;
auto dataset = group.getDataSet(name);
Array<data_type> array(dataset.getElementCount());
if constexpr (is_tiny_vector_v<data_type>) {
dataset.read<data_type>(&(array[0]), TinyVectorDataType<data_type>{});
} else if constexpr (is_tiny_matrix_v<data_type>) {
dataset.read<data_type>(&(array[0]), TinyMatrixDataType<data_type>{});
} else {
dataset.read<data_type>(&(array[0]));
}
return array;
}
template <typename DataType>
Table<std::remove_const_t<DataType>>
_readTable(HighFive::Group& group, const std::string& name) const
{
using data_type = std::remove_const_t<DataType>;
auto dataset = group.getDataSet(name);
Table<data_type> table(dataset.getDimensions()[0], dataset.getDimensions()[1]);
if constexpr (is_tiny_vector_v<data_type>) {
dataset.read<data_type>(&(table(0, 0)), TinyVectorDataType<data_type>{});
} else if constexpr (is_tiny_matrix_v<data_type>) {
dataset.read<data_type>(&(table(0, 0)), TinyMatrixDataType<data_type>{});
} else {
dataset.read<data_type>(&(table(0, 0)));
}
return table;
}
size_t
_getConnectivityId(const std::shared_ptr<const IConnectivity>& i_connectivity) const
{
switch (i_connectivity->dimension()) {
case 1: {
return dynamic_cast<const Connectivity<1>&>(*i_connectivity).id();
}
case 2: {
return dynamic_cast<const Connectivity<2>&>(*i_connectivity).id();
}
case 3: {
return dynamic_cast<const Connectivity<3>&>(*i_connectivity).id();
}
// LCOV_EXCL_START
default: {
throw UnexpectedError("unexpected connectivity dimension");
}
// LCOV_EXCL_STOP
}
}
template <ItemType item_type>
Array<const int>
_getItemNumber(const std::shared_ptr<const IConnectivity>& i_connectivity) const
{
switch (i_connectivity->dimension()) {
case 1: {
const Connectivity<1>& connectivity = dynamic_cast<const Connectivity<1>&>(*i_connectivity);
return connectivity.number<item_type>().arrayView();
}
case 2: {
const Connectivity<2>& connectivity = dynamic_cast<const Connectivity<2>&>(*i_connectivity);
return connectivity.number<item_type>().arrayView();
}
case 3: {
const Connectivity<3>& connectivity = dynamic_cast<const Connectivity<3>&>(*i_connectivity);
return connectivity.number<item_type>().arrayView();
}
// LCOV_EXCL_START
default: {
throw UnexpectedError("unexpected connectivity dimension");
}
// LCOV_EXCL_STOP
}
}
template <ItemType item_type, ItemType sub_item_type>
Array<const typename ConnectivityMatrix::IndexType>
_getSubItemRowsMap(const std::shared_ptr<const IConnectivity>& i_connectivity)
{
switch (i_connectivity->dimension()) {
case 1: {
const Connectivity<1>& connectivity = dynamic_cast<const Connectivity<1>&>(*i_connectivity);
return connectivity.getMatrix(item_type, sub_item_type).rowsMap();
}
case 2: {
const Connectivity<2>& connectivity = dynamic_cast<const Connectivity<2>&>(*i_connectivity);
return connectivity.getMatrix(item_type, sub_item_type).rowsMap();
}
case 3: {
const Connectivity<3>& connectivity = dynamic_cast<const Connectivity<3>&>(*i_connectivity);
return connectivity.getMatrix(item_type, sub_item_type).rowsMap();
}
// LCOV_EXCL_START
default: {
throw UnexpectedError("unexpected connectivity dimension");
}
// LCOV_EXCL_STOP
}
}
template <ItemType item_type>
Array<const int>
_getItemOwner(const std::shared_ptr<const IConnectivity>& i_connectivity) const
{
switch (i_connectivity->dimension()) {
case 1: {
const Connectivity<1>& connectivity = dynamic_cast<const Connectivity<1>&>(*i_connectivity);
return connectivity.owner<item_type>().arrayView();
}
case 2: {
const Connectivity<2>& connectivity = dynamic_cast<const Connectivity<2>&>(*i_connectivity);
return connectivity.owner<item_type>().arrayView();
}
case 3: {
const Connectivity<3>& connectivity = dynamic_cast<const Connectivity<3>&>(*i_connectivity);
return connectivity.owner<item_type>().arrayView();
}
// LCOV_EXCL_START
default: {
throw UnexpectedError("unexpected connectivity dimension");
}
// LCOV_EXCL_STOP
}
}
template <ItemType item_type>
void
_checkGlobalNumberOfItems(const std::shared_ptr<const IConnectivity>& i_connectivity,
size_t reference_number_of_items)
{
Array<const bool> is_owned;
switch (i_connectivity->dimension()) {
case 1: {
const Connectivity<1>& connectivity = dynamic_cast<const Connectivity<1>&>(*i_connectivity);
is_owned = connectivity.isOwned<item_type>().arrayView();
break;
}
case 2: {
const Connectivity<2>& connectivity = dynamic_cast<const Connectivity<2>&>(*i_connectivity);
is_owned = connectivity.isOwned<item_type>().arrayView();
break;
}
case 3: {
const Connectivity<3>& connectivity = dynamic_cast<const Connectivity<3>&>(*i_connectivity);
is_owned = connectivity.isOwned<item_type>().arrayView();
break;
}
// LCOV_EXCL_START
default: {
throw UnexpectedError("unexpected connectivity dimension");
}
// LCOV_EXCL_STOP
}
size_t number_of_items = 0;
for (size_t i = 0; i < is_owned.size(); ++i) {
number_of_items += is_owned[i];
}
if (parallel::allReduceSum(number_of_items) != reference_number_of_items) {
throw NormalError("number of items differs from reference");
}
}
template <ItemType item_type>
void
_writeItemNumbers(const std::shared_ptr<const IConnectivity> i_connectivity,
HighFive::File file,
HighFive::Group group) const
{
std::string item_number_path = "/connectivities/" + std::to_string(this->_getConnectivityId(i_connectivity)) + '/' +
std::string{itemName(item_type)};
if (not file.exist(item_number_path)) {
HighFive::Group item_number_group = file.createGroup(item_number_path);
this->_writeArray(item_number_group, "numbers", this->_getItemNumber<item_type>(i_connectivity));
}
HighFive::DataSet item_numbers = file.getDataSet(item_number_path + "/numbers");
group.createHardLink("numbers", item_numbers);
}
template <typename ItemOfItem>
void
_writeSubItemRowsMap(const std::shared_ptr<const IConnectivity> i_connectivity,
HighFive::File file,
HighFive::Group group)
{
constexpr ItemType item_type = ItemOfItem::item_type;
constexpr ItemType sub_item_type = ItemOfItem::sub_item_type;
std::string subitem_of_item_row_map_path =
"/connectivities/" + std::to_string(this->_getConnectivityId(i_connectivity)) + '/' +
std::string{itemName(sub_item_type)} + "of" + std::string{itemName(item_type)};
if (not file.exist(subitem_of_item_row_map_path)) {
HighFive::Group subitem_of_item_row_map_group = file.createGroup(subitem_of_item_row_map_path);
this->_writeArray(subitem_of_item_row_map_group, "rows_map",
this->_getSubItemRowsMap<item_type, sub_item_type>(i_connectivity));
}
HighFive::DataSet subitem_of_item_row_map = file.getDataSet(subitem_of_item_row_map_path + "/rows_map");
group.createHardLink("rows_map", subitem_of_item_row_map);
}
template <typename DataType, ItemType item_type>
bool
_checkIsComparable(const std::string& name,
const SourceLocation& source_location,
const std::vector<size_t> data_shape,
const std::shared_ptr<const IConnectivity>& i_connectivity,
HighFive::Group group) const
{
const std::string reference_name = group.getAttribute("name").read<std::string>();
const std::string reference_file_name = group.getAttribute("filename").read<std::string>();
const std::string reference_function_name = group.getAttribute("function").read<std::string>();
const size_t reference_line_number = group.getAttribute("line").read<size_t>();
const size_t reference_dimension = group.getAttribute("dimension").read<size_t>();
const std::string reference_item_type = group.getAttribute("item_type").read<std::string>();
const std::string reference_data_type = group.getAttribute("data_type").read<std::string>();
bool is_comparable = true;
if (i_connectivity->dimension() != reference_dimension) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different support dimensions: reference ("
<< rang::fgB::yellow << reference_dimension << rang::fgB::red << ") / target (" << rang::fgB::yellow
<< i_connectivity->dimension() << rang::fg::reset << ")\n";
is_comparable = false;
}
if (itemName(item_type) != reference_item_type) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different item types: reference (" << rang::fgB::yellow
<< reference_item_type << rang::fgB::red << ") / target (" << rang::fgB::yellow << itemName(item_type)
<< rang::fg::reset << ")\n";
is_comparable = false;
}
if (demangle<DataType>() != reference_data_type) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different data types: reference (" << rang::fgB::yellow
<< reference_data_type << rang::fgB::red << ") / target (" << rang::fgB::yellow << demangle<DataType>()
<< rang::fg::reset << ")\n";
is_comparable = false;
}
std::vector reference_data_shape = group.getDataSet(reference_name).getSpace().getDimensions();
if (reference_data_shape.size() != data_shape.size()) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different data shape kind: reference ("
<< rang::fgB::yellow << reference_data_shape.size() << "d array" << rang::fgB::red << ") / target ("
<< rang::fgB::yellow << data_shape.size() << "d array" << rang::fg::reset << ")\n";
is_comparable = false;
}
{
bool same_shapes = (reference_data_shape.size() == data_shape.size());
if (same_shapes) {
for (size_t i = 1; i < reference_data_shape.size(); ++i) {
same_shapes &= (reference_data_shape[i] == data_shape[i]);
}
}
if (not same_shapes) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different data shape: reference ("
<< rang::fgB::yellow << "*";
for (size_t i = 1; i < reference_data_shape.size(); ++i) {
std::cout << ":" << reference_data_shape[i];
}
std::cout << rang::fgB::red << ") / target (" << rang::fgB::yellow << "*";
for (size_t i = 1; i < data_shape.size(); ++i) {
std::cout << ":" << data_shape[i];
}
std::cout << rang::fg::reset << ")\n";
is_comparable = false;
}
}
if (name != reference_name) {
// Just warn for different labels (maybe useful for some kind of
// debugging...)
std::cout << rang::fg::cyan << " | " << rang::fgB::magenta << "different names: reference (" << rang::fgB::yellow
<< rang::style::reversed << reference_name << rang::style::reset << rang::fgB::magenta << ") / target ("
<< rang::fgB::yellow << rang::style::reversed << name << rang::style::reset << rang::fg::reset << ")\n";
std::cout << rang::fg::cyan << " | " << rang::fgB::magenta << "reference from " << rang::fgB::blue
<< reference_file_name << rang::fg::reset << ':' << rang::style::bold << reference_line_number
<< rang::style::reset << '\n';
if ((reference_function_name.size() > 0) or (source_location.function().size() > 0)) {
std::cout << rang::fg::cyan << " | " << rang::fgB::magenta << "reference function " << rang::fgB::blue
<< reference_function_name << rang::fg::reset << '\n';
std::cout << rang::fg::cyan << " | " << rang::fgB::magenta << "target function " << rang::fgB::blue
<< source_location.function() << rang::fg::reset << '\n';
}
}
return is_comparable;
}
template <ItemType sub_item_type>
bool
_checkIsComparable(HighFive::Group group) const
{
const std::string reference_sub_item_type = group.getAttribute("subitem_type").read<std::string>();
bool is_comparable = true;
if (itemName(sub_item_type) != reference_sub_item_type) {
std::cout << rang::fg::cyan << " | " << rang::fgB::red << "different sub_item types: reference ("
<< rang::fgB::yellow << reference_sub_item_type << rang::fgB::red << ") / target (" << rang::fgB::yellow
<< itemName(sub_item_type) << rang::fg::reset << ")\n";
is_comparable = false;
}
return is_comparable;
}
template <typename DataType, ItemType item_type>
void
_throwIfNotComparable(const std::string& name,
const SourceLocation& source_location,
const std::vector<size_t> data_shape,
const std::shared_ptr<const IConnectivity>& i_connectivity,
HighFive::Group group) const
{
bool is_comparable =
this->_checkIsComparable<DataType, item_type>(name, source_location, data_shape, i_connectivity, group);
if (not parallel::allReduceAnd(is_comparable)) {
throw NormalError("cannot compare data");
}
}
template <typename DataType, typename ItemOfItem>
void
_throwIfNotComparable(const std::string& name,
const SourceLocation& source_location,
const std::vector<size_t> data_shape,
const std::shared_ptr<const IConnectivity>& i_connectivity,
HighFive::Group group) const
{
bool is_comparable = this->_checkIsComparable<DataType, ItemOfItem::item_type>(name, source_location, data_shape,
i_connectivity, group) //
and this->_checkIsComparable<ItemOfItem::sub_item_type>(group);
if (not parallel::allReduceAnd(is_comparable)) {
throw NormalError("cannot compare data");
}
}
private:
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
write(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file = this->_createOrOpenFileRW();
auto group = file.createGroup("values/" + std::to_string(m_tag));
group.createAttribute("filename", std::string{source_location.filename()});
group.createAttribute("function", std::string{source_location.function()});
group.createAttribute("line", static_cast<size_t>(source_location.line()));
group.createAttribute("name", name);
std::shared_ptr<const IConnectivity> i_connectivity = item_value.connectivity_ptr();
group.createAttribute("dimension", static_cast<size_t>(i_connectivity->dimension()));
group.createAttribute("item_type", std::string{itemName(item_type)});
group.createAttribute("data_type", demangle<DataType>());
this->_writeArray(group, name, item_value.arrayView());
this->_writeItemNumbers<item_type>(i_connectivity, file, group);
++m_tag;
std::cout << rang::fg::cyan << " | writing " << rang::fgB::green << "success" << rang::fg::reset << '\n';
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
write(const ItemArray<DataType, item_type, ConnectivityPtr>& item_array,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file = this->_createOrOpenFileRW();
auto group = file.createGroup("values/" + std::to_string(m_tag));
group.createAttribute("filename", std::string{source_location.filename()});
group.createAttribute("function", std::string{source_location.function()});
group.createAttribute("line", static_cast<size_t>(source_location.line()));
group.createAttribute("name", name);
std::shared_ptr<const IConnectivity> i_connectivity = item_array.connectivity_ptr();
group.createAttribute("dimension", static_cast<size_t>(i_connectivity->dimension()));
group.createAttribute("item_type", std::string{itemName(item_type)});
group.createAttribute("data_type", demangle<DataType>());
this->_writeTable(group, name, item_array.tableView());
this->_writeItemNumbers<item_type>(i_connectivity, file, group);
++m_tag;
std::cout << rang::fg::cyan << " | writing " << rang::fgB::green << "success" << rang::fg::reset << '\n';
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
write(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location)
{
constexpr ItemType item_type = ItemOfItem::item_type;
constexpr ItemType sub_item_type = ItemOfItem::sub_item_type;
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file = this->_createOrOpenFileRW();
auto group = file.createGroup("values/" + std::to_string(m_tag));
group.createAttribute("filename", std::string{source_location.filename()});
group.createAttribute("function", std::string{source_location.function()});
group.createAttribute("line", static_cast<size_t>(source_location.line()));
group.createAttribute("name", name);
std::shared_ptr<const IConnectivity> i_connectivity = subitem_value_per_item.connectivity_ptr();
group.createAttribute("dimension", static_cast<size_t>(i_connectivity->dimension()));
group.createAttribute("item_type", std::string{itemName(item_type)});
group.createAttribute("subitem_type", std::string{itemName(sub_item_type)});
group.createAttribute("data_type", demangle<DataType>());
this->_writeArray(group, name, subitem_value_per_item.arrayView());
this->_writeItemNumbers<item_type>(i_connectivity, file, group);
this->_writeSubItemRowsMap<ItemOfItem>(i_connectivity, file, group);
++m_tag;
std::cout << rang::fg::cyan << " | writing " << rang::fgB::green << "success" << rang::fg::reset << '\n';
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
write(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location)
{
constexpr ItemType item_type = ItemOfItem::item_type;
constexpr ItemType sub_item_type = ItemOfItem::sub_item_type;
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file = this->_createOrOpenFileRW();
auto group = file.createGroup("values/" + std::to_string(m_tag));
group.createAttribute("filename", std::string{source_location.filename()});
group.createAttribute("function", std::string{source_location.function()});
group.createAttribute("line", static_cast<size_t>(source_location.line()));
group.createAttribute("name", name);
std::shared_ptr<const IConnectivity> i_connectivity = subitem_value_per_item.connectivity_ptr();
group.createAttribute("dimension", static_cast<size_t>(i_connectivity->dimension()));
group.createAttribute("item_type", std::string{itemName(item_type)});
group.createAttribute("subitem_type", std::string{itemName(sub_item_type)});
group.createAttribute("data_type", demangle<DataType>());
this->_writeTable(group, name, subitem_value_per_item.tableView());
this->_writeItemNumbers<item_type>(i_connectivity, file, group);
this->_writeSubItemRowsMap<ItemOfItem>(i_connectivity, file, group);
++m_tag;
std::cout << rang::fg::cyan << " | writing " << rang::fgB::green << "success" << rang::fg::reset << '\n';
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
compare(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file{m_filename, HighFive::File::ReadOnly};
auto group = file.getGroup("values/" + std::to_string(m_tag));
const std::string reference_name = group.getAttribute("name").read<std::string>();
std::shared_ptr<const IConnectivity> i_connectivity = item_value.connectivity_ptr();
this->_throwIfNotComparable<DataType, item_type>(name, source_location,
std::vector<size_t>{item_value.numberOfItems()}, i_connectivity,
group);
Array<const int> reference_item_numbers = this->_readArray<int>(group, "numbers");
Array<const DataType> reference_item_value = this->_readArray<DataType>(group, reference_name);
Array<const int> item_numbers = this->_getItemNumber<item_type>(i_connectivity);
using ItemId = ItemIdT<item_type>;
std::unordered_map<int, ItemId> item_number_to_item_id_map;
for (ItemId item_id = 0; item_id < item_numbers.size(); ++item_id) {
const auto& [iterator, success] =
item_number_to_item_id_map.insert(std::make_pair(item_numbers[item_id], item_id));
// LCOV_EXCL_START
if (not success) {
throw UnexpectedError("item numbers have duplicate values");
}
// LCOV_EXCL_STOP
}
Assert(item_number_to_item_id_map.size() == item_numbers.size());
Array<int> index_in_reference(item_numbers.size());
index_in_reference.fill(-1);
for (size_t i = 0; i < reference_item_numbers.size(); ++i) {
const auto& i_number_to_item_id = item_number_to_item_id_map.find(reference_item_numbers[i]);
if (i_number_to_item_id != item_number_to_item_id_map.end()) {
index_in_reference[i_number_to_item_id->second] = i;
}
}
if (parallel::allReduceMin(min(index_in_reference)) < 0) {
throw NormalError("some item numbers are not defined in reference");
}
this->_checkGlobalNumberOfItems<item_type>(i_connectivity, reference_item_numbers.size());
Array<const int> owner = this->_getItemOwner<item_type>(i_connectivity);
bool has_own_differences = false;
bool is_same = true;
for (ItemId item_id = 0; item_id < item_value.numberOfItems(); ++item_id) {
if (reference_item_value[index_in_reference[item_id]] != item_value[item_id]) {
is_same = false;
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
is_same = parallel::allReduceAnd(is_same);
has_own_differences = parallel::allReduceOr(has_own_differences);
if (is_same) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::green << "success" << rang::fg::reset << '\n';
} else {
if (has_own_differences) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::red << "failed!" << rang::fg::reset;
} else {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::yellow << "not synchronized" << rang::fg::reset;
}
std::cout << rang::fg::cyan << " [see \"" << rang::fgB::blue << m_path + "parallel_differences_" << m_tag
<< "_*" << rang::fg::cyan << "\" files for details]" << rang::fg::reset << '\n';
{
std::ofstream fout(std::string{m_path + "parallel_differences_"} + stringify(m_tag) + std::string{"_"} +
stringify(parallel::rank()));
fout.precision(15);
for (ItemId item_id = 0; item_id < item_value.numberOfItems(); ++item_id) {
if (reference_item_value[index_in_reference[item_id]] != item_value[item_id]) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " number=" << item_numbers[item_id]
<< " reference=" << reference_item_value[index_in_reference[item_id]]
<< " target=" << item_value[item_id]
<< " difference=" << reference_item_value[index_in_reference[item_id]] - item_value[item_id] << '\n';
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
}
if (parallel::allReduceAnd(has_own_differences)) {
throw NormalError("calculations differ!");
}
}
++m_tag;
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
compare(const ItemArray<DataType, item_type, ConnectivityPtr>& item_array,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file{m_filename, HighFive::File::ReadOnly};
auto group = file.getGroup("values/" + std::to_string(m_tag));
const std::string reference_name = group.getAttribute("name").read<std::string>();
std::shared_ptr<const IConnectivity> i_connectivity = item_array.connectivity_ptr();
this->_throwIfNotComparable<DataType, item_type>(name, source_location,
std::vector<size_t>{item_array.numberOfItems(),
item_array.sizeOfArrays()},
i_connectivity, group);
Array<const int> reference_item_numbers = this->_readArray<int>(group, "numbers");
Table<const DataType> reference_item_array = this->_readTable<DataType>(group, reference_name);
Array<const int> item_numbers = this->_getItemNumber<item_type>(i_connectivity);
using ItemId = ItemIdT<item_type>;
std::unordered_map<int, ItemId> item_number_to_item_id_map;
for (ItemId item_id = 0; item_id < item_numbers.size(); ++item_id) {
const auto& [iterator, success] =
item_number_to_item_id_map.insert(std::make_pair(item_numbers[item_id], item_id));
// LCOV_EXCL_START
if (not success) {
throw UnexpectedError("item numbers have duplicate values");
}
// LCOV_EXCL_STOP
}
Assert(item_number_to_item_id_map.size() == item_numbers.size());
Array<int> index_in_reference(item_numbers.size());
index_in_reference.fill(-1);
for (size_t i = 0; i < reference_item_numbers.size(); ++i) {
const auto& i_number_to_item_id = item_number_to_item_id_map.find(reference_item_numbers[i]);
if (i_number_to_item_id != item_number_to_item_id_map.end()) {
index_in_reference[i_number_to_item_id->second] = i;
}
}
if (parallel::allReduceMin(min(index_in_reference)) < 0) {
throw NormalError("some item numbers are not defined in reference");
}
this->_checkGlobalNumberOfItems<item_type>(i_connectivity, reference_item_numbers.size());
Array<const int> owner = this->_getItemOwner<item_type>(i_connectivity);
bool has_own_differences = false;
bool is_same = true;
for (ItemId item_id = 0; item_id < item_array.numberOfItems(); ++item_id) {
for (size_t i = 0; i < reference_item_array.numberOfColumns(); ++i) {
if (reference_item_array[index_in_reference[item_id]][i] != item_array[item_id][i]) {
is_same = false;
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
}
is_same = parallel::allReduceAnd(is_same);
has_own_differences = parallel::allReduceOr(has_own_differences);
if (is_same) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::green << "success" << rang::fg::reset << '\n';
} else {
if (has_own_differences) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::red << "failed!" << rang::fg::reset;
} else {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::yellow << "not synchronized" << rang::fg::reset;
}
std::cout << rang::fg::cyan << " [see \"" << rang::fgB::blue << "parallel_differences_" << m_tag << "_*"
<< rang::fg::cyan << "\" files for details]" << rang::fg::reset << '\n';
{
std::ofstream fout(std::string{m_path + "parallel_differences_"} + stringify(m_tag) + std::string{"_"} +
stringify(parallel::rank()));
fout.precision(15);
for (ItemId item_id = 0; item_id < item_array.numberOfItems(); ++item_id) {
for (size_t i = 0; i < item_array.sizeOfArrays(); ++i) {
if (reference_item_array[index_in_reference[item_id]][i] != item_array[item_id][i]) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " column=" << i << " number=" << item_numbers[item_id]
<< " reference=" << reference_item_array[index_in_reference[item_id]][i]
<< " target=" << item_array[item_id][i]
<< " difference=" << reference_item_array[index_in_reference[item_id]][i] - item_array[item_id][i]
<< '\n';
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
}
}
if (parallel::allReduceAnd(has_own_differences)) {
throw NormalError("calculations differ!");
}
}
++m_tag;
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
compare(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file{m_filename, HighFive::File::ReadOnly};
auto group = file.getGroup("values/" + std::to_string(m_tag));
const std::string reference_name = group.getAttribute("name").read<std::string>();
std::shared_ptr<const IConnectivity> i_connectivity = subitem_value_per_item.connectivity_ptr();
this->_throwIfNotComparable<DataType, ItemOfItem>(name, source_location,
std::vector<size_t>{subitem_value_per_item.numberOfItems()},
i_connectivity, group);
constexpr ItemType item_type = ItemOfItem::item_type;
constexpr ItemType sub_item_type = ItemOfItem::sub_item_type;
using IndexType = typename ConnectivityMatrix::IndexType;
Array<const int> reference_item_numbers = this->_readArray<int>(group, "numbers");
Array<const IndexType> reference_subitem_rows_map = this->_readArray<IndexType>(group, "rows_map");
Array<const DataType> reference_subitem_value_per_item = this->_readArray<DataType>(group, reference_name);
Array<const int> item_numbers = this->_getItemNumber<item_type>(i_connectivity);
Array<const IndexType> sub_item_row_map = this->_getSubItemRowsMap<item_type, sub_item_type>(i_connectivity);
using ItemId = ItemIdT<item_type>;
std::unordered_map<int, ItemId> item_number_to_item_id_map;
for (ItemId item_id = 0; item_id < item_numbers.size(); ++item_id) {
const auto& [iterator, success] =
item_number_to_item_id_map.insert(std::make_pair(item_numbers[item_id], item_id));
// LCOV_EXCL_START
if (not success) {
throw UnexpectedError("item numbers have duplicate values");
}
// LCOV_EXCL_STOP
}
Assert(item_number_to_item_id_map.size() == item_numbers.size());
Array<int> item_index_in_reference(item_numbers.size());
item_index_in_reference.fill(-1);
for (size_t i = 0; i < reference_item_numbers.size(); ++i) {
const auto& i_number_to_item_id = item_number_to_item_id_map.find(reference_item_numbers[i]);
if (i_number_to_item_id != item_number_to_item_id_map.end()) {
item_index_in_reference[i_number_to_item_id->second] = i;
}
}
if (parallel::allReduceMin(min(item_index_in_reference)) < 0) {
throw NormalError("some item numbers are not defined in reference");
}
this->_checkGlobalNumberOfItems<item_type>(i_connectivity, reference_item_numbers.size());
Array<const int> owner = this->_getItemOwner<item_type>(i_connectivity);
bool has_own_differences = false;
bool is_same = true;
for (ItemId item_id = 0; item_id < subitem_value_per_item.numberOfItems(); ++item_id) {
const size_t reference_item_index = item_index_in_reference[item_id];
const size_t index_begin_in_reference = reference_subitem_rows_map[reference_item_index];
const size_t index_end_in_reference = reference_subitem_rows_map[reference_item_index + 1];
bool item_is_same = true;
if ((index_end_in_reference - index_begin_in_reference) != subitem_value_per_item[item_id].size()) {
item_is_same = false;
} else {
for (size_t i_sub_item = 0; i_sub_item < subitem_value_per_item[item_id].size(); ++i_sub_item) {
if (reference_subitem_value_per_item[index_begin_in_reference + i_sub_item] !=
subitem_value_per_item[item_id][i_sub_item]) {
item_is_same = false;
}
}
}
if (not item_is_same) {
is_same = false;
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
is_same = parallel::allReduceAnd(is_same);
has_own_differences = parallel::allReduceOr(has_own_differences);
if (is_same) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::green << "success" << rang::fg::reset << '\n';
} else {
if (has_own_differences) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::red << "failed!" << rang::fg::reset;
} else {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::yellow << "not synchronized" << rang::fg::reset;
}
std::cout << rang::fg::cyan << " [see \"" << rang::fgB::blue << m_path + "parallel_differences_" << m_tag
<< "_*" << rang::fg::cyan << "\" files for details]" << rang::fg::reset << '\n';
{
std::ofstream fout(std::string{m_path + "parallel_differences_"} + stringify(m_tag) + std::string{"_"} +
stringify(parallel::rank()));
fout.precision(15);
for (ItemId item_id = 0; item_id < subitem_value_per_item.numberOfItems(); ++item_id) {
const size_t reference_item_index = item_index_in_reference[item_id];
const size_t index_begin_in_reference = reference_subitem_rows_map[reference_item_index];
const size_t index_end_in_reference = reference_subitem_rows_map[reference_item_index + 1];
if ((index_end_in_reference - index_begin_in_reference) != subitem_value_per_item[item_id].size()) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " number=" << item_numbers[item_id]
<< " reference[subitems number]=" << index_end_in_reference - index_begin_in_reference
<< " target[subitems number]=" << subitem_value_per_item[item_id].size() << '\n';
} else {
for (size_t i_sub_item = 0; i_sub_item < subitem_value_per_item[item_id].size(); ++i_sub_item) {
if (reference_subitem_value_per_item[index_begin_in_reference + i_sub_item] !=
subitem_value_per_item[item_id][i_sub_item]) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " number=" << item_numbers[item_id] << " i_subitem=" << i_sub_item
<< " reference=" << reference_subitem_value_per_item[index_begin_in_reference + i_sub_item]
<< " target=" << subitem_value_per_item[item_id][i_sub_item] << " difference="
<< reference_subitem_value_per_item[index_begin_in_reference + i_sub_item] -
subitem_value_per_item[item_id][i_sub_item]
<< '\n';
}
}
}
}
}
if (has_own_differences) {
throw NormalError("calculations differ!");
}
}
++m_tag;
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
compare(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_array_per_item,
const std::string& name,
const SourceLocation& source_location)
{
HighFive::SilenceHDF5 m_silence_hdf5{true};
this->_printHeader(name, source_location);
try {
HighFive::File file{m_filename, HighFive::File::ReadOnly};
auto group = file.getGroup("values/" + std::to_string(m_tag));
const std::string reference_name = group.getAttribute("name").read<std::string>();
std::shared_ptr<const IConnectivity> i_connectivity = subitem_array_per_item.connectivity_ptr();
this->_throwIfNotComparable<DataType, ItemOfItem>(name, source_location,
std::vector<size_t>{subitem_array_per_item.numberOfItems(),
subitem_array_per_item.sizeOfArrays()},
i_connectivity, group);
constexpr ItemType item_type = ItemOfItem::item_type;
constexpr ItemType sub_item_type = ItemOfItem::sub_item_type;
using IndexType = typename ConnectivityMatrix::IndexType;
Array<const int> reference_item_numbers = this->_readArray<int>(group, "numbers");
Array<const IndexType> reference_subitem_rows_map = this->_readArray<IndexType>(group, "rows_map");
Table<const DataType> reference_subitem_array_per_item = this->_readTable<DataType>(group, reference_name);
Array<const int> item_numbers = this->_getItemNumber<item_type>(i_connectivity);
Array<const IndexType> sub_item_row_map = this->_getSubItemRowsMap<item_type, sub_item_type>(i_connectivity);
using ItemId = ItemIdT<item_type>;
std::unordered_map<int, ItemId> item_number_to_item_id_map;
for (ItemId item_id = 0; item_id < item_numbers.size(); ++item_id) {
const auto& [iterator, success] =
item_number_to_item_id_map.insert(std::make_pair(item_numbers[item_id], item_id));
// LCOV_EXCL_START
if (not success) {
throw UnexpectedError("item numbers have duplicate values");
}
// LCOV_EXCL_STOP
}
Assert(item_number_to_item_id_map.size() == item_numbers.size());
Array<int> item_index_in_reference(item_numbers.size());
item_index_in_reference.fill(-1);
for (size_t i = 0; i < reference_item_numbers.size(); ++i) {
const auto& i_number_to_item_id = item_number_to_item_id_map.find(reference_item_numbers[i]);
if (i_number_to_item_id != item_number_to_item_id_map.end()) {
item_index_in_reference[i_number_to_item_id->second] = i;
}
}
if (parallel::allReduceMin(min(item_index_in_reference)) < 0) {
throw NormalError("some item numbers are not defined in reference");
}
this->_checkGlobalNumberOfItems<item_type>(i_connectivity, reference_item_numbers.size());
Array<const int> owner = this->_getItemOwner<item_type>(i_connectivity);
bool has_own_differences = false;
bool is_same = true;
for (ItemId item_id = 0; item_id < subitem_array_per_item.numberOfItems(); ++item_id) {
const size_t reference_item_index = item_index_in_reference[item_id];
const size_t index_begin_in_reference = reference_subitem_rows_map[reference_item_index];
const size_t index_end_in_reference = reference_subitem_rows_map[reference_item_index + 1];
bool item_is_same = true;
if ((index_end_in_reference - index_begin_in_reference) != subitem_array_per_item[item_id].numberOfRows()) {
item_is_same = false;
} else {
for (size_t i_sub_item = 0; i_sub_item < subitem_array_per_item[item_id].numberOfRows(); ++i_sub_item) {
for (size_t i = 0; i < subitem_array_per_item.sizeOfArrays(); ++i) {
if (reference_subitem_array_per_item[index_begin_in_reference + i_sub_item][i] !=
subitem_array_per_item[item_id][i_sub_item][i]) {
item_is_same = false;
}
}
}
}
if (not item_is_same) {
is_same = false;
if (static_cast<size_t>(owner[item_id]) == parallel::rank()) {
has_own_differences = true;
}
}
}
is_same = parallel::allReduceAnd(is_same);
has_own_differences = parallel::allReduceOr(has_own_differences);
if (is_same) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::green << "success" << rang::fg::reset << '\n';
} else {
if (has_own_differences) {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::red << "failed!" << rang::fg::reset;
} else {
std::cout << rang::fg::cyan << " | compare: " << rang::fgB::yellow << "not synchronized" << rang::fg::reset;
}
std::cout << rang::fg::cyan << " [see \"" << rang::fgB::blue << m_path + "parallel_differences_" << m_tag
<< "_*" << rang::fg::cyan << "\" files for details]" << rang::fg::reset << '\n';
{
std::ofstream fout(std::string{m_path + "parallel_differences_"} + stringify(m_tag) + std::string{"_"} +
stringify(parallel::rank()));
fout.precision(15);
for (ItemId item_id = 0; item_id < subitem_array_per_item.numberOfItems(); ++item_id) {
const size_t reference_item_index = item_index_in_reference[item_id];
const size_t index_begin_in_reference = reference_subitem_rows_map[reference_item_index];
const size_t index_end_in_reference = reference_subitem_rows_map[reference_item_index + 1];
if ((index_end_in_reference - index_begin_in_reference) != subitem_array_per_item[item_id].numberOfRows()) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " number=" << item_numbers[item_id]
<< " reference[subitems number]=" << index_end_in_reference - index_begin_in_reference
<< " target[subitems number]=" << subitem_array_per_item[item_id].numberOfRows() << '\n';
} else {
for (size_t i_sub_item = 0; i_sub_item < subitem_array_per_item[item_id].numberOfRows(); ++i_sub_item) {
for (size_t i = 0; i < subitem_array_per_item.sizeOfArrays(); ++i) {
if (reference_subitem_array_per_item[index_begin_in_reference + i_sub_item][i] !=
subitem_array_per_item[item_id][i_sub_item][i]) {
const bool is_own_difference = (parallel::rank() == static_cast<size_t>(owner[item_id]));
if (is_own_difference) {
fout << rang::fgB::red << "[ own ]" << rang::fg::reset;
} else {
fout << rang::fgB::yellow << "[ghost]" << rang::fg::reset;
}
fout << " rank=" << parallel::rank() << " owner=" << owner[item_id] << " item_id=" << item_id
<< " number=" << item_numbers[item_id] << " i_subitem=" << i_sub_item << " i_value=" << i
<< " reference=" << reference_subitem_array_per_item[index_begin_in_reference + i_sub_item]
<< " target=" << subitem_array_per_item[item_id][i_sub_item] << " difference="
<< reference_subitem_array_per_item[index_begin_in_reference + i_sub_item][i] -
subitem_array_per_item[item_id][i_sub_item][i]
<< '\n';
}
}
}
}
}
}
if (has_own_differences) {
throw NormalError("calculations differ!");
}
}
++m_tag;
}
// LCOV_EXCL_START
catch (HighFive::Exception& e) {
throw NormalError(e.what());
}
// LCOV_EXCL_STOP
}
#else // PUGS_HAS_HDF5
template <typename T>
void
write(const T&, const std::string&, const SourceLocation&)
{
throw NormalError("parallel checker cannot be used without HDF5 support");
}
template <typename T>
void
compare(const T&, const std::string&, const SourceLocation&)
{
throw NormalError("parallel checker cannot be used without HDF5 support");
}
#endif // PUGS_HAS_HDF5
public:
static void create();
static void destroy();
static ParallelChecker&
instance()
{
Assert(m_instance != nullptr, "ParallelChecker was not created");
return *m_instance;
}
Mode
mode() const
{
return m_mode;
}
void
setMode(const Mode& mode)
{
if (m_tag != 0) {
throw UnexpectedError("Cannot modify parallel checker mode if it was already used");
}
// LCOV_EXCL_START
if ((mode == Mode::write) and (parallel::size() > 1)) {
throw NotImplementedError("parallel check write in parallel");
}
// LCOV_EXCL_STOP
m_mode = mode;
}
const std::string&
filename() const
{
return m_filename;
}
void
setFilename(const std::string& filename)
{
if (m_tag != 0) {
throw UnexpectedError("Cannot modify parallel checker file if it was already used");
}
m_filename = filename;
m_path = std::filesystem::path(filename).remove_filename();
}
bool
isWriting() const
{
bool is_writting = false;
switch (m_mode) {
case Mode::automatic: {
is_writting = (parallel::size() == 1);
break;
}
case Mode::write: {
is_writting = true;
break;
}
case Mode::read: {
is_writting = false;
break;
}
}
return is_writting;
}
};
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
parallel_check(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value,
const std::string& name,
const SourceLocation& source_location)
{
if (ParallelChecker::instance().isWriting()) {
ParallelChecker::instance().write(item_value, name, source_location);
} else {
ParallelChecker::instance().compare(item_value, name, source_location);
}
}
template <typename DataType, ItemType item_type, typename ConnectivityPtr>
void
parallel_check(const ItemArray<DataType, item_type, ConnectivityPtr>& item_array,
const std::string& name,
const SourceLocation& source_location)
{
if (ParallelChecker::instance().isWriting()) {
ParallelChecker::instance().write(item_array, name, source_location);
} else {
ParallelChecker::instance().compare(item_array, name, source_location);
}
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
parallel_check(const SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_value_per_item,
const std::string& name,
const SourceLocation& source_location)
{
if (ParallelChecker::instance().isWriting()) {
ParallelChecker::instance().write(subitem_value_per_item, name, source_location);
} else {
ParallelChecker::instance().compare(subitem_value_per_item, name, source_location);
}
}
template <typename DataType, typename ItemOfItem, typename ConnectivityPtr>
void
parallel_check(const SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& subitem_array_per_item,
const std::string& name,
const SourceLocation& source_location)
{
if (ParallelChecker::instance().isWriting()) {
ParallelChecker::instance().write(subitem_array_per_item, name, source_location);
} else {
ParallelChecker::instance().compare(subitem_array_per_item, name, source_location);
}
}
template <size_t Dimension, typename DataType>
void PUGS_INLINE
parallel_check(const DiscreteFunctionP0<Dimension, DataType>& discrete_function,
const std::string& name,
const SourceLocation& source_location = SourceLocation{})
{
parallel_check(discrete_function.cellValues(), name, source_location);
}
template <size_t Dimension, typename DataType>
void PUGS_INLINE
parallel_check(const DiscreteFunctionP0Vector<Dimension, DataType>& discrete_function,
const std::string& name,
const SourceLocation& source_location = SourceLocation{})
{
parallel_check(discrete_function.cellArrays(), name, source_location);
}
void parallel_check(const ItemValueVariant& item_value_variant,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
void parallel_check(const ItemArrayVariant& item_array_variant,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
void parallel_check(const SubItemValuePerItemVariant& subitem_value_per_item_variant,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
void parallel_check(const SubItemArrayPerItemVariant& subitem_array_per_item_variant,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
void parallel_check(const DiscreteFunctionVariant& discrete_function_variant,
const std::string& name,
const SourceLocation& source_location = SourceLocation{});
#endif // PARALLEL_CHECKER_HPP