@@ -297,6 +297,144 @@ XDMFFile::write_function(const fem::Function<std::complex<double>, double>&,
297
297
double , std::string);
298
298
// / @endcond
299
299
// -----------------------------------------------------------------------------
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
+ // -----------------------------------------------------------------------------
300
438
template <typename U, std::floating_point T>
301
439
void XDMFFile::write_meshtags (const mesh::MeshTags<U>& meshtags,
302
440
const mesh::Geometry<T>& x,
0 commit comments