Skip to content
Snippets Groups Projects
writeXdmf.cpp 14.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    #include "writeXdmf.h"
    
    // ToDo (tm) Remove spdlogfmt includes with c++20
    #include <spdlog/fmt/bundled/core.h>
    #include <spdlog/fmt/bundled/format.h>
    
    #include <algorithm>
    
    #include <array>
    
    #include <fstream>
    #include <iostream>
    #include <iterator>
    #include <list>
    #include <map>
    #include <optional>
    #include <string_view>
    #include <vector>
    
    
    #include "BaseLib/Error.h"
    
    #include "BaseLib/cpp23.h"
    #include "MeshLib/Location.h"
    
    // naming convention for local function objects by postfix:
    // _transform: functions that take data (mostly XDMF meta data) and return
    // transformed data (XDMF string)
    // _fn: functions that take a function and return a new function
    
    using namespace fmt::literals;
    using namespace BaseLib;
    namespace MeshLib::IO
    {
    // similar definition in Location.h - XDMF uses capital letters
    // Actually there is no correspondece for "IntegrationPoint" in XDMF Format
    // specification
    constexpr char const* mesh_item_type_strings[] = {"Node", "Edge", "Face",
                                                      "Cell", "IntegrationPoint"};
    
    // Transforms MeshItemType into string
    
    static std::string meshItemTypeString(
    
        std::optional<MeshItemType> const& item_type)
    {
        if (item_type)
        {
            return mesh_item_type_strings[static_cast<int>(item_type.value())];
        }
    
        OGS_FATAL("Cannot convert an empty optional mesh item type.");
    
    }
    
    // Transforms MeshPropertyDataType into string
    // constexpr with literal type std::string not yet supported
    static auto meshPropertyDatatypeString()
    {
        std::array<std::string, to_underlying(MeshPropertyDataType::enum_length)>
            ogs_to_xdmf_type = {};
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::float64)] = "Float";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::float32)] = "Float";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int32)] = "Int";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int64)] = "Int";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint32)] = "UInt";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint64)] = "UInt";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::int8)] = "Int";
        ogs_to_xdmf_type[to_underlying(MeshPropertyDataType::uint8)] = "UInt";
        return ogs_to_xdmf_type;
    }
    
    // Transform MeshPropertyDatatype into string )
    static std::string getPropertyDataTypeString(
        MeshPropertyDataType const& ogs_data_type)
    {
    
        return meshPropertyDatatypeString()[to_underlying(ogs_data_type)];
    
    }
    
    // Returns MeshPropertyDataType-to-Size (in bytes) lookup-table
    static constexpr auto meshPropertyDatatypeSize()
    {
        std::array<int, to_underlying(MeshPropertyDataType::enum_length)>
            property_sizes{};
        property_sizes[to_underlying(MeshPropertyDataType::float64)] = 8;
        property_sizes[to_underlying(MeshPropertyDataType::float32)] = 4,
        property_sizes[to_underlying(MeshPropertyDataType::int32)] = 4,
        property_sizes[to_underlying(MeshPropertyDataType::int64)] = 8,
        property_sizes[to_underlying(MeshPropertyDataType::uint32)] = 4,
        property_sizes[to_underlying(MeshPropertyDataType::uint64)] = 8,
        property_sizes[to_underlying(MeshPropertyDataType::int8)] = 1,
        property_sizes[to_underlying(MeshPropertyDataType::uint8)] = 1;
        return property_sizes;
    }
    
    
    inline auto ogs_to_xdmf_type = meshPropertyDatatypeSize();
    
    
    // Transform MeshPropertyDataType into type_sizes (in bytes)
    static std::string getPropertyDataTypeSize(
        MeshPropertyDataType const& ogs_data_type)
    {
        return fmt::format("{}", ogs_to_xdmf_type[to_underlying(ogs_data_type)]);
    }
    
    // Collects all known data and return a function that takes all unknown data
    // known: XdmfData of mesh to be written, unknown: timesteps
    std::function<std::string(std::vector<double>)> write_xdmf(
        XdmfData const& geometry, XdmfData const& topology,
        std::vector<XdmfData> const& constant_attributes,
        std::vector<XdmfData> const& variable_attributes,
        std::string const& h5filename, std::string const& ogs_version)
    {
        // Generates function that writes <DataItem>. Late binding needed because
        // maximum number of steps unknown. Time step and h5filename are captured to
        // return a unary function
        // _a suffix is a user defined literal for fmt named arguments
        auto const time_dataitem_genfn = [](int const time_step, int const max_step,
    
    wenqing's avatar
    wenqing committed
                                            std::string const h5filename)
        {
            return [time_step, max_step, h5filename](auto const& xdmfdata)
            {
    
                return fmt::format(
                    "\n\t\t<DataItem DataType=\"{datatype}\" "
                    "Dimensions=\"{local_dimensions}\" "
                    "Format=\"HDF\" "
                    "Precision=\"{precision}\">{filename}:data/{datasetname}|"
                    "{time_step} {starts}:1 {strides}:1 "
                    "{local_dimensions}:{max_step} "
                    "{global_dimensions}</"
                    "DataItem>",
                    "datatype"_a = getPropertyDataTypeString(xdmfdata.data_type),
                    "local_dimensions"_a =
                        fmt::join(xdmfdata.global_block_dims, " "),
                    "precision"_a = getPropertyDataTypeSize(xdmfdata.data_type),
                    "filename"_a = h5filename,
                    "datasetname"_a = xdmfdata.name,
                    "starts"_a = fmt::join(xdmfdata.starts, " "),
                    "strides"_a = fmt::join(xdmfdata.strides, " "),
                    "global_dimensions"_a =
                        fmt::join(xdmfdata.global_block_dims, " "),
                    "time_step"_a = fmt::format("{}", time_step),
                    "max_step"_a = fmt::format("{}", max_step));
            };
        };
    
        // string_join could be moved to ogs lib if used more
    
    wenqing's avatar
    wenqing committed
        auto const string_join_fn = [](auto const& collection)
        { return fmt::to_string(fmt::join(collection, "")); };
    
    
        // m_bind could be moved to ogs lib if used more
    
    wenqing's avatar
    wenqing committed
        auto const m_bind_fn = [](auto const& transform, auto const& join)
        {
            return [join, transform](auto const& collection)
            {
    
                temp.reserve(collection.size());
    
                std::transform(collection.begin(), collection.end(),
                               std::back_inserter(temp), transform);
                return join(temp);
            };
        };
    
        // XDMF if part of the data that is already written in any previous step
        // subsequent steps can refer to this data collection of elements navigates
        // the xml tree (take first child, then in child take 2nd child ...)
    
    wenqing's avatar
    wenqing committed
        auto const pointer_transfrom = [](auto const& elements)
        {
    
            return fmt::format(
                "\n\t<xi:include xpointer=\"element(/{elements})\"/>",
                "elements"_a = fmt::join(elements, "/"));
        };
    
        // Defines content of <Attribute> in XDMF, child of Attribute is a single
        // <DataItem>
    
    wenqing's avatar
    wenqing committed
        auto const attribute_transform =
            [](XdmfData const& attribute, auto const& dataitem_transform)
        {
    
            return fmt::format(
                "\n\t<Attribute Center=\"{center}\" ElementCell=\"\" "
                "ElementDegree=\"0\" "
                "ElementFamily=\"\" ItemType=\"\" Name=\"{name}\" "
                "Type=\"None\">{dataitem}\n\t</Attribute>",
    
                "center"_a = meshItemTypeString(attribute.attribute_center),
    
                "name"_a = attribute.name,
                "dataitem"_a = dataitem_transform(attribute));
        };
    
        // Define content of <Geometry> in XDMF, same as attribute_transform
    
    wenqing's avatar
    wenqing committed
        auto const geometry_transform =
            [](XdmfData const& geometry, auto const& dataitem_transform)
        {
    
            return fmt::format(
                "\n\t<Geometry Origin=\"\" Type=\"XYZ\">{dataitem}\n\t</Geometry>",
                "dataitem"_a = dataitem_transform(geometry));
        };
    
        // Define content of <Topology> in XDMF, same as attribute_transform
    
    wenqing's avatar
    wenqing committed
        auto const topology_transform =
            [](XdmfData const& topology, auto const& dataitem_transform)
        {
    
            return fmt::format(
                "\n\t<Topology Dimensions=\"{dimensions}\" "
                "Type=\"Mixed\">{dataitem}\n\t</Topology>",
                "dataitem"_a = dataitem_transform(topology),
                "dimensions"_a = fmt::join(topology.global_block_dims, " "));
        };
    
        // Defines content of <Grid> of a single time step, takes specific transform
        // functions for all of its child elements
    
    wenqing's avatar
    wenqing committed
        auto const grid_transform =
            [](double const& time_value, auto const& geometry, auto const& topology,
               auto const& constant_attributes, auto const& variable_attributes)
        {
    
            return fmt::format(
                "\n<Grid Name=\"Grid\">\n\t<Time "
                "Value=\"{time_value}\"/"
                ">{geometry}{topology}{fix_attributes}{variable_attributes}\n</"
                "Grid>",
                "time_value"_a = time_value, "geometry"_a = geometry,
                "topology"_a = topology, "fix_attributes"_a = constant_attributes,
                "variable_attributes"_a = variable_attributes);
        };
    
        // An attribute may change over time (variable) or stay constant
        enum class time_attribute
        {
            constant,
            variable
        };
    
        // Generates a function that either writes the data or points to existing
        // data
        auto const time_step_fn = [time_dataitem_genfn, pointer_transfrom,
                                   h5filename](auto const& component_transform,
                                               int const time_step,
                                               int const max_step,
    
    wenqing's avatar
    wenqing committed
                                               time_attribute const variable)
        {
    
            return [component_transform, time_dataitem_genfn, pointer_transfrom,
    
    wenqing's avatar
    wenqing committed
                    variable, time_step, max_step, h5filename](XdmfData const& attr)
            {
    
                bool const changed =
    
                    ((time_step > 0 && (variable == time_attribute::variable)) ||
                     time_step == 0);
                if (changed)
                {
                    auto dataitem =
                        time_dataitem_genfn(time_step, max_step, h5filename);
                    return component_transform(attr, dataitem);
                }
                else
                {
    
                    std::array<unsigned int, 5> position = {1, 1, 2, 1, attr.index};
                    return pointer_transfrom(position);
    
                };
            };
        };
    
        // Top-Level transform function (take spatial and temporal transform
        // functions) and return the time depended grid transform function
        // ToDo (tm) Remove capturing m_bind and string_join as helper function
    
        auto const time_grid_transform =
            [time_step_fn, m_bind_fn, string_join_fn, grid_transform,
             geometry_transform, topology_transform, attribute_transform](
                double const time, int const step, int const max_step,
                auto const& geometry, auto const& topology,
    
    wenqing's avatar
    wenqing committed
                auto const& constant_attributes, auto const& variable_attributes)
        {
            auto const time_step_geometry_transform = time_step_fn(
                geometry_transform, step, max_step, time_attribute::constant);
            auto const time_step_topology_transform = time_step_fn(
                topology_transform, step, max_step, time_attribute::constant);
            auto const time_step_const_attribute_fn = time_step_fn(
                attribute_transform, step, max_step, time_attribute::constant);
            auto const time_step_variable_attribute_fn = time_step_fn(
                attribute_transform, step, max_step, time_attribute::variable);
    
    wenqing's avatar
    wenqing committed
            // lift both functions from handling single attributes to work with
            // collection of attributes
            auto const variable_attributes_transform =
                m_bind_fn(time_step_variable_attribute_fn, string_join_fn);
            auto const constant_attributes_transform =
                m_bind_fn(time_step_const_attribute_fn, string_join_fn);
    
    wenqing's avatar
    wenqing committed
            return grid_transform(
                time, time_step_geometry_transform(geometry),
                time_step_topology_transform(topology),
                constant_attributes_transform(constant_attributes),
                variable_attributes_transform(variable_attributes));
        };
    
    
        // Function that combines all the data from spatial (geometry, topology,
        // attribute) and temporal domain (time) with the time domain
        // (time_step_fn). And writes <Grid CollectionType="Temporal">
        auto const temporal_grid_collection_transform =
            [time_grid_transform](
                auto const& times, auto const& geometry, auto const& topology,
    
    wenqing's avatar
    wenqing committed
                auto const& constant_attributes, auto const& variable_attributes)
        {
            // c++20 ranges zip missing
            auto const max_step = times.size();
            std::vector<std::string> grids;
    
            grids.reserve(max_step);
    
    wenqing's avatar
    wenqing committed
            for (size_t time_step = 0; time_step < max_step; ++time_step)
            {
                grids.push_back(time_grid_transform(
                    times[time_step], time_step, max_step, geometry, topology,
                    constant_attributes, variable_attributes));
            }
            return fmt::format(
                "\n<Grid CollectionType=\"Temporal\" GridType=\"Collection\" "
                "Name=\"Collection\">{grids}\n</Grid>\n",
                "grids"_a = fmt::join(grids, ""));
        };
    
    
        // Generator function that return a function that represents complete xdmf
        // structure
    
        auto const xdmf_writer_fn = [temporal_grid_collection_transform](
                                        auto ogs_version, auto geometry,
                                        auto topology, auto constant_attributes,
                                        auto variable_attributes)
    
            // This function will be the return of the surrounding named function
            // capture by copy - it is the function that will survive this scope!!
            // Use generalized lambda capture to move for optimization
    
            return [temporal_grid_collection_transform,
                    ogs_version = std::move(ogs_version),
    
                    geometry = std::move(geometry), topology = std::move(topology),
                    constant_attributes = std::move(constant_attributes),
                    variable_attributes = std::move(variable_attributes)](
    
    wenqing's avatar
    wenqing committed
                       std::vector<double> const& times)
            {
    
                return fmt::format(
                    "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Xdmf "
                    "xmlns:xi=\"http://www.w3.org/2001/XInclude\" "
                    "Version=\"3.0\">\n<Domain>\n<Information Name=\"OGS_VERSION\" "
                    "Value=\"{ogs_version}\"/>{grid_collection}</Domain>\n</Xdmf>",
                    "ogs_version"_a = ogs_version,
                    "grid_collection"_a = temporal_grid_collection_transform(
                        times, geometry, topology, constant_attributes,
                        variable_attributes));
            };
        };
    
        return xdmf_writer_fn(ogs_version, geometry, topology, constant_attributes,
                              variable_attributes);
    }
    }  // namespace MeshLib::IO