Commit ff5a283b authored by Lars Bilke's avatar Lars Bilke

Merge branch 'staticMeshXdmfWriter' into 'master'

Static mesh xdmf writer

See merge request ogs/ogs!3246
parents 78fd5333 20b44cd9
......@@ -46,9 +46,6 @@
[submodule "ThirdParty/spdlog"]
path = ThirdParty/spdlog
url = https://github.com/gabime/spdlog.git
[submodule "ThirdParty/xdmfdiff"]
path = ThirdParty/xdmfdiff
url = https://gitlab.opengeosys.org/ogs/xdmfdiff.git
[submodule "ThirdParty/xdmf"]
path = ThirdParty/xdmf
url = https://gitlab.opengeosys.org/ogs/xdmflib.git
......@@ -268,12 +268,6 @@ if(EXISTS ${PROJECT_SOURCE_DIR}/ThirdParty/vtkdiff/CMakeLists.txt AND BUILD_TEST
install(PROGRAMS $<TARGET_FILE:vtkdiff> DESTINATION bin COMPONENT ogs_extras)
endif()
# xdmfdiff
if(EXISTS ${PROJECT_SOURCE_DIR}/ThirdParty/xdmfdiff/CMakeLists.txt AND BUILD_TESTING)
add_subdirectory(ThirdParty/xdmfdiff)
install(PROGRAMS $<TARGET_FILE:xdmfdiff> DESTINATION bin COMPONENT ogs_extras)
endif()
include(scripts/cmake/CheckHeaderCompilation.cmake)
add_subdirectory(Applications)
......
......@@ -17,6 +17,7 @@ namespace GitInfoLib
namespace GitInfo
{
const std::string OGS_VERSION = "OGS_VERSION";
const std::string git_version_sha1_short("@GIT_SHA1_SHORT@");
const std::string ogs_version("@OGS_VERSION@");
}
......
......@@ -21,6 +21,7 @@ namespace GitInfoLib
namespace GitInfo
{
extern GITINFOLIB_EXPORT std::string const OGS_VERSION;
extern GITINFOLIB_EXPORT const std::string git_version_sha1_short;
extern GITINFOLIB_EXPORT const std::string ogs_version;
} // namespace
......
......@@ -26,7 +26,7 @@ ogs_add_library(MeshLib ${SOURCES})
target_link_libraries(
MeshLib
PUBLIC BaseLib GeoLib MathLib ${VTK_LIBRARIES}
PUBLIC BaseLib GeoLib GitInfoLib MathLib ${VTK_LIBRARIES} OgsXdmf
PRIVATE spdlog::spdlog)
if(OGS_USE_MPI AND TARGET MPI::MPI_CXX)
......
/**
* \file
* \copyright
* Copyright (c) 2012-2020, 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 "Xdmf3Writer.h"
#include <Xdmf.hpp>
#include <XdmfAttribute.hpp>
#include <XdmfDomain.hpp>
#include <XdmfGeometryType.hpp>
#include <XdmfGridCollection.hpp>
#include <XdmfGridCollectionType.hpp>
#include <XdmfHDF5Controller.hpp>
#include <XdmfHeavyDataDescription.hpp>
#include <XdmfInformation.hpp>
#include <XdmfTopologyType.hpp>
#include <XdmfUnstructuredGrid.hpp>
#include <XdmfWriter.hpp>
#include <string>
#include "InfoLib/GitInfo.h"
#include "XdmfData.h"
#include "writeHDF5.h"
using namespace MeshLib::IO;
using namespace std::string_literals;
static std::string getTimeSection(int const step, std::string const& name)
{
return "t_"s + std::to_string(step) + "/"s + name;
}
static boost::shared_ptr<XdmfGeometry> getLightGeometry(
std::string const& hdf5filename, int const step, Geometry const& geometry)
{
auto xdmf_geometry = XdmfGeometry::New();
xdmf_geometry->setType(XdmfGeometryType::XYZ());
boost::shared_ptr<XdmfHDF5Controller> geometry_controller =
XdmfHDF5Controller::New(hdf5filename,
getTimeSection(step, "geometry"),
XdmfArrayType::Float64(),
geometry.starts,
geometry.strides,
geometry.vdims,
geometry.vdims);
xdmf_geometry->setHeavyDataController(geometry_controller);
return xdmf_geometry;
}
static boost::shared_ptr<XdmfTopology> getLightTopology(
std::string const& hdf5filename, int const step, Topology const& topology)
{
auto xdmf_topology = XdmfTopology::New();
xdmf_topology->setType(XdmfTopologyType::Mixed());
auto topology_controller =
XdmfHDF5Controller::New(hdf5filename,
getTimeSection(step, "topology"),
XdmfArrayType::Int32(),
topology.starts,
topology.strides,
topology.vdims,
topology.vdims);
xdmf_topology->setHeavyDataController(topology_controller);
return xdmf_topology;
}
static boost::shared_ptr<XdmfAttribute> getLightAttribute(
std::string const& hdf5filename,
int const step,
AttributeMeta const& attribute)
{
auto attribute_controller =
XdmfHDF5Controller::New(hdf5filename,
getTimeSection(step, attribute.name),
attribute.data_type,
attribute.starts,
attribute.strides,
attribute.vdims,
attribute.vdims);
auto xdmf_attribute = XdmfAttribute::New();
xdmf_attribute->setCenter(attribute.attribute_center);
xdmf_attribute->setName(attribute.name);
xdmf_attribute->setHeavyDataController(attribute_controller);
return xdmf_attribute;
}
namespace MeshLib::IO
{
Xdmf3Writer::Xdmf3Writer(std::filesystem::path const& filepath,
Geometry const& geometry,
Topology const& topology,
std::vector<AttributeMeta>
attributes,
int const timestep)
: _attributes(std::move(attributes)),
_hdf5filepath(filepath.parent_path() /
(filepath.stem().string() + ".h5"))
{
std::filesystem::path const xdmf_filepath =
filepath.parent_path() / (filepath.stem().string() + ".xdmf");
auto const ret_hdf5 = writeHDF5Initial(geometry.flattend_values,
geometry.vldims,
topology.flattend_values,
timestep,
_hdf5filepath);
// If we find a library for compression we use it
_use_compression = ret_hdf5.second;
_initial_geometry =
getLightGeometry(_hdf5filepath.filename().string(), timestep, geometry);
_initial_topology =
getLightTopology(_hdf5filepath.filename().string(), timestep, topology);
_writer = XdmfWriter::New(xdmf_filepath.string());
_writer->setMode(XdmfWriter::DistributedHeavyData);
auto version = XdmfInformation::New();
version->setKey(GitInfoLib::GitInfo::OGS_VERSION);
version->setValue(GitInfoLib::GitInfo::ogs_version);
auto grid_collection = XdmfGridCollection::New();
grid_collection->setType(XdmfGridCollectionType::Temporal());
_root = XdmfDomain::New();
_root->insert(version);
_root->insert(grid_collection);
}
void Xdmf3Writer::writeStep(int const time_step, double const time)
{
auto grid = XdmfUnstructuredGrid::New();
grid->setGeometry(_initial_geometry);
grid->setTopology(_initial_topology);
grid->setTime(XdmfTime::New(time));
for (auto const& attribute : _attributes)
{
writeHDF5Step(_hdf5filepath,
time_step,
attribute.name,
attribute.data_start,
attribute.vldims,
attribute.data_type,
_use_compression);
grid->insert(
getLightAttribute(_hdf5filepath.filename().string(), time_step, attribute));
}
auto gridcollection = _root->getGridCollection(0);
gridcollection->insert(grid);
_root->accept(_writer);
}
} // namespace MeshLib::IO
/**
* \file
* \author Tobias Meisel
* \date 2020-11-13
* \brief XdmfWriter which takes contiguous data and writes 1 xdmf + 1 hdf file
*
* \copyright
* Copyright (c) 2012-2020, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*/
#pragma once
#include <filesystem.h>
#include <boost/shared_ptr.hpp>
#include "XdmfData.h"
class XdmfGridCollection;
class XdmfTopology;
class XdmfGeometry;
class XdmfWriter;
class XdmfDomain;
namespace MeshLib::IO
{
struct Geometry;
struct Topology;
} // namespace MeshLib::IO
namespace MeshLib::IO
{
class Xdmf3Writer
{
public:
/**
* \brief Write xdmf and h5 file with geometry and topology data.
* @param filepath absolute or relativ filepath to the hdf5 file
* @param time_step number of the step (temporal collection)
* @param geometry contains point coordinates
* @param topology contains cells
* @param attributes vector of attributes (each attribute is a OGS property)
*/
Xdmf3Writer(std::filesystem::path const& filepath,
Geometry const& geometry,
Topology const& topology,
std::vector<AttributeMeta> attributes,
int time_step);
/**
* \brief Write attribute data that has modified to previous time step or
* initial
* @param filepath absolute or relativ filepath to the hdf5 file
* @param time_step number of the step (temporal collection)
* @param time time value of the current time_step
*/
void writeStep(int time_step, double time);
private:
boost::shared_ptr<XdmfGridCollection> _gridCollection;
boost::shared_ptr<XdmfTopology> _initial_topology;
boost::shared_ptr<XdmfGeometry> _initial_geometry;
std::vector<AttributeMeta> _attributes;
boost::shared_ptr<XdmfWriter> _writer;
boost::shared_ptr<XdmfDomain> _root;
std::filesystem::path const _hdf5filepath;
bool _use_compression;
};
} // namespace MeshLib::IO
/**
* \file
* \author Tobias Meisel
* \date 2020-11-13
* \brief Definition of the data layer for writing Meshes
* \copyright
* Copyright (c) 2012-2020, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*/
#pragma once
#include <boost/shared_ptr.hpp>
#include <string>
#include <vector>
class XdmfAttributeCenter;
class XdmfArrayType;
namespace MeshLib::IO
{
using XdmfDimType = unsigned int;
using Hdf5DimType = unsigned long long;
struct Geometry final
{
std::vector<double> flattend_values;
std::vector<XdmfDimType> const starts;
std::vector<XdmfDimType> const strides;
std::vector<XdmfDimType> const vdims;
std::vector<Hdf5DimType> const vldims;
};
struct Topology final
{
std::vector<int> flattend_values;
std::vector<XdmfDimType> const starts;
std::vector<XdmfDimType> const strides;
std::vector<XdmfDimType> const vdims;
std::vector<Hdf5DimType> const vldims;
};
struct AttributeMeta final
{
const void* data_start;
std::string name;
boost::shared_ptr<const XdmfAttributeCenter> attribute_center;
boost::shared_ptr<const XdmfArrayType> data_type;
std::vector<XdmfDimType> starts;
std::vector<XdmfDimType> strides;
std::vector<XdmfDimType> vdims;
std::vector<Hdf5DimType> vldims;
};
} // namespace MeshLib::IO
\ No newline at end of file
/**
* \file
* \copyright
* Copyright (c) 2012-2020, OpenGeoSys Community (http://www.opengeosys.org)
* Distributed under a Modified BSD License.
* See accompanying file LICENSE.txt or
* http://www.opengeosys.org/project/license
*/
// \TODO (tm) Extend xdmf lib with 64bit datatypes
#if !_MSC_VER
#pragma GCC diagnostic ignored "-Wnarrowing"
#endif
#include "transformData.h"
#include <XdmfArrayType.hpp>
#include <XdmfAttributeCenter.hpp>
#include <XdmfTopologyType.hpp>
#include <optional>
#include <variant>
#include "InfoLib/GitInfo.h"
#include "MeshLib/Elements/Element.h"
#include "MeshLib/Mesh.h"
#include "MeshLib/MeshEnums.h"
#include "MeshLib/Node.h"
namespace
{
using XdmfDimType = unsigned int;
using Hdf5DimType = unsigned long long;
} // namespace
namespace MeshLib::IO
{
// \TODO (tm) constexpr by other funtion signature can not be transformed to
// constexpr shared_ptr is not literal type / has non trivial destructor
boost::shared_ptr<const XdmfTopologyType> cellTypeOGS2XDMF(
MeshLib::CellType const cell_type)
{
static std::map<MeshLib::CellType const,
boost::shared_ptr<const XdmfTopologyType> const>
elem_type_ogs2xdmf = {
{MeshLib::CellType::POINT1, XdmfTopologyType::Polyvertex()},
{MeshLib::CellType::LINE2, XdmfTopologyType::Polyline(2)},
{MeshLib::CellType::LINE3, XdmfTopologyType::Polyline(3)},
{MeshLib::CellType::QUAD4, XdmfTopologyType::Quadrilateral()},
{MeshLib::CellType::QUAD8, XdmfTopologyType::Quadrilateral_8()},
{MeshLib::CellType::QUAD9, XdmfTopologyType::Quadrilateral_9()},
{MeshLib::CellType::TET4, XdmfTopologyType::Tetrahedron()},
{MeshLib::CellType::TET10, XdmfTopologyType::Tetrahedron_10()},
{MeshLib::CellType::TRI3, XdmfTopologyType::Triangle()},
{MeshLib::CellType::TRI6, XdmfTopologyType::Triangle_6()},
{MeshLib::CellType::PRISM6,
XdmfTopologyType::Wedge()}, // parallel triangle wedge
{MeshLib::CellType::PRISM15, XdmfTopologyType::Wedge_15()},
{MeshLib::CellType::PRISM18, XdmfTopologyType::Wedge_18()},
{MeshLib::CellType::PYRAMID13, XdmfTopologyType::Pyramid_13()},
{MeshLib::CellType::PYRAMID5, XdmfTopologyType::Pyramid()},
{MeshLib::CellType::HEX20, XdmfTopologyType::Hexahedron_20()},
{MeshLib::CellType::HEX27, XdmfTopologyType::Hexahedron_27()},
{MeshLib::CellType::HEX8, XdmfTopologyType::Hexahedron()}};
return elem_type_ogs2xdmf.at(cell_type);
}
boost::shared_ptr<const XdmfAttributeCenter> elemTypeOGS2XDMF(
MeshLib::MeshItemType const elem_type)
{
std::map<MeshLib::MeshItemType,
boost::shared_ptr<const XdmfAttributeCenter>>
mesh_item_type_ogs2xdmf = {
{MeshLib::MeshItemType::Cell, XdmfAttributeCenter::Cell()},
{MeshLib::MeshItemType::Edge, XdmfAttributeCenter::Edge()},
{MeshLib::MeshItemType::Face, XdmfAttributeCenter::Face()},
{MeshLib::MeshItemType::Node, XdmfAttributeCenter::Node()},
{MeshLib::MeshItemType::IntegrationPoint,
XdmfAttributeCenter::Other()}};
return mesh_item_type_ogs2xdmf.at(elem_type);
}
std::optional<AttributeMeta> transformAttribute(
std::pair<std::string, PropertyVectorBase*> const& property)
{
// lambda f : Transforms property to AttributeMeta, first parameter is
// output parameter because the boolean return type is used to allow kind of
// pipe using || operator
auto f = [&](std::optional<AttributeMeta>& attribute_meta,
auto basic_type,
auto property_pair) -> bool {
auto const property_base = property_pair.second;
auto const name = property_pair.first;
auto const typed_property =
dynamic_cast<PropertyVector<decltype(basic_type)> const*>(
property_base);
if (typed_property == nullptr)
{
attribute_meta = std::nullopt;
return false;
}
auto const mesh_item_type =
elemTypeOGS2XDMF(property_base->getMeshItemType());
auto const global_components =
property_base->getNumberOfGlobalComponents();
auto const size = typed_property->getNumberOfTuples();
auto const vdims = [](XdmfDimType num_components,
XdmfDimType size) -> std::vector<XdmfDimType> {
if (num_components > 1)
{
return {size, num_components};
}
return {size};
}(global_components, size);
// \TODO (tm) Remove code duplicationby eliminating the need for a
// second vldim at all by modification of XdmfHdf5Controller
// taking unsigned long long
auto const vldims = [](XdmfDimType num_components,
XdmfDimType size) -> std::vector<Hdf5DimType> {
if (num_components > 1)
{
return std::vector<Hdf5DimType>{size, num_components};
}
else
{
return std::vector<Hdf5DimType>{size};
}
}(global_components, size);
auto data_type = XdmfArrayType::Float64();
if constexpr (std::is_same_v<double, decltype(basic_type)>)
{
// The standard 64-bit IEEE 754 floating-point type (double-precision)
// has a 53 bit fractional part (52 bits written, one implied)
static_assert((std::numeric_limits<double>::digits == 53),
"Double has 52 bits fractional part");
data_type = XdmfArrayType::Float64();
}
else if constexpr (std::is_same_v<float, decltype(basic_type)>)
{
// The standard 32-bit IEEE 754 floating-point type (single-precision)
// has a 24 bit fractional part (23 bits written, one implied)
static_assert((std::numeric_limits<float>::digits == 24),
"Float has 23 bits fractional part");
data_type = XdmfArrayType::Float32();
}
else if constexpr (std::is_same_v<int, decltype(basic_type)>)
{
static_assert((std::numeric_limits<int>::digits == 31),
"Signed int has 32-1 bits");
data_type = XdmfArrayType::Int32();
}
// \TODO (tm) Reimplement size checks
// else if constexpr (std::is_same_v<long, decltype(basic_type)>)
// {
// static_assert((std::numeric_limits<long>::digits == 63),
// "Signed int has 64-1 bits");
// data_type = XdmfArrayType::Int64();
// }
else if constexpr (std::is_same_v<unsigned int, decltype(basic_type)>)
{
static_assert((std::numeric_limits<unsigned int>::digits == 32),
"Unsigned int has 32 bits");
data_type = XdmfArrayType::UInt32();
}
// else if constexpr (std::is_same_v<unsigned long, decltype(basic_type)>)
// {
// static_assert((std::numeric_limits<unsigned long>::digits == 64),
// "Unsigned long has 64 bits");
// // \TODO (tm) Extend XdmfLibrary with 64bit datatypes
// data_type = XdmfArrayType::UInt32();
// }
else if constexpr (std::is_same_v<std::size_t, decltype(basic_type)>)
{
static_assert((std::numeric_limits<std::size_t>::digits == 64),
"size_t has 64 bits");
// \TODO (tm) Extend XdmfLibrary with 64bit datatypes
data_type = XdmfArrayType::UInt32();
}
else if constexpr (std::is_same_v<char, decltype(basic_type)>)
{
static_assert((std::numeric_limits<char>::digits == 7),
"char has 8-1 bits");
data_type = XdmfArrayType::Int8();
}
else if constexpr (std::is_same_v<unsigned char, decltype(basic_type)>)
{
static_assert((std::numeric_limits<unsigned char>::digits == 8),
"Unsigned char has 8 bits");
data_type = XdmfArrayType::UInt8();
}
else
{
return false;
}
std::vector<XdmfDimType> const starts = {0, 0, 0};
std::vector<XdmfDimType> const strides = {1, 1, 1};
attribute_meta = {typed_property->data(),
name,
mesh_item_type,
data_type,
starts,
strides,
vdims,
vldims};
return true;
};
std::optional<AttributeMeta> attribute;
f(attribute, double{}, property) || f(attribute, float{}, property) ||
f(attribute, int{}, property) || f(attribute, long{}, property) ||
f(attribute, unsigned{}, property) || f(attribute, long{}, property) ||
f(attribute, static_cast<unsigned long>(0), property) ||
f(attribute, std::size_t{}, property) ||
f(attribute, char{}, property) ||
f(attribute, static_cast<unsigned char>(0), property);
if (!attribute)
{
OGS_FATAL("Could not apply function to PropertyVector '{:s}'.",
property.second->getPropertyName());
return std::nullopt;
}
return attribute;
}
std::vector<AttributeMeta> transformAttributes(MeshLib::Mesh const& mesh)
{
MeshLib::Properties const& properties = mesh.getProperties();
// \TODO (tm) use c++20 ranges
// a = p | filter (first!=OGS_VERSION) | filter null_opt | transformAttr |
// optional_value
std::vector<AttributeMeta> attributes;
for (auto [name, property_base] : properties)
{
if (name == GitInfoLib::GitInfo::OGS_VERSION)
{
continue;
}
auto attribute = transformAttribute(std::pair(name, property_base));
if (attribute)
{
attributes.push_back(attribute.value());
}
}
return attributes;
}