NodePartitionedMeshReader.cpp
/*!
\file
\author Wenqing Wang
\date 2014.08
\brief Define members of class NodePartitionedMeshReader to read node-wise
partitioned mesh with MPI functions.
\copyright
Copyright (c) 2012-2023, OpenGeoSys Community (http://www.opengeosys.org)
Distributed under a Modified BSD License.
See accompanying file LICENSE.txt or
http://www.opengeosys.org/project/license
*/
#include "NodePartitionedMeshReader.h"
#include <mpi.h>
#include <fstream>
#include <numeric>
#include "BaseLib/FileTools.h"
#include "BaseLib/Logging.h"
#include "BaseLib/RunTime.h"
#include "MeshLib/Elements/Elements.h"
#include "MeshLib/MeshEnums.h"
#include "MeshLib/Properties.h"
// Check if the value can by converted to given type without overflow.
template <typename VALUE, typename TYPE>
bool is_safely_convertable(VALUE const& value)
{
bool const result = value <= std::numeric_limits<TYPE>::max();
if (!result)
{
ERR("The value {:d} is too large for conversion.", value);
ERR("Maximum available size is {:d}.",
std::numeric_limits<TYPE>::max());
}
return result;
}
namespace MeshLib
{
namespace IO
{
NodePartitionedMeshReader::NodePartitionedMeshReader(MPI_Comm comm)
: _mpi_comm(comm)
{
MPI_Comm_size(_mpi_comm, &_mpi_comm_size);
MPI_Comm_rank(_mpi_comm, &_mpi_rank);
registerNodeDataMpiType();
}
NodePartitionedMeshReader::~NodePartitionedMeshReader()
{
MPI_Type_free(&_mpi_node_type);
}
void NodePartitionedMeshReader::registerNodeDataMpiType()
{
int const count = 2;
int blocks[count] = {1, 3};
MPI_Datatype types[count] = {MPI_UNSIGNED_LONG, MPI_DOUBLE};
MPI_Aint displacements[count] = {0, sizeof(NodeData::index)};
MPI_Type_create_struct(count, blocks, displacements, types,
&_mpi_node_type);
MPI_Type_commit(&_mpi_node_type);
}
MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::read(
const std::string& file_name_base)
{
BaseLib::RunTime timer;
timer.start();
MeshLib::NodePartitionedMesh* mesh = nullptr;
// Always try binary file first
std::string const fname_new = file_name_base + "_partitioned_msh_cfg" +
std::to_string(_mpi_comm_size) + ".bin";
if (!BaseLib::IsFileExisting(fname_new)) // binary file does not exist.
{
std::string const fname_ascii = file_name_base +
"_partitioned_msh_cfg" +
std::to_string(_mpi_comm_size) + ".msh";
if (BaseLib::IsFileExisting(fname_ascii))
{
ERR("Reading of ASCII mesh file {:s} is not supported since OGS "
"version 6.3.3.",
fname_ascii);
}
OGS_FATAL("Required binary file {:s} does not exist.\n", fname_new);
}
INFO("Reading corresponding part of mesh data from binary file {:s} ...",
file_name_base);
mesh = readMesh(file_name_base);
INFO("[time] Reading the mesh took {:f} s.", timer.elapsed());
MPI_Barrier(_mpi_comm);
return mesh;
}
template <typename DATA>
bool NodePartitionedMeshReader::readDataFromFile(std::string const& filename,
MPI_Offset offset,
MPI_Datatype type,
DATA& data) const
{
// Check container size
if (!is_safely_convertable<std::size_t, int>(data.size()))
{
ERR("The container size is too large for MPI_File_read() call.");
return false;
}
// Open file
MPI_File file;
char* filename_char = const_cast<char*>(filename.data());
int const file_status = MPI_File_open(
_mpi_comm, filename_char, MPI_MODE_RDONLY, MPI_INFO_NULL, &file);
if (file_status != 0)
{
ERR("Error opening file {:s}. MPI error code {:d}", filename,
file_status);
return false;
}
// Read data
char file_mode[] = "native";
MPI_File_set_view(file, offset, type, type, file_mode, MPI_INFO_NULL);
// The static cast is checked above.
MPI_File_read(file, data.data(), static_cast<int>(data.size()), type,
MPI_STATUS_IGNORE);
MPI_File_close(&file);
return true;
}
MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::readMesh(
const std::string& file_name_base)
{
//----------------------------------------------------------------------------------
// Read headers
const std::string fname_header = file_name_base + "_partitioned_msh_";
const std::string fname_num_p_ext = std::to_string(_mpi_comm_size) + ".bin";
if (!readDataFromFile(
fname_header + "cfg" + fname_num_p_ext,
static_cast<MPI_Offset>(static_cast<unsigned>(_mpi_rank) *
sizeof(_mesh_info)),
MPI_LONG, _mesh_info))
return nullptr;
//----------------------------------------------------------------------------------
// Read Nodes
std::vector<NodeData> nodes(_mesh_info.nodes);
if (!readDataFromFile(fname_header + "nod" + fname_num_p_ext,
static_cast<MPI_Offset>(_mesh_info.offset[2]),
_mpi_node_type, nodes))
return nullptr;
std::vector<MeshLib::Node*> mesh_nodes;
std::vector<unsigned long> glb_node_ids;
setNodes(nodes, mesh_nodes, glb_node_ids);
//----------------------------------------------------------------------------------
// Read non-ghost elements
std::vector<unsigned long> elem_data(_mesh_info.regular_elements +
_mesh_info.offset[0]);
if (!readDataFromFile(fname_header + "ele" + fname_num_p_ext,
static_cast<MPI_Offset>(_mesh_info.offset[3]),
MPI_LONG, elem_data))
return nullptr;
std::vector<MeshLib::Element*> mesh_elems(_mesh_info.regular_elements +
_mesh_info.ghost_elements);
setElements(mesh_nodes, elem_data, mesh_elems);
//----------------------------------------------------------------------------------
// Read ghost element
std::vector<unsigned long> ghost_elem_data(_mesh_info.ghost_elements +
_mesh_info.offset[1]);
if (!readDataFromFile(fname_header + "ele_g" + fname_num_p_ext,
static_cast<MPI_Offset>(_mesh_info.offset[4]),
MPI_LONG, ghost_elem_data))
return nullptr;
const bool process_ghost = true;
setElements(mesh_nodes, ghost_elem_data, mesh_elems, process_ghost);
//----------------------------------------------------------------------------------
// read the properties
MeshLib::Properties p(readProperties(file_name_base));
return newMesh(BaseLib::extractBaseName(file_name_base), mesh_nodes,
glb_node_ids, mesh_elems, p);
}
MeshLib::Properties NodePartitionedMeshReader::readProperties(
const std::string& file_name_base) const
{
MeshLib::Properties p;
readProperties(file_name_base, MeshLib::MeshItemType::Node, p);
readProperties(file_name_base, MeshLib::MeshItemType::Cell, p);
readProperties(file_name_base, MeshLib::MeshItemType::IntegrationPoint, p);
return p;
}
void NodePartitionedMeshReader::readProperties(
const std::string& file_name_base, MeshLib::MeshItemType t,
MeshLib::Properties& p) const
{
const std::string item_type = MeshLib::toString(t);
const std::string fname_cfg = file_name_base + "_partitioned_" + item_type +
"_properties_cfg" +
std::to_string(_mpi_comm_size) + ".bin";
std::ifstream is(fname_cfg.c_str(), std::ios::binary | std::ios::in);
if (!is)
{
WARN(
"Could not open file '{:s}'.\n"
"\tYou can ignore this warning if the mesh does not contain {:s}-"
"wise property data.",
fname_cfg, item_type.data());
return;
}
std::size_t number_of_properties = 0;
is.read(reinterpret_cast<char*>(&number_of_properties),
sizeof(std::size_t));
std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> vec_pvmd(
number_of_properties);
for (std::size_t i(0); i < number_of_properties; ++i)
{
vec_pvmd[i] = MeshLib::IO::readPropertyVectorMetaData(is);
if (!vec_pvmd[i])
{
OGS_FATAL(
"Error in NodePartitionedMeshReader::readProperties: "
"Could not read the meta data for the PropertyVector {:d}",
i);
}
}
for (std::size_t i(0); i < number_of_properties; ++i)
{
MeshLib::IO::writePropertyVectorMetaData(*(vec_pvmd[i]));
}
auto pos = is.tellg();
auto offset =
static_cast<long>(pos) +
static_cast<long>(_mpi_rank *
sizeof(MeshLib::IO::PropertyVectorPartitionMetaData));
is.seekg(offset);
std::optional<MeshLib::IO::PropertyVectorPartitionMetaData> pvpmd(
MeshLib::IO::readPropertyVectorPartitionMetaData(is));
bool pvpmd_read_ok = static_cast<bool>(pvpmd);
bool all_pvpmd_read_ok;
MPI_Allreduce(&pvpmd_read_ok, &all_pvpmd_read_ok, 1, MPI_C_BOOL, MPI_LOR,
_mpi_comm);
if (!all_pvpmd_read_ok)
{
OGS_FATAL(
"Error in NodePartitionedMeshReader::readProperties: "
"Could not read the partition meta data for the mpi process {:d}",
_mpi_rank);
}
DBUG("[{:d}] offset in the PropertyVector: {:d}", _mpi_rank, pvpmd->offset);
DBUG("[{:d}] {:d} tuples in partition.", _mpi_rank,
pvpmd->number_of_tuples);
is.close();
const std::string fname_val = file_name_base + "_partitioned_" + item_type +
"_properties_val" +
std::to_string(_mpi_comm_size) + ".bin";
is.open(fname_val.c_str(), std::ios::binary | std::ios::in);
if (!is)
{
ERR("Could not open file '{:s}'\n."
"\tYou can ignore this warning if the mesh does not contain {:s}-"
"wise property data.",
fname_val, item_type.data());
}
readDomainSpecificPartOfPropertyVectors(vec_pvmd, *pvpmd, t, is, p);
}
void NodePartitionedMeshReader::readDomainSpecificPartOfPropertyVectors(
std::vector<std::optional<MeshLib::IO::PropertyVectorMetaData>> const&
vec_pvmd,
MeshLib::IO::PropertyVectorPartitionMetaData const& pvpmd,
MeshLib::MeshItemType t,
std::istream& is,
MeshLib::Properties& p) const
{
unsigned long global_offset = 0;
std::size_t const number_of_properties = vec_pvmd.size();
for (std::size_t i(0); i < number_of_properties; ++i)
{
DBUG(
"[{:d}] global offset: {:d}, offset within the PropertyVector: "
"{:d}.",
_mpi_rank, global_offset,
global_offset + pvpmd.offset * vec_pvmd[i]->number_of_components *
vec_pvmd[i]->data_type_size_in_bytes);
// Special field data such as OGS_VERSION, IntegrationPointMetaData,
// etc., which are not "real" integration points, are copied "as is"
// (i.e. fully) for every partition.
if (vec_pvmd[i]->property_name.find("_ip") == std::string::npos &&
t == MeshLib::MeshItemType::IntegrationPoint)
{
createSpecificPropertyVectorPart<char>(is, *vec_pvmd[i], t,
global_offset, p);
global_offset += vec_pvmd[i]->data_type_size_in_bytes *
vec_pvmd[i]->number_of_tuples;
continue;
}
if (vec_pvmd[i]->is_int_type)
{
if (vec_pvmd[i]->is_data_type_signed)
{
if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(char))
{
createPropertyVectorPart<char>(is, *vec_pvmd[i], pvpmd, t,
global_offset, p);
}
else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(int))
{
createPropertyVectorPart<int>(is, *vec_pvmd[i], pvpmd, t,
global_offset, p);
}
else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(long))
createPropertyVectorPart<long>(is, *vec_pvmd[i], pvpmd, t,
global_offset, p);
else
{
WARN(
"Implementation for reading signed integer property "
"vector '{:s}' is not available.",
vec_pvmd[i]->property_name);
}
}
else
{
if (vec_pvmd[i]->data_type_size_in_bytes ==
sizeof(unsigned char))
{
createPropertyVectorPart<unsigned char>(
is, *vec_pvmd[i], pvpmd, t, global_offset, p);
}
else if (vec_pvmd[i]->data_type_size_in_bytes ==
sizeof(unsigned int))
createPropertyVectorPart<unsigned int>(
is, *vec_pvmd[i], pvpmd, t, global_offset, p);
else if (vec_pvmd[i]->data_type_size_in_bytes ==
sizeof(unsigned long))
createPropertyVectorPart<unsigned long>(
is, *vec_pvmd[i], pvpmd, t, global_offset, p);
else
{
WARN(
"Implementation for reading unsigned property vector "
"'{:s}' is not available.",
vec_pvmd[i]->property_name);
}
}
}
else
{
if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(float))
createPropertyVectorPart<float>(is, *vec_pvmd[i], pvpmd, t,
global_offset, p);
else if (vec_pvmd[i]->data_type_size_in_bytes == sizeof(double))
createPropertyVectorPart<double>(is, *vec_pvmd[i], pvpmd, t,
global_offset, p);
else
{
WARN(
"Implementation for reading floating point property vector "
"'{:s}' is not available.",
vec_pvmd[i]->property_name);
}
}
global_offset += vec_pvmd[i]->data_type_size_in_bytes *
vec_pvmd[i]->number_of_tuples *
vec_pvmd[i]->number_of_components;
}
}
MeshLib::NodePartitionedMesh* NodePartitionedMeshReader::newMesh(
std::string const& mesh_name,
std::vector<MeshLib::Node*> const& mesh_nodes,
std::vector<unsigned long> const& glb_node_ids,
std::vector<MeshLib::Element*> const& mesh_elems,
MeshLib::Properties const& properties) const
{
std::vector<std::size_t> gathered_n_active_base_nodes(_mpi_comm_size);
MPI_Allgather(&_mesh_info.active_base_nodes,
1,
MPI_UNSIGNED_LONG,
gathered_n_active_base_nodes.data(),
1,
MPI_UNSIGNED_LONG,
_mpi_comm);
std::vector<std::size_t> n_active_base_nodes_at_rank;
n_active_base_nodes_at_rank.push_back(0);
std::partial_sum(begin(gathered_n_active_base_nodes),
end(gathered_n_active_base_nodes),
back_inserter(n_active_base_nodes_at_rank));
std::vector<std::size_t> gathered_n_active_high_order_nodes(_mpi_comm_size);
std::size_t const n_active_high_order_nodes =
_mesh_info.active_nodes - _mesh_info.active_base_nodes;
MPI_Allgather(&n_active_high_order_nodes,
1,
MPI_UNSIGNED_LONG,
gathered_n_active_high_order_nodes.data(),
1,
MPI_UNSIGNED_LONG,
_mpi_comm);
std::vector<std::size_t> n_active_high_order_nodes_at_rank;
n_active_high_order_nodes_at_rank.push_back(0);
std::partial_sum(begin(gathered_n_active_high_order_nodes),
end(gathered_n_active_high_order_nodes),
back_inserter(n_active_high_order_nodes_at_rank));
return new MeshLib::NodePartitionedMesh(
mesh_name, mesh_nodes, glb_node_ids, mesh_elems, properties,
_mesh_info.global_base_nodes, _mesh_info.global_nodes,
_mesh_info.active_nodes, std::move(n_active_base_nodes_at_rank),
std::move(n_active_high_order_nodes_at_rank));
}
void NodePartitionedMeshReader::setNodes(
const std::vector<NodeData>& node_data,
std::vector<MeshLib::Node*>& mesh_node,
std::vector<unsigned long>& glb_node_ids) const
{
mesh_node.resize(_mesh_info.nodes);
glb_node_ids.resize(_mesh_info.nodes);
for (std::size_t i = 0; i < mesh_node.size(); i++)
{
NodeData const& nd = node_data[i];
glb_node_ids[i] = nd.index;
mesh_node[i] = new MeshLib::Node(nd.x, nd.y, nd.z, i);
}
}
void NodePartitionedMeshReader::setElements(
const std::vector<MeshLib::Node*>& mesh_nodes,
const std::vector<unsigned long>& elem_data,
std::vector<MeshLib::Element*>& mesh_elems, const bool ghost) const
{
// Number of elements, either ghost or regular
unsigned long const ne =
ghost ? _mesh_info.ghost_elements : _mesh_info.regular_elements;
unsigned long const id_offset_ghost =
ghost ? _mesh_info.regular_elements : 0;
for (unsigned long i = 0; i < ne; i++)
{
unsigned long id_offset_elem = elem_data[i];
// Unused for now, keep for elem_data documentation purpose here.
{
const unsigned mat_idx =
static_cast<unsigned>(elem_data[id_offset_elem++]);
(void)mat_idx;
}
const unsigned long e_type = elem_data[id_offset_elem++];
unsigned long const nnodes = elem_data[id_offset_elem++];
MeshLib::Node** elem_nodes = new MeshLib::Node*[nnodes];
for (unsigned long k = 0; k < nnodes; k++)
elem_nodes[k] = mesh_nodes[elem_data[id_offset_elem++]];
// The element types below are defined by the MeshLib::CellType.
switch (static_cast<CellType>(e_type))
{
case CellType::POINT1:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Point(elem_nodes);
break;
case CellType::LINE2:
mesh_elems[i + id_offset_ghost] = new MeshLib::Line(elem_nodes);
break;
case CellType::LINE3:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Line3(elem_nodes);
break;
case CellType::QUAD4:
mesh_elems[i + id_offset_ghost] = new MeshLib::Quad(elem_nodes);
break;
case CellType::QUAD8:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Quad8(elem_nodes);
break;
case CellType::QUAD9:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Quad9(elem_nodes);
break;
case CellType::HEX8:
mesh_elems[i + id_offset_ghost] = new MeshLib::Hex(elem_nodes);
break;
case CellType::HEX20:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Hex20(elem_nodes);
break;
case CellType::HEX27:
OGS_FATAL(
"NodePartitionedMeshReader: construction of HEX27 element "
"with id {:d} is not implemented.",
i);
break;
case CellType::TRI3:
mesh_elems[i + id_offset_ghost] = new MeshLib::Tri(elem_nodes);
break;
case CellType::TRI6:
mesh_elems[i + id_offset_ghost] = new MeshLib::Tri6(elem_nodes);
break;
case CellType::TET4:
mesh_elems[i + id_offset_ghost] = new MeshLib::Tet(elem_nodes);
break;
case CellType::TET10:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Tet10(elem_nodes);
break;
case CellType::PRISM6:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Prism(elem_nodes);
break;
case CellType::PRISM15:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Prism15(elem_nodes);
break;
case CellType::PYRAMID5:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Pyramid(elem_nodes);
break;
case CellType::PYRAMID13:
mesh_elems[i + id_offset_ghost] =
new MeshLib::Pyramid13(elem_nodes);
break;
case CellType::INVALID:
OGS_FATAL(
"NodePartitionedMeshReader: construction of INVALID "
"element type with id {:d} is not possible.",
i);
break;
default:
OGS_FATAL(
"NodePartitionedMeshReader: construction of element type "
"{:d} is not implemented.",
e_type);
}
}
}
} // namespace IO
} // namespace MeshLib