@@ -578,4 +578,138 @@ std::vector<U> read_data(std::string point_or_cell, std::string filename,
578578
579579 return values;
580580}
581+
582+ // / @brief Read a CG1 function from a VTKHDF format file.
583+ // /
584+ // / @tparam U Scalar type of mesh
585+ // / @param filename Name of the file to read from.
586+ // / @param mesh Mesh previously read from the same file.
587+ // / @param timestep The time step to read for time-dependent data.
588+ // / @return The function read from file.
589+ // /
590+ // / @note This only supports meshes with a single cell type as of now.
591+ template <std::floating_point U>
592+ fem::Function<U> read_CG1_function (std::string filename,
593+ std::shared_ptr<mesh::Mesh<U>> mesh,
594+ std::int32_t timestep = 0 )
595+ {
596+ auto element = basix::create_element<U>(
597+ basix::element::family::P,
598+ mesh::cell_type_to_basix_type (mesh->topology ()->cell_type ()), 1 ,
599+ basix::element::lagrange_variant::unset,
600+ basix::element::dpc_variant::unset, false );
601+
602+ hid_t h5file = hdf5::open_file (mesh->comm (), filename, " r" , true );
603+ std::string dataset_name = " /VTKHDF/PointData/u" ;
604+
605+ std::vector<std::int64_t > shape
606+ = hdf5::get_dataset_shape (h5file, dataset_name);
607+ hdf5::close_file (h5file);
608+
609+ const std::size_t block_size = shape[1 ];
610+
611+ auto P1
612+ = std::make_shared<fem::FunctionSpace<U>>(fem::create_functionspace<U>(
613+ mesh, std::make_shared<fem::FiniteElement<U>>(
614+ element, std::vector<std::size_t >{block_size})));
615+
616+ fem::Function<U> u_in (P1);
617+
618+ std::shared_ptr<const common::IndexMap> index_map (
619+ mesh->geometry ().index_map ());
620+
621+ std::int64_t range0 = index_map->local_range ()[0 ];
622+
623+ int npoints = index_map->size_local ();
624+
625+ std::array<std::int64_t , 2 > range{range0, range0 + npoints};
626+
627+ const auto values
628+ = io::VTKHDF::read_data (" Point" , filename, *mesh, range, timestep);
629+
630+ // Parallel distribution. For vector functions we distribute each component
631+ // separately.
632+ std::vector<std::vector<double >> values_s (block_size);
633+ for (auto & v : values_s)
634+ {
635+ v.reserve (values.size () / block_size);
636+ }
637+ for (std::size_t i = 0 ; i < values.size () / block_size; ++i)
638+ {
639+ for (int j = 0 ; j < block_size; ++j)
640+ {
641+ values_s[j].push_back (values[block_size * i + j]);
642+ }
643+ }
644+
645+ std::vector<std::int64_t > entities (range[1 ] - range[0 ]);
646+ std::iota (entities.begin (), entities.end (), range[0 ]);
647+
648+ MDSPAN_IMPL_STANDARD_NAMESPACE::mdspan<
649+ const std::int64_t ,
650+ MDSPAN_IMPL_STANDARD_NAMESPACE::dextents<std::size_t , 2 >>
651+ entities_span (entities.data (),
652+ std::array<std::size_t , 2 >{entities.size (), 1 });
653+
654+ std::vector<std::pair<std::vector<std::int32_t >, std::vector<double >>>
655+ entities_values;
656+
657+ for (std::size_t i = 0 ; i < values_s.size (); ++i)
658+ {
659+ entities_values.push_back (dolfinx::io::distribute_entity_data<double >(
660+ *mesh->topology (), mesh->geometry ().input_global_indices (),
661+ mesh->geometry ().index_map ()->size_global (),
662+ mesh->geometry ().cmap ().create_dof_layout (), mesh->geometry ().dofmap (),
663+ mesh::cell_dim (mesh::CellType::point), entities_span, values_s[i]));
664+ }
665+
666+ auto num_vertices_per_cell
667+ = dolfinx::mesh::num_cell_vertices (mesh->topology ()->cell_type ());
668+ std::vector<std::int32_t > local_vertex_map (num_vertices_per_cell);
669+
670+ for (int i = 0 ; i < num_vertices_per_cell; ++i)
671+ {
672+ const auto v_to_d
673+ = u_in.function_space ()->dofmap ()->element_dof_layout ().entity_dofs (0 ,
674+ i);
675+ assert (v_to_d.size () == 1 );
676+ local_vertex_map[i] = v_to_d.front ();
677+ }
678+
679+ const auto tdim = mesh->topology ()->dim ();
680+ const auto c_to_v = mesh->topology ()->connectivity (tdim, 0 );
681+ std::vector<std::int32_t > vertex_to_dofmap (
682+ mesh->topology ()->index_map (0 )->size_local ()
683+ + mesh->topology ()->index_map (0 )->num_ghosts ());
684+
685+ for (int i = 0 ; i < mesh->topology ()->index_map (tdim)->size_local (); ++i)
686+ {
687+ const auto local_vertices = c_to_v->links (i);
688+ const auto local_dofs = u_in.function_space ()->dofmap ()->cell_dofs (i);
689+ for (int j = 0 ; j < num_vertices_per_cell; ++j)
690+ {
691+ vertex_to_dofmap[local_vertices[j]] = local_dofs[local_vertex_map[j]];
692+ }
693+ }
694+
695+ /*
696+ * After the data is read and distributed, we need to place the
697+ * retrieved values in the correct position in the function's array,
698+ * reading values and positions from `entities_values`.
699+ */
700+ for (std::size_t i = 0 ; i < entities_values[0 ].first .size (); ++i)
701+ {
702+ for (std::size_t j = 0 ; j < block_size; ++j)
703+ {
704+ u_in.x ()
705+ ->array ()[block_size * vertex_to_dofmap[entities_values[0 ].first [i]]
706+ + j]
707+ = entities_values[j].second [i];
708+ }
709+ }
710+
711+ u_in.x ()->scatter_fwd ();
712+
713+ return u_in;
714+ }
581715} // namespace dolfinx::io::VTKHDF
0 commit comments