Files
mfem/general/adios2stream.cpp
T
Veselin Dobrev c9d9d0f8ff Various tweaks in the template + SIMD code. Make sure the SIMD
interinsics can be used when CUDA is enabled.

A few tweaks related to adios2 when building with GNU make.
2020-05-15 12:33:07 -07:00

757 lines
23 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
//
// Created on: Jan 22, 2019
// Author: William F Godoy godoywf@ornl.gov
// adios2: Adaptable Input/Output System https://github.com/ornladios/ADIOS2
#include "adios2stream.hpp"
#ifdef MFEM_USE_ADIOS2
#include "../fem/geom.hpp"
#include "../general/array.hpp"
#include "../mesh/element.hpp"
#include "../mesh/mesh.hpp"
#include "../fem/gridfunc.hpp"
#include <algorithm>
namespace mfem
{
namespace
{
// these functions might be included in adios2 upstream next release
template <class T>
adios2::Variable<T> SafeDefineVariable(adios2::IO io,
const std::string& variable_name,
const adios2::Dims& shape = adios2::Dims(),
const adios2::Dims& start = adios2::Dims(),
const adios2::Dims& count = adios2::Dims())
{
adios2::Variable<T> variable = io.InquireVariable<T>(variable_name);
if (variable)
{
if (variable.Count() != count &&
variable.ShapeID() == adios2::ShapeID::LocalArray)
{
variable.SetSelection({start, count});
}
}
else
{
variable = io.DefineVariable<T>(variable_name, shape, start, count);
}
return variable;
}
template <class T>
adios2::Attribute<T> SafeDefineAttribute(adios2::IO io,
const std::string& attribute_name,
const T& value,
const std::string& variable_name = "",
const std::string separator = "/")
{
adios2::Attribute<T> attribute = io.InquireAttribute<T>(attribute_name);
if (attribute)
{
return attribute;
}
return io.DefineAttribute<T>(attribute_name, value, variable_name, separator );
}
template <class T>
adios2::Attribute<T> SafeDefineAttribute(adios2::IO io,
const std::string& attribute_name,
const T* values, const size_t size,
const std::string& variable_name = "",
const std::string separator = "/")
{
adios2::Attribute<T> attribute = io.InquireAttribute<T>(attribute_name);
if (attribute)
{
return attribute;
}
return io.DefineAttribute<T>(attribute_name, values, size, variable_name,
separator );
}
bool SetBoolParameter(const std::string key,
const std::map<std::string, std::string>& parameters,
const bool default_value) noexcept
{
auto it = parameters.find(key);
if (it != parameters.end())
{
std::string value = it->second;
std::transform(value.begin(), value.end(), value.begin(), ::tolower);
if (value == "on" || value == "true")
{
return true;
}
else if ( value == "off" || value == "false")
{
return false;
}
}
return default_value;
}
} //end empty namespace
// PUBLIC
#ifdef MFEM_USE_MPI
adios2stream::adios2stream(const std::string& name, const openmode mode,
MPI_Comm comm, const std::string engineType)
: name(name),
adios2_openmode(mode),
adios(new adios2::ADIOS(comm)),
io(adios->DeclareIO(name))
{
io.SetEngine(engineType);
}
#else
adios2stream::adios2stream(const std::string& name, const openmode mode,
const std::string engineType)
: name(name),
adios2_openmode(mode),
adios(new adios2::ADIOS()),
io(adios->DeclareIO(name))
{
io.SetEngine(engineType);
}
#endif
adios2stream::~adios2stream()
{
if (engine)
{
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
engine.Close();
}
}
void adios2stream::SetParameters(
const std::map<std::string, std::string>& parameters)
{
io.SetParameters(parameters);
refine = SetBoolParameter("RefinedData", parameters, true);
}
void adios2stream::SetParameter(const std::string key,
const std::string value) noexcept
{
io.SetParameter(key, value);
if (key == "RefinedData")
{
refine = SetBoolParameter("RefinedData", io.Parameters(), true);
}
}
void adios2stream::BeginStep()
{
if (!engine)
{
engine = io.Open(name, adios2::Mode::Write);
}
engine.BeginStep();
active_step = true;
}
void adios2stream::EndStep()
{
if (!engine || !active_step)
{
const std::string message = "MFEM adios2stream error: calling EndStep "
"on uninitialized step (need BeginStep)";
mfem_error(message.c_str());
}
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
engine.EndStep();
active_step = false;
}
void adios2stream::SetTime(const double time)
{
adios2::Variable<double> var_time = SafeDefineVariable<double>(io, "TIME");
engine.Put(var_time, time);
transient = true;
}
void adios2stream::SetCycle(const int cycle)
{
adios2::Variable<int> var_cycle = SafeDefineVariable<int>(io,"CYCLE");
engine.Put(var_cycle, cycle);
}
void adios2stream::SetRefinementLevel(const int level) noexcept
{
refinement_level = level;
}
size_t adios2stream::CurrentStep() const
{
return engine.CurrentStep();
}
void adios2stream::Close()
{
if (engine)
{
if (!active_step)
{
SafeDefineAttribute<std::string>(io, "vtk.xml", VTKSchema() );
}
engine.Close();
}
if (adios)
{
adios.reset();
}
}
// PROTECTED (accessible by friend class Mesh)
void adios2stream::Print(const Mesh& mesh, const mode print_mode)
{
auto lf_DefineMeshMetadata = [this](Mesh& mesh)
{
// check types are constant
if (!IsConstantElementType(mesh.elements))
{
throw std::invalid_argument("MFEM::adios2stream ERROR: non-constant "
" element types not yet implemented\n");
}
// format info
SafeDefineAttribute<std::string>(io, "format", "MFEM ADIOS2 BP v0.1" );
SafeDefineAttribute<std::string>(io, "format/version", "0.1" );
std::string mesh_type = "Unknown";
std::vector<std::string> viz_tools;
viz_tools.reserve(2); //for now
if (mesh.NURBSext)
{
mesh_type = "MFEM NURBS";
viz_tools.push_back("NONE");
}
else if (mesh.ncmesh)
{
mesh_type = "MFEM mesh v1.1";
viz_tools.push_back("NONE");
}
else
{
mesh_type = "MFEM mesh v1.0";
viz_tools.push_back("Paraview: ADIOS2VTXReader");
viz_tools.push_back("VTK: vtkADIOS2VTXReader.h");
}
SafeDefineAttribute<std::string>(io, "format/mfem_mesh", mesh_type );
SafeDefineAttribute<std::string>(io, "format/viz_tools", viz_tools.data(),
viz_tools.size() );
// elements
const uint32_t dimension = static_cast<int32_t>(mesh.Dimension());
SafeDefineAttribute<uint32_t>(io, "dimension", dimension);
SafeDefineVariable<uint32_t>(io,"NumOfElements", {adios2::LocalValueDim});
SafeDefineVariable<uint32_t>(io, "types");
size_t nelements = 0;
size_t element_nvertices = 0;
size_t nvertices = 0;
if (refine)
{
for (int i = 0; i < mesh.GetNE(); ++i)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(i);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
if (refined_geometry == nullptr)
{
mfem_error("ERROR: could not refine geometry in call to Save with adios2stream \n");
}
element_nvertices = static_cast<size_t>(Geometries.GetVertices(
type)->GetNPoints());
nelements += refined_geometry->RefGeoms.Size() / element_nvertices;
nvertices += refined_geometry->RefPts.GetNPoints();
}
refined_mesh_nelements = nelements;
refined_mesh_nvertices = nvertices;
}
else
{
nelements = static_cast<size_t>(mesh.GetNE());
element_nvertices = static_cast<size_t>(mesh.elements[0]->GetNVertices());
}
SafeDefineVariable<uint64_t>(io, "connectivity", {}, {}, {nelements, element_nvertices+1});
// vertices
SafeDefineVariable<uint32_t>(io,"NumOfVertices", {adios2::LocalValueDim});
if (refine)
{
SafeDefineVariable<double>( io, "vertices", {}, {}, {nvertices, static_cast<size_t>(dimension)});
}
else
{
const GridFunction* grid_function = mesh.GetNodes();
if (grid_function == nullptr)
{
const size_t nVertices = static_cast<size_t>(mesh.GetNV());
const size_t spaceDim = static_cast<size_t>(mesh.SpaceDimension());
// similar to Ordering::byVDIM
SafeDefineVariable<double>( io, "vertices", {}, {}, {nVertices, spaceDim});
}
else
{
const size_t size = static_cast<size_t>(grid_function->Size());
const FiniteElementSpace* fes = grid_function->FESpace();
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
SafeDefineVariable<double>(io, "vertices", {}, {}, {tuples, components} );
if (fes->GetOrdering() == Ordering::byNODES)
{
ordering_by_node = true;
}
}
}
};
auto lf_PrintRefinedMeshData = [this](Mesh& mesh)
{
// elements and vertices
engine.Put("NumOfElements", static_cast<uint32_t>(refined_mesh_nelements));
engine.Put("NumOfVertices", static_cast<uint32_t>(refined_mesh_nvertices));
const uint32_t vtkType =
GLVISToVTKType(static_cast<int>(mesh.elements[0]->GetGeometryType()));
engine.Put("types", vtkType);
adios2::Variable<double> var_vertices = io.InquireVariable<double>("vertices");
adios2::Variable<double>::Span span_vertices = engine.Put<double>(var_vertices);
adios2::Variable<uint64_t> var_connectivity =
io.InquireVariable<uint64_t>("connectivity");
adios2::Variable<uint64_t>::Span span_connectivity = engine.Put<uint64_t>
(var_connectivity);
size_t span_vertices_offset = 0;
size_t span_connectivity_offset = 0;
// use for setting absolute node id for each element
size_t point_id = 0;
DenseMatrix pmatrix;
for (int e = 0; e < mesh.GetNE(); ++e)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(e);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
// vertices
mesh.GetElementTransformation(e)->Transform(refined_geometry->RefPts, pmatrix);
for (int i = 0; i < pmatrix.Width(); ++i)
{
for (int j = 0; j < pmatrix.Height(); ++j)
{
span_vertices[span_vertices_offset + i*pmatrix.Height() + j] = pmatrix(j, i);
}
}
span_vertices_offset += static_cast<size_t>(pmatrix.Width()*pmatrix.Height());
// connectivity
const int nv = Geometries.GetVertices(type)->GetNPoints();
const Array<int> &element_vertices = refined_geometry->RefGeoms;
for (int v = 0; v < element_vertices.Size();)
{
span_connectivity[span_connectivity_offset] = static_cast<uint64_t>(nv);
++span_connectivity_offset;
for (int k =0; k < nv; k++, v++ )
{
span_connectivity[span_connectivity_offset] = static_cast<uint64_t>
(point_id + element_vertices[v]);
++span_connectivity_offset;
}
}
point_id += static_cast<size_t>(refined_geometry->RefPts.GetNPoints());
}
for (int e = 0; e < mesh.GetNE(); ++e)
{
const Geometry::Type type = mesh.GetElementBaseGeometry(e);
RefinedGeometry* refined_geometry = GlobGeometryRefiner.Refine(type,
refinement_level, 1);
}
};
auto lf_PrintMeshData = [&](Mesh& mesh)
{
if (refine)
{
lf_PrintRefinedMeshData(mesh);
return;
}
// elements
engine.Put("NumOfElements", static_cast<uint32_t>(mesh.GetNE()));
const uint32_t vtkType =
GLVISToVTKType(static_cast<int>(mesh.elements[0]->GetGeometryType()));
engine.Put("types", vtkType);
adios2::Variable<uint64_t> varConnectivity =
io.InquireVariable<uint64_t>("connectivity");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<uint64_t>::Span spanConnectivity =
engine.Put<uint64_t>(varConnectivity);
size_t elementPosition = 0;
for (int e = 0; e < mesh.GetNE(); ++e)
{
const int nVertices = mesh.elements[e]->GetNVertices();
spanConnectivity[elementPosition] = nVertices;
for (int v = 0; v < nVertices; ++v)
{
spanConnectivity[elementPosition + v + 1] =
mesh.elements[e]->GetVertices()[v];
}
elementPosition += nVertices + 1;
}
// vertices
engine.Put("NumOfVertices", static_cast<uint32_t>(mesh.GetNV()));
if (mesh.GetNodes() == nullptr)
{
adios2::Variable<double> varVertices = io.InquireVariable<double>("vertices");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double>::Span spanVertices = engine.Put(varVertices);
for (int v = 0; v < mesh.GetNV(); ++v)
{
const int space_dim = mesh.SpaceDimension();
for (int coord = 0; coord < space_dim; ++coord)
{
spanVertices[v * space_dim + coord] = mesh.vertices[v](coord);
}
}
}
else
{
const GridFunction* grid_function = mesh.GetNodes();
if (ordering_by_node)
{
adios2::Variable<double> varVertices = io.InquireVariable<double>("vertices");
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double>::Span spanVertices = engine.Put(varVertices);
const size_t size = static_cast<size_t>(grid_function->Size());
const FiniteElementSpace* fes = grid_function->FESpace();
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
const double* data = grid_function->GetData();
for (size_t i = 0; i < tuples; ++i)
{
for (size_t j = 0; j < components; ++j)
{
spanVertices[i*components + j] = data[j*tuples + i];
}
}
}
else
{
grid_function->Print(*this, "vertices");
}
}
};
// BODY OF FUNCTION STARTS HERE
try
{
Mesh ref_mesh(mesh);
lf_DefineMeshMetadata(ref_mesh);
if (!engine) // if Engine is closed
{
engine = io.Open(name, adios2::Mode::Write);
}
lf_PrintMeshData(ref_mesh);
if (print_mode == mode::sync)
{
engine.PerformPuts();
}
}
catch (std::exception& e)
{
const std::string warning =
"MFEM: adios2stream exception caught, invalid bp dataset: " + name +
"," + e.what();
mfem_warning( warning.c_str());
}
}
void adios2stream::Save(const GridFunction& grid_function,
const std::string& variable_name, const data_type type)
{
auto lf_SafeDefine = [&](const std::string& variable_name,
const size_t tuples, const size_t components,
const Ordering::Type ordering,
const std::string& fespace_name)
{
adios2::Variable<double> var = io.InquireVariable<double>(variable_name);
if (!var)
{
if (components == 1 && type == adios2stream::data_type::point_data)
{
io.DefineVariable<double>(variable_name, {}, {}, {tuples*components});
}
else
{
const adios2::Dims count = (ordering == Ordering::byNODES) ?
adios2::Dims{components, tuples} :
adios2::Dims{tuples, components};
io.DefineVariable<double>(variable_name, {}, {}, count);
}
SafeDefineAttribute<std::string>(io, "FiniteElementSpace",
fespace_name, variable_name);
}
};
// BODY OF FUNCTION STARTS HERE
const std::map<std::string, std::string> parameters = io.Parameters();
const bool full_data = SetBoolParameter("FullData", parameters, false);
if (!full_data && !refine)
{
return;
}
const FiniteElementSpace* fes = grid_function.FESpace();
if (refine)
{
const Mesh *mesh = fes->GetMesh();
const size_t components = static_cast<size_t>(grid_function.VectorDim());
// const size_t tuples = static_cast<size_t>(mesh->GetNV());
const size_t tuples = refined_mesh_nvertices;
lf_SafeDefine(variable_name, tuples, components,
Ordering::byVDIM, std::string(fes->FEColl()->Name()));
if (type == adios2stream::data_type::point_data)
{
point_data_variables.insert(variable_name);
}
RefinedGeometry* refined_geometry;
DenseMatrix transform;
// zero-copy access to adios2 buffer to put non-contiguous to contiguous memory
adios2::Variable<double> variable = io.InquireVariable<double>(variable_name);
adios2::Variable<double>::Span span = engine.Put<double>(variable);
size_t offset = 0;
if (components == 1)
{
Vector scalar;
const int nelements = mesh->GetNE();
for (int e = 0; e < nelements; ++e)
{
refined_geometry = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(e), refinement_level, 1);
grid_function.GetValues(e, refined_geometry->RefPts, scalar, transform);
const int size = scalar.Size();
for (int i = 0; i < size; ++i)
{
const double value = scalar(i);
span.at(offset+i) = value;
}
offset += static_cast<size_t>(size);
}
}
else
{
DenseMatrix vector;
for (int e = 0; e < mesh->GetNE(); ++e)
{
refined_geometry = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(e), refinement_level, 1);
grid_function.GetVectorValues(e, refined_geometry->RefPts, vector, transform);
for (int i = 0; i < vector.Width(); ++i)
{
for (int j = 0; j < vector.Height(); ++j)
{
span[offset + i*vector.Height() + j] = vector(j, i);
}
}
offset += static_cast<size_t>(vector.Width()*vector.Height());
}
}
}
if (full_data)
{
const size_t size = static_cast<size_t>(grid_function.Size());
const size_t components = static_cast<size_t>(fes->GetVDim());
const size_t tuples = size /components;
lf_SafeDefine(variable_name +"/full", tuples, components,
fes->GetOrdering(),
std::string(fes->FEColl()->Name()) );
// calls Vector::Print
grid_function.Print(*this, variable_name+"/full");
if (!refine && type == adios2stream::data_type::point_data)
{
point_data_variables.insert(variable_name+"/full");
}
}
}
// PRIVATE
int32_t adios2stream::GLVISToVTKType(
const int glvisType) const noexcept
{
uint32_t vtkType = 0;
switch (glvisType)
{
case Geometry::Type::POINT:
vtkType = 1;
break;
case Geometry::Type::SEGMENT:
vtkType = 3;
break;
case Geometry::Type::TRIANGLE:
vtkType = 5;
break;
case Geometry::Type::SQUARE:
// vtkType = 8;
vtkType = 9;
break;
case Geometry::Type::TETRAHEDRON:
vtkType = 10;
break;
case Geometry::Type::CUBE:
// vtkType = 11;
vtkType = 12;
break;
case Geometry::Type::PRISM:
vtkType = 13;
break;
default:
vtkType = 0;
break;
}
return vtkType;
}
bool adios2stream::IsConstantElementType(const Array<Element*>& elements ) const
noexcept
{
bool isConstType = true;
const Geometry::Type type = elements[0]->GetGeometryType();
for (int e = 1; e < elements.Size(); ++e)
{
if (type != elements[e]->GetGeometryType())
{
isConstType = false;
break;
}
}
return isConstType;
}
std::string adios2stream::VTKSchema() const noexcept
{
std::string vtkSchema = R"(
<?xml version="1.0"?>
<VTKFile type="UnstructuredGrid" version="0.1" byte_order="LittleEndian">
<UnstructuredGrid>
<Piece NumberOfPoints="NumOfVertices" NumberOfCells="NumOfElements">
<Points>
<DataArray Name="vertices" />)";
vtkSchema += R"(
</Points>
<Cells>
<DataArray Name="connectivity" />
<DataArray Name="types" />
</Cells>
<PointData>)";
if (point_data_variables.empty())
{
vtkSchema += "\n";
}
else
{
for (const std::string& point_datum : point_data_variables )
{
vtkSchema += " <DataArray Name=\"" + point_datum +"\"/>\n";
}
}
if (transient)
{
vtkSchema += " <DataArray Name=\"TIME\">\n";
vtkSchema += " TIME\n";
vtkSchema += " </DataArray>\n";
}
vtkSchema += R"(
</PointData>
</Piece>
</UnstructuredGrid>
</VTKFile>)";
return vtkSchema;
}
adios2::Mode adios2stream::ToADIOS2Mode(const adios2stream::openmode mode) const
noexcept
{
adios2::Mode adios2Mode = adios2::Mode::Undefined;
switch (mode)
{
case adios2stream::openmode::out:
adios2Mode = adios2::Mode::Write;
break;
case adios2stream::openmode::in:
adios2Mode = adios2::Mode::Read;
break;
default:
const std::string message = "MFEM adios2stream ERROR: only "
"openmode::out and openmode::in "
" are valid, in call to adios2stream constructor";
mfem_error(message.c_str());
}
return adios2Mode;
}
} // end namespace mfem
#endif // MFEM_USE_ADIOS2