DOLFINx 0.12.0.0
DOLFINx C++
Loading...
Searching...
No Matches
utils.h
1// Copyright (C) 2012-2020 Chris Richardson
2//
3// This file is part of DOLFINx (https://www.fenicsproject.org)
4//
5// SPDX-License-Identifier: LGPL-3.0-or-later
6
7#pragma once
8
9#include <array>
10#include <concepts>
11#include <cstdint>
12#include <dolfinx/common/MPI.h>
13#include <dolfinx/common/log.h>
14#include <dolfinx/graph/AdjacencyList.h>
15#include <dolfinx/mesh/Mesh.h>
16#include <dolfinx/mesh/utils.h>
17#include <memory>
18#include <set>
19#include <span>
20#include <tuple>
21#include <vector>
22
23namespace dolfinx::mesh
24{
25template <typename T>
26class MeshTags;
27class Topology;
28enum class GhostMode : std::uint8_t;
29} // namespace dolfinx::mesh
30
31namespace dolfinx::common
32{
33class IndexMap;
34} // namespace dolfinx::common
35
36namespace dolfinx::refinement
37{
38namespace impl
39{
41std::int64_t local_to_global(std::int32_t local_index,
42 const common::IndexMap& map);
43
52template <std::floating_point T>
53std::pair<std::vector<T>, std::array<std::size_t, 2>>
54create_new_geometry(const mesh::Mesh<T>& mesh,
55 const std::vector<std::int32_t>& marked_edge_list)
56{
57 // Build map from vertex -> geometry dof
58 auto x_dofmap = mesh.geometry().dofmaps().front();
59 const int tdim = mesh.topology()->dim();
60 auto c_to_v = mesh.topology()->connectivity(tdim, 0);
61 assert(c_to_v);
62 auto map_v = mesh.topology()->index_map(0);
63 assert(map_v);
64 std::vector<std::int32_t> vertex_to_x(map_v->size_local()
65 + map_v->num_ghosts());
66 auto map_c = mesh.topology()->index_map(tdim);
67
68 assert(map_c);
69 auto dof_layout = mesh.geometry().cmaps().front().create_dof_layout();
70 auto entity_dofs_all = dof_layout.entity_dofs_all();
71 for (int c = 0; c < map_c->size_local() + map_c->num_ghosts(); ++c)
72 {
73 auto vertices = c_to_v->links(c);
74 auto dofs = md::submdspan(x_dofmap, c, md::full_extent);
75 for (std::size_t i = 0; i < vertices.size(); ++i)
76 {
77 auto vertex_pos = entity_dofs_all[0][i][0];
78 vertex_to_x[vertices[i]] = dofs[vertex_pos];
79 }
80 }
81
82 // Copy over existing mesh vertices
83 std::span<const T> x_g = mesh.geometry().x();
84 const std::size_t gdim = mesh.geometry().dim();
85 const std::size_t num_vertices = map_v->size_local();
86 const std::size_t num_new_vertices = marked_edge_list.size();
87
88 std::array<std::size_t, 2> shape = {num_vertices + num_new_vertices, gdim};
89 std::vector<T> new_vertex_coords(shape[0] * shape[1]);
90
91 // Copy existing vertices
92 for (std::size_t v = 0; v < num_vertices; ++v)
93 {
94 std::size_t pos = 3 * vertex_to_x[v];
95 for (std::size_t j = 0; j < gdim; ++j)
96 new_vertex_coords[gdim * v + j] = x_g[pos + j];
97 }
98 spdlog::info("Copied {} existing vertices", num_vertices);
99
100 // Compute new vertices on edges
101 if (num_new_vertices > 0)
102 {
103 // Compute midpoint of each edge (padded to 3D)
104 const std::vector<T> midpoints
105 = mesh::compute_midpoints(mesh, 1, marked_edge_list);
106 for (std::size_t i = 0; i < num_new_vertices; ++i)
107 for (std::size_t j = 0; j < gdim; ++j)
108 new_vertex_coords[gdim * (num_vertices + i) + j] = midpoints[3 * i + j];
109 }
110 spdlog::info("Created {} new vertices", num_new_vertices);
111
112 return {std::move(new_vertex_coords), shape};
113}
114
115} // namespace impl
116
127 MPI_Comm comm,
128 const std::vector<std::vector<std::int32_t>>& marked_for_update,
129 std::span<std::int8_t> marked_edges, const common::IndexMap& map);
130
145template <std::floating_point T>
146std::tuple<graph::AdjacencyList<std::int64_t>, std::vector<T>,
147 std::array<std::size_t, 2>>
149 const graph::AdjacencyList<int>& shared_edges,
150 const mesh::Mesh<T>& mesh,
151 std::span<const std::int8_t> marked_edges)
152{
153 // Take marked_edges and use to create new vertices
154 std::shared_ptr<const common::IndexMap> edge_index_map
155 = mesh.topology()->index_map(1);
156
157 // Add new edge midpoints to list of vertices. The new vertex will be owned by
158 // the process owning the edge.
159 std::vector<std::int32_t> marked_edge_list;
160 for (int local_i = 0; local_i < edge_index_map->size_local(); ++local_i)
161 {
162 if (marked_edges[local_i])
163 marked_edge_list.push_back(local_i);
164 }
165 spdlog::info("Marked edge list has {} entries", marked_edge_list.size());
166
167 // Create actual points
168 auto [new_vertex_coords, xshape]
169 = impl::create_new_geometry(mesh, marked_edge_list);
170
171 const std::int64_t num_local = marked_edge_list.size();
172 std::int64_t global_offset = 0;
173 MPI_Exscan(&num_local, &global_offset, 1, MPI_INT64_T, MPI_SUM, mesh.comm());
174 global_offset += mesh.topology()->index_map(0)->local_range()[1];
175 std::vector<std::int64_t> local_edge_to_new_vertex(marked_edge_list.size());
176 std::iota(local_edge_to_new_vertex.begin(), local_edge_to_new_vertex.end(),
177 global_offset);
178
179 spdlog::info("Marked edge global offset = {}", global_offset);
180
181 // If they are shared, then the new global vertex index needs to be
182 // sent off-process.
183
184 // Get number of neighbors
185 int indegree(-1), outdegree(-2), weighted(-1);
186 MPI_Dist_graph_neighbors_count(comm, &indegree, &outdegree, &weighted);
187 assert(indegree == outdegree);
188 const int num_neighbors = indegree;
189
190 std::vector<std::vector<std::int64_t>> values_to_send(num_neighbors);
191 for (std::size_t i = 0; i < marked_edge_list.size(); ++i)
192 {
193 // shared, but locally owned : remote owned are not in list.
194 for (int remote_process : shared_edges.links(marked_edge_list[i]))
195 {
196 // Send (global edge index) -> (new global vertex index) map
197 values_to_send[remote_process].push_back(
198 impl::local_to_global(marked_edge_list[i], *edge_index_map));
199 values_to_send[remote_process].push_back(local_edge_to_new_vertex[i]);
200 }
201 }
202
203 // Send all shared edges marked for update and receive from other
204 // processes
205 std::vector<std::int64_t> received_values;
206 {
207 std::vector<std::int64_t> send_buffer;
208 std::vector<int> send_sizes;
209 for (auto& x : values_to_send)
210 {
211 send_sizes.push_back(x.size());
212 send_buffer.insert(send_buffer.end(), x.begin(), x.end());
213 }
214 assert((int)send_sizes.size() == outdegree);
215
216 std::vector<int> recv_sizes(outdegree);
217 send_sizes.reserve(1);
218 recv_sizes.reserve(1);
219 MPI_Neighbor_alltoall(send_sizes.data(), 1, MPI_INT, recv_sizes.data(), 1,
220 MPI_INT, comm);
221
222 // Build displacements
223 std::vector<int> send_disp{0};
224 std::partial_sum(send_sizes.begin(), send_sizes.end(),
225 std::back_inserter(send_disp));
226 std::vector<int> recv_disp{0};
227 std::partial_sum(recv_sizes.begin(), recv_sizes.end(),
228 std::back_inserter(recv_disp));
229
230 received_values.resize(recv_disp.back());
231 MPI_Neighbor_alltoallv(send_buffer.data(), send_sizes.data(),
232 send_disp.data(), MPI_INT64_T,
233 received_values.data(), recv_sizes.data(),
234 recv_disp.data(), MPI_INT64_T, comm);
235 }
236
237 // Add received remote global vertex indices to map
238 {
239 assert(received_values.size() % 2 == 0);
240 std::vector<std::int64_t> recv_global_edge(received_values.size() / 2);
241 for (std::size_t i = 0; i < recv_global_edge.size(); ++i)
242 recv_global_edge[i] = received_values[i * 2];
243 std::vector<std::int32_t> recv_local_edge(recv_global_edge.size());
244 mesh.topology()->index_map(1)->global_to_local(recv_global_edge,
245 recv_local_edge);
246 for (std::size_t i = 0; i < recv_global_edge.size(); ++i)
247 {
248 assert(recv_local_edge[i] != -1);
249 marked_edge_list.push_back(recv_local_edge[i]);
250 local_edge_to_new_vertex.push_back(received_values[i * 2 + 1]);
251 }
252 }
253
254 // Compile marked_edge_list and local_edge_to_new_vertex into an
255 // AdjacencyList
256 // Permute data into ascending order of marked_edge_list
257 std::vector<std::int32_t> perm(marked_edge_list.size());
258 std::iota(perm.begin(), perm.end(), 0);
259 std::ranges::sort(perm, std::less{},
260 [&marked_edge_list](int i) { return marked_edge_list[i]; });
261 std::vector<std::int64_t> data(local_edge_to_new_vertex.size());
262 for (std::size_t i = 0; i < perm.size(); ++i)
263 data[i] = local_edge_to_new_vertex[perm[i]];
264
265 // Compute offset for marked edges
266 std::vector<std::int32_t> offset(
267 edge_index_map->size_local() + edge_index_map->num_ghosts() + 1, 0);
268 for (auto m : marked_edge_list)
269 offset[m + 1] = 1;
270 std::partial_sum(offset.begin(), offset.end(), offset.begin());
271
272 graph::AdjacencyList<std::int64_t> local_edge_to_new_vertex_adj(data, offset);
273 return {std::move(local_edge_to_new_vertex_adj), std::move(new_vertex_coords),
274 xshape};
275}
276
289std::vector<std::int64_t> adjust_indices(const common::IndexMap& map,
290 std::int32_t n);
291
304std::array<std::vector<std::int32_t>, 2> transfer_facet_meshtag(
305 const mesh::MeshTags<std::int32_t>& tags0, const mesh::Topology& topology1,
306 std::span<const std::int32_t> cell, std::span<const std::int8_t> facet);
307
319std::array<std::vector<std::int32_t>, 2>
321 const mesh::Topology& topology1,
322 std::span<const std::int32_t> parent_cell);
323
324} // namespace dolfinx::refinement
Distribution of a global index range [0, N) across MPI ranks.
Definition IndexMap.h:114
This class provides a static adjacency list data structure.
Definition AdjacencyList.h:41
std::span< LinkData > links(std::size_t node)
Get the links (edges) for given node.
Definition AdjacencyList.h:169
MeshTags associate values with mesh topology entities.
Definition MeshTags.h:37
A Mesh consists of a set of connected and numbered mesh topological entities, and geometry data.
Definition Mesh.h:25
Topology stores the topology of a mesh, consisting of mesh entities and connectivity (incidence relat...
Definition Topology.h:49
Functions supporting mesh operations.
Miscellaneous classes, functions and types.
Definition dolfinx_common.h:8
Mesh data structures and algorithms on meshes.
Definition DofMap.h:32
std::vector< T > compute_midpoints(const Mesh< T > &mesh, int dim, std::span< const std::int32_t > entities)
Compute the midpoints for mesh entities of a given dimension.
Definition utils.h:474
GhostMode
Enum for different partitioning ghost modes.
Definition types.h:19
Mesh refinement algorithms.
Definition dolfinx_refinement.h:8
std::array< std::vector< std::int32_t >, 2 > transfer_facet_meshtag(const mesh::MeshTags< std::int32_t > &tags0, const mesh::Topology &topology1, std::span< const std::int32_t > cell, std::span< const std::int8_t > facet)
Transfer facet MeshTags from coarse mesh to refined mesh.
Definition utils.cpp:153
std::tuple< graph::AdjacencyList< std::int64_t >, std::vector< T >, std::array< std::size_t, 2 > > create_new_vertices(MPI_Comm comm, const graph::AdjacencyList< int > &shared_edges, const mesh::Mesh< T > &mesh, std::span< const std::int8_t > marked_edges)
Add new vertex for each marked edge, and create new_vertex_coordinates and global_edge->new_vertex ma...
Definition utils.h:148
@ parent_cell
Definition option.h:20
std::vector< std::int64_t > adjust_indices(const common::IndexMap &map, std::int32_t n)
Given an index map, add "n" extra indices at the end of local range.
Definition utils.cpp:103
std::array< std::vector< std::int32_t >, 2 > transfer_cell_meshtag(const mesh::MeshTags< std::int32_t > &tags0, const mesh::Topology &topology1, std::span< const std::int32_t > parent_cell)
Transfer cell MeshTags from coarse mesh to refined mesh.
Definition utils.cpp:275
void update_logical_edgefunction(MPI_Comm comm, const std::vector< std::vector< std::int32_t > > &marked_for_update, std::span< std::int8_t > marked_edges, const common::IndexMap &map)
Communicate edge markers between processes that share edges.
Definition utils.cpp:47