Skip to content

Commit 9005a9a

Browse files
committed
Add XDMFFile::read_function to read P1
1 parent d699dd7 commit 9005a9a

File tree

3 files changed

+149
-1
lines changed

3 files changed

+149
-1
lines changed

cpp/dolfinx/fem/assembler.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ void assemble_matrix(
310310
/// @param[in] mat_add The function for adding values into the matrix
311311
/// @param[in] a The bilinear from to assemble
312312
/// @param[in] bcs Boundary conditions to apply. For boundary condition
313-
/// dofs the row and column are zeroed. The diagonal entry is not set.
313+
/// dofs the row and column are zeroed. The diagonal entry is not set.
314314
template <dolfinx::scalar T, std::floating_point U>
315315
void assemble_matrix(
316316
auto mat_add, const Form<T, U>& a,

cpp/dolfinx/io/XDMFFile.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,144 @@ XDMFFile::write_function(const fem::Function<std::complex<double>, double>&,
297297
double, std::string);
298298
/// @endcond
299299
//-----------------------------------------------------------------------------
300+
void XDMFFile::read_function(const mesh::Mesh<double>& mesh, std::string name,
301+
fem::Function<double, double>& u,
302+
std::optional<std::string> function_name,
303+
std::string xpath)
304+
{
305+
/*
306+
* This routine reads a function from file. The implementation closely
307+
* follows `XDMFFile::read_meshtags` below.
308+
*
309+
* For reference, we are reading an xdmf file whose header is akin to
310+
*
311+
* <Xdmf Version="3.0">
312+
* <Domain>
313+
* <Grid Name="Grid">
314+
* <Geometry GeometryType="XYZ">
315+
* <DataItem DataType="Float" Dimensions="23103 3" Format="HDF"
316+
* Precision="4"> power.h5:/data0</DataItem>
317+
* </Geometry>
318+
* <Topology TopologyType="Hexahedron" NumberOfElements="15000"
319+
* NodesPerElement="8"> <DataItem DataType="Int" Dimensions="15000 8"
320+
* Format="HDF" Precision="8"> power.h5:/data1</DataItem>
321+
* </Topology>
322+
* <Attribute Name="wall power density" AttributeType="Scalar"
323+
* Center="Node"> <DataItem DataType="Float" Dimensions="23103" Format="HDF"
324+
* Precision="8"> power.h5:/data2</DataItem>
325+
* </Attribute>
326+
* </Grid>
327+
* </Domain>
328+
* </Xdmf>
329+
*
330+
* and that was generated saving a vtu file from Paraview and converting it
331+
* to xdmf with meshio.
332+
*
333+
* The goal for now is to read a P1 function, so the degrees of freedom are
334+
* the vertexes of the mesh.
335+
*/
336+
spdlog::info("XDMF read function ({})", name);
337+
pugi::xml_node node = _xml_doc->select_node(xpath.c_str()).node();
338+
if (!node)
339+
throw std::runtime_error("XML node '" + xpath + "' not found.");
340+
pugi::xml_node grid_node
341+
= node.select_node(("Grid[@Name='" + name + "']").c_str()).node();
342+
if (!grid_node)
343+
throw std::runtime_error("<Grid> with name '" + name + "' not found.");
344+
345+
pugi::xml_node values_data_node
346+
= grid_node.child("Attribute").child("DataItem");
347+
if (function_name)
348+
{
349+
// Search for a child that contains an attribute with the requested name
350+
pugi::xml_node function_node = grid_node.find_child(
351+
[fun_name = *function_name](auto n)
352+
{ return n.attribute("Name").value() == fun_name; });
353+
if (!function_node)
354+
{
355+
throw std::runtime_error("Function with name '" + *function_name
356+
+ "' not found.");
357+
}
358+
else
359+
values_data_node = function_node.child("DataItem");
360+
}
361+
const std::vector values
362+
= xdmf_utils::get_dataset<double>(_comm.comm(), values_data_node, _h5_id);
363+
364+
/*
365+
* Reading the cell type would read "hexahedron", so we set this
366+
* manually to "point" instead.
367+
*/
368+
mesh::CellType cell_type = mesh::CellType::point;
369+
370+
/*
371+
* The `entities1` vector contains the global indexes of the entities
372+
* [aka points] that this process owns.
373+
*/
374+
std::int64_t num_xnodes = mesh.geometry().index_map()->size_global();
375+
auto range
376+
= dolfinx::MPI::local_range(dolfinx::MPI::rank(mesh.comm()), num_xnodes,
377+
dolfinx::MPI::size(mesh.comm()));
378+
std::vector<std::int64_t> entities1(range[1] - range[0]);
379+
std::iota(entities1.begin(), entities1.end(), range[0]);
380+
381+
std::array<std::size_t, 2> shape
382+
= {static_cast<std::size_t>(range[1] - range[0]), 1};
383+
384+
MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
385+
const std::int64_t,
386+
MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<std::size_t, 2>>
387+
entities_span(entities1.data(), shape);
388+
389+
std::pair<std::vector<std::int32_t>, std::vector<double>> entities_values
390+
= xdmf_utils::distribute_entity_data<double>(
391+
*mesh.topology(), mesh.geometry().input_global_indices(),
392+
mesh.geometry().index_map()->size_global(),
393+
mesh.geometry().cmap().create_dof_layout(), mesh.geometry().dofmap(),
394+
mesh::cell_dim(cell_type), entities_span, values);
395+
396+
auto num_vertices_per_cell
397+
= dolfinx::mesh::num_cell_vertices(mesh.topology()->cell_type());
398+
std::vector<std::int32_t> local_vertex_map(num_vertices_per_cell);
399+
400+
for (int i = 0; i < num_vertices_per_cell; ++i)
401+
{
402+
const auto v_to_d
403+
= u.function_space()->dofmap()->element_dof_layout().entity_dofs(0, i);
404+
assert(v_to_d.size() == 1);
405+
local_vertex_map[i] = v_to_d.front();
406+
}
407+
408+
const auto tdim = mesh.topology()->dim();
409+
const auto c_to_v = mesh.topology()->connectivity(tdim, 0);
410+
std::vector<std::int32_t> vertex_to_dofmap(
411+
mesh.topology()->index_map(0)->size_local()
412+
+ mesh.topology()->index_map(0)->num_ghosts());
413+
414+
for (int i = 0; i < mesh.topology()->index_map(tdim)->size_local(); ++i)
415+
{
416+
const auto local_vertices = c_to_v->links(i);
417+
const auto local_dofs = u.function_space()->dofmap()->cell_dofs(i);
418+
for (int j = 0; j < num_vertices_per_cell; ++j)
419+
{
420+
vertex_to_dofmap[local_vertices[j]] = local_dofs[local_vertex_map[j]];
421+
}
422+
}
423+
424+
/*
425+
* After the data is read and distributed, we need to place the
426+
* retrieved values in the correct position in the function's array,
427+
* reading values and positions from `entities_values`.
428+
*/
429+
for (size_t i = 0; i < entities_values.first.size(); ++i)
430+
{
431+
u.x()->mutable_array()[vertex_to_dofmap[entities_values.first[i]]]
432+
= entities_values.second[i];
433+
}
434+
435+
u.x()->scatter_fwd();
436+
}
437+
//-----------------------------------------------------------------------------
300438
template <typename U, std::floating_point T>
301439
void XDMFFile::write_meshtags(const mesh::MeshTags<U>& meshtags,
302440
const mesh::Geometry<T>& x,

cpp/dolfinx/io/XDMFFile.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ class XDMFFile
190190
std::string mesh_xpath
191191
= "/Xdmf/Domain/Grid[@GridType='Uniform'][1]");
192192

193+
/// Read Function
194+
/// @param[in] mesh The Mesh that the data is defined on
195+
/// @param[in] name
196+
/// @param[out] u The function into which to read data
197+
/// @param[in] function_name The (optional) name of the function to read from file
198+
/// @param[in] xpath XPath where MeshFunction Grid is stored in file
199+
void read_function(const mesh::Mesh<double>& mesh, std::string name,
200+
fem::Function<double, double>& u, std::optional<std::string> function_name,
201+
std::string xpath = "/Xdmf/Domain");
202+
193203
/// Write MeshTags
194204
/// @param[in] meshtags
195205
/// @param[in] x Mesh geometry

0 commit comments

Comments
 (0)