@@ -642,4 +642,138 @@ std::vector<U> read_data(std::string point_or_cell, std::string filename,
642642
643643 return values;
644644}
645+
646+ // / @brief Read a CG1 function from a VTKHDF format file.
647+ // /
648+ // / @tparam U Scalar type of mesh
649+ // / @param filename Name of the file to read from.
650+ // / @param mesh Mesh previously read from the same file.
651+ // / @param timestep The time step to read for time-dependent data.
652+ // / @return The function read from file.
653+ // /
654+ // / @note This only supports meshes with a single cell type as of now.
655+ template <std::floating_point U>
656+ fem::Function<U> read_CG1_function (std::string filename,
657+ std::shared_ptr<mesh::Mesh<U>> mesh,
658+ std::int32_t timestep = 0 )
659+ {
660+ auto element = basix::create_element<U>(
661+ basix::element::family::P,
662+ mesh::cell_type_to_basix_type (mesh->topology ()->cell_type ()), 1 ,
663+ basix::element::lagrange_variant::unset,
664+ basix::element::dpc_variant::unset, false );
665+
666+ hid_t h5file = hdf5::open_file (mesh->comm (), filename, " r" , true );
667+ std::string dataset_name = " /VTKHDF/PointData/u" ;
668+
669+ std::vector<std::int64_t > shape
670+ = hdf5::get_dataset_shape (h5file, dataset_name);
671+ hdf5::close_file (h5file);
672+
673+ const std::size_t block_size = shape[1 ];
674+
675+ auto P1
676+ = std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
677+ mesh, std::make_shared<fem::FiniteElement<U>>(
678+ element, std::vector<std::size_t >{block_size})));
679+
680+ fem::Function<U> u_in (P1);
681+
682+ std::shared_ptr<const common::IndexMap> index_map (
683+ mesh->geometry ().index_map ());
684+
685+ std::int64_t range0 = index_map->local_range ()[0 ];
686+
687+ int npoints = index_map->size_local ();
688+
689+ std::array<std::int64_t , 2 > range{range0, range0 + npoints};
690+
691+ const auto values
692+ = io::VTKHDF::read_data (" Point" , filename, *mesh, range, timestep);
693+
694+ // Parallel distribution. For vector functions we distribute each component
695+ // separately.
696+ std::vector<std::vector<double >> values_s (block_size);
697+ for (auto & v : values_s)
698+ {
699+ v.reserve (values.size () / block_size);
700+ }
701+ for (std::size_t i = 0 ; i < values.size () / block_size; ++i)
702+ {
703+ for (int j = 0 ; j < block_size; ++j)
704+ {
705+ values_s[j].push_back (values[block_size * i + j]);
706+ }
707+ }
708+
709+ std::vector<std::int64_t > entities (range[1 ] - range[0 ]);
710+ std::iota (entities.begin (), entities.end (), range[0 ]);
711+
712+ MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
713+ const std::int64_t ,
714+ MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<std::size_t , 2 >>
715+ entities_span (entities.data (),
716+ std::array<std::size_t , 2 >{entities.size (), 1 });
717+
718+ std::vector<std::pair<std::vector<std::int32_t >, std::vector<double >>>
719+ entities_values;
720+
721+ for (std::size_t i = 0 ; i < values_s.size (); ++i)
722+ {
723+ entities_values.push_back (dolfinx::io::distribute_entity_data<double >(
724+ *mesh->topology (), mesh->geometry ().input_global_indices (),
725+ mesh->geometry ().index_map ()->size_global (),
726+ mesh->geometry ().cmap ().create_dof_layout (), mesh->geometry ().dofmap (),
727+ mesh::cell_dim (mesh::CellType::point), entities_span, values_s[i]));
728+ }
729+
730+ auto num_vertices_per_cell
731+ = dolfinx::mesh::num_cell_vertices (mesh->topology ()->cell_type ());
732+ std::vector<std::int32_t > local_vertex_map (num_vertices_per_cell);
733+
734+ for (int i = 0 ; i < num_vertices_per_cell; ++i)
735+ {
736+ const auto v_to_d
737+ = u_in.function_space ()->dofmap ()->element_dof_layout ().entity_dofs (0 ,
738+ i);
739+ assert (v_to_d.size () == 1 );
740+ local_vertex_map[i] = v_to_d.front ();
741+ }
742+
743+ const auto tdim = mesh->topology ()->dim ();
744+ const auto c_to_v = mesh->topology ()->connectivity (tdim, 0 );
745+ std::vector<std::int32_t > vertex_to_dofmap (
746+ mesh->topology ()->index_map (0 )->size_local ()
747+ + mesh->topology ()->index_map (0 )->num_ghosts ());
748+
749+ for (int i = 0 ; i < mesh->topology ()->index_map (tdim)->size_local (); ++i)
750+ {
751+ const auto local_vertices = c_to_v->links (i);
752+ const auto local_dofs = u_in.function_space ()->dofmap ()->cell_dofs (i);
753+ for (int j = 0 ; j < num_vertices_per_cell; ++j)
754+ {
755+ vertex_to_dofmap[local_vertices[j]] = local_dofs[local_vertex_map[j]];
756+ }
757+ }
758+
759+ /*
760+ * After the data is read and distributed, we need to place the
761+ * retrieved values in the correct position in the function's array,
762+ * reading values and positions from `entities_values`.
763+ */
764+ for (std::size_t i = 0 ; i < entities_values[0 ].first .size (); ++i)
765+ {
766+ for (std::size_t j = 0 ; j < block_size; ++j)
767+ {
768+ u_in.x ()
769+ ->array ()[block_size * vertex_to_dofmap[entities_values[0 ].first [i]]
770+ + j]
771+ = entities_values[j].second [i];
772+ }
773+ }
774+
775+ u_in.x ()->scatter_fwd ();
776+
777+ return u_in;
778+ }
645779} // namespace dolfinx::io::VTKHDF
0 commit comments