Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
domain.cpp
1 // Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
29 
30 namespace serac {
31 
38 template <int d>
39 std::vector<tensor<double, d>> gather(const mfem::Vector& coordinates, mfem::Array<int> ids)
40 {
41  int num_vertices = coordinates.Size() / d;
42  std::vector<tensor<double, d>> x(std::size_t(ids.Size()));
43  for (int v = 0; v < ids.Size(); v++) {
44  for (int j = 0; j < d; j++) {
45  x[uint32_t(v)][j] = coordinates[j * num_vertices + ids[v]];
46  }
47  }
48  return x;
49 }
50 
51 template <int d>
52 static Domain domain_of_vertices(const mfem::Mesh& mesh, std::function<bool(tensor<double, d>)> predicate)
53 {
54  assert(mesh.SpaceDimension() == d);
55 
56  Domain output{mesh, 0 /* points are 0-dimensional */};
57 
58  // layout is undocumented, but it seems to be
59  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
60  mfem::Vector vertices;
61  mesh.GetVertices(vertices);
62 
63  // vertices that satisfy the predicate are added to the domain
64  int num_vertices = mesh.GetNV();
65  for (int i = 0; i < num_vertices; i++) {
66  tensor<double, d> x;
67  for (int j = 0; j < d; j++) {
68  x[j] = vertices[j * num_vertices + i];
69  }
70 
71  if (predicate(x)) {
72  output.vertex_ids_.push_back(i);
73  }
74  }
75 
76  return output;
77 }
78 
79 Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function<bool(vec2)> func)
80 {
81  return domain_of_vertices(mesh, func);
82 }
83 
84 Domain Domain::ofVertices(const mfem::Mesh& mesh, std::function<bool(vec3)> func)
85 {
86  return domain_of_vertices(mesh, func);
87 }
88 
91 
92 template <int d, typename T>
93 static Domain domain_of_edges(const mfem::Mesh& mesh, std::function<T> predicate)
94 {
95  assert(mesh.SpaceDimension() == d);
96 
97  Domain output{mesh, 1 /* edges are 1-dimensional */};
98 
99  // layout is undocumented, but it seems to be
100  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
101  mfem::Vector vertices;
102  mesh.GetVertices(vertices);
103 
104  mfem::Array<int> edge_id_to_bdr_id;
105  if (d == 2) {
106  edge_id_to_bdr_id = mesh.GetFaceToBdrElMap();
107  }
108 
109  int num_edges = mesh.GetNEdges();
110  for (int i = 0; i < num_edges; i++) {
111  mfem::Array<int> vertex_ids;
112  mesh.GetEdgeVertices(i, vertex_ids);
113 
114  auto x = gather<d>(vertices, vertex_ids);
115 
116  if constexpr (d == 2) {
117  int bdr_id = edge_id_to_bdr_id[i];
118  int attr = (bdr_id > 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
119  if (predicate(x, attr)) {
120  output.edge_ids_.push_back(i);
121  }
122  } else {
123  if (predicate(x)) {
124  output.edge_ids_.push_back(i);
125  }
126  }
127  }
128 
129  return output;
130 }
131 
132 Domain Domain::ofEdges(const mfem::Mesh& mesh, std::function<bool(std::vector<vec2>, int)> func)
133 {
134  return domain_of_edges<2>(mesh, func);
135 }
136 
137 Domain Domain::ofEdges(const mfem::Mesh& mesh, std::function<bool(std::vector<vec3>)> func)
138 {
139  return domain_of_edges<3>(mesh, func);
140 }
141 
144 
145 template <int d>
146 static Domain domain_of_faces(const mfem::Mesh& mesh,
147  std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
148 {
149  assert(mesh.SpaceDimension() == d);
150 
151  Domain output{mesh, 2 /* faces are 2-dimensional */};
152 
153  // layout is undocumented, but it seems to be
154  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
155  mfem::Vector vertices;
156  mesh.GetVertices(vertices);
157 
158  mfem::Array<int> face_id_to_bdr_id;
159  if (d == 3) {
160  face_id_to_bdr_id = mesh.GetFaceToBdrElMap();
161  }
162 
163  // faces that satisfy the predicate are added to the domain
164  int num_faces;
165  if (d == 2) {
166  num_faces = mesh.GetNE();
167  } else {
168  num_faces = mesh.GetNumFaces();
169  }
170 
171  int tri_id = 0;
172  int quad_id = 0;
173 
174  for (int i = 0; i < num_faces; i++) {
175  mfem::Array<int> vertex_ids;
176 
177  if (mesh.Dimension() == 2) {
178  mesh.GetElementVertices(i, vertex_ids);
179  } else {
180  mesh.GetFaceVertices(i, vertex_ids);
181  }
182 
183  auto x = gather<d>(vertices, vertex_ids);
184 
185  int attr;
186  if (d == 2) {
187  attr = mesh.GetAttribute(i);
188  } else {
189  int bdr_id = face_id_to_bdr_id[i];
190  attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
191  }
192 
193  if (predicate(x, attr)) {
194  if (x.size() == 3) {
195  output.tri_ids_.push_back(tri_id);
196  }
197  if (x.size() == 4) {
198  output.quad_ids_.push_back(quad_id);
199  }
200  }
201 
202  if (x.size() == 3) {
203  tri_id++;
204  }
205  if (x.size() == 4) {
206  quad_id++;
207  }
208  }
209 
210  return output;
211 }
212 
213 Domain Domain::ofFaces(const mfem::Mesh& mesh, std::function<bool(std::vector<vec2>, int)> func)
214 {
215  return domain_of_faces(mesh, func);
216 }
217 
218 Domain Domain::ofFaces(const mfem::Mesh& mesh, std::function<bool(std::vector<vec3>, int)> func)
219 {
220  return domain_of_faces(mesh, func);
221 }
222 
225 
226 template <int d>
227 static Domain domain_of_elems(const mfem::Mesh& mesh,
228  std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
229 {
230  assert(mesh.SpaceDimension() == d);
231 
232  Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */};
233 
234  // layout is undocumented, but it seems to be
235  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
236  mfem::Vector vertices;
237  mesh.GetVertices(vertices);
238 
239  int tri_id = 0;
240  int quad_id = 0;
241  int tet_id = 0;
242  int hex_id = 0;
243 
244  // elements that satisfy the predicate are added to the domain
245  int num_elems = mesh.GetNE();
246  for (int i = 0; i < num_elems; i++) {
247  mfem::Array<int> vertex_ids;
248  mesh.GetElementVertices(i, vertex_ids);
249 
250  auto x = gather<d>(vertices, vertex_ids);
251 
252  bool add = predicate(x, mesh.GetAttribute(i));
253 
254  switch (x.size()) {
255  case 3:
256  if (add) {
257  output.tri_ids_.push_back(tri_id);
258  }
259  tri_id++;
260  break;
261  case 4:
262  if constexpr (d == 2) {
263  if (add) {
264  output.quad_ids_.push_back(quad_id);
265  }
266  quad_id++;
267  }
268  if constexpr (d == 3) {
269  if (add) {
270  output.tet_ids_.push_back(tet_id);
271  }
272  tet_id++;
273  }
274  break;
275  case 8:
276  if (add) {
277  output.hex_ids_.push_back(hex_id);
278  }
279  hex_id++;
280  break;
281  default:
282  SLIC_ERROR("unsupported element type");
283  break;
284  }
285  }
286 
287  return output;
288 }
289 
290 Domain Domain::ofElements(const mfem::Mesh& mesh, std::function<bool(std::vector<vec2>, int)> func)
291 {
292  return domain_of_elems<2>(mesh, func);
293 }
294 
295 Domain Domain::ofElements(const mfem::Mesh& mesh, std::function<bool(std::vector<vec3>, int)> func)
296 {
297  return domain_of_elems<3>(mesh, func);
298 }
299 
302 
303 template <int d>
304 static Domain domain_of_boundary_elems(const mfem::Mesh& mesh,
305  std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
306 {
307  assert(mesh.SpaceDimension() == d);
308 
309  Domain output{mesh, d - 1, Domain::Type::BoundaryElements};
310 
311  mfem::Array<int> face_id_to_bdr_id = mesh.GetFaceToBdrElMap();
312 
313  // layout is undocumented, but it seems to be
314  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
315  mfem::Vector vertices;
316  mesh.GetVertices(vertices);
317 
318  int edge_id = 0;
319  int tri_id = 0;
320  int quad_id = 0;
321 
322  // faces that satisfy the predicate are added to the domain
323  for (int f = 0; f < mesh.GetNumFaces(); f++) {
324  // discard faces with the wrong type
325  if (mesh.GetFaceInformation(f).IsInterior()) continue;
326 
327  auto geom = mesh.GetFaceGeometry(f);
328 
329  mfem::Array<int> vertex_ids;
330  mesh.GetFaceVertices(f, vertex_ids);
331 
332  auto x = gather<d>(vertices, vertex_ids);
333 
334  int bdr_id = face_id_to_bdr_id[f];
335  int attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
336 
337  bool add = predicate(x, attr);
338 
339  switch (geom) {
340  case mfem::Geometry::SEGMENT:
341  if (add) {
342  output.edge_ids_.push_back(edge_id);
343  }
344  edge_id++;
345  break;
346  case mfem::Geometry::TRIANGLE:
347  if (add) {
348  output.tri_ids_.push_back(tri_id);
349  }
350  tri_id++;
351  break;
352  case mfem::Geometry::SQUARE:
353  if (add) {
354  output.quad_ids_.push_back(quad_id);
355  }
356  quad_id++;
357  break;
358  default:
359  SLIC_ERROR("unsupported element type");
360  break;
361  }
362  }
363 
364  return output;
365 }
366 
367 Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function<bool(std::vector<vec2>, int)> func)
368 {
369  return domain_of_boundary_elems<2>(mesh, func);
370 }
371 
372 Domain Domain::ofBoundaryElements(const mfem::Mesh& mesh, std::function<bool(std::vector<vec3>, int)> func)
373 {
374  return domain_of_boundary_elems<3>(mesh, func);
375 }
376 
381 
382 Domain EntireDomain(const mfem::Mesh& mesh)
383 {
384  Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */};
385 
386  int tri_id = 0;
387  int quad_id = 0;
388  int tet_id = 0;
389  int hex_id = 0;
390 
391  // faces that satisfy the predicate are added to the domain
392  int num_elems = mesh.GetNE();
393  for (int i = 0; i < num_elems; i++) {
394  auto geom = mesh.GetElementGeometry(i);
395 
396  switch (geom) {
397  case mfem::Geometry::TRIANGLE:
398  output.tri_ids_.push_back(tri_id++);
399  break;
400  case mfem::Geometry::SQUARE:
401  output.quad_ids_.push_back(quad_id++);
402  break;
403  case mfem::Geometry::TETRAHEDRON:
404  output.tet_ids_.push_back(tet_id++);
405  break;
406  case mfem::Geometry::CUBE:
407  output.hex_ids_.push_back(hex_id++);
408  break;
409  default:
410  SLIC_ERROR("unsupported element type");
411  break;
412  }
413  }
414 
415  return output;
416 }
417 
418 Domain EntireBoundary(const mfem::Mesh& mesh)
419 {
420  Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::BoundaryElements};
421 
422  int edge_id = 0;
423  int tri_id = 0;
424  int quad_id = 0;
425 
426  for (int f = 0; f < mesh.GetNumFaces(); f++) {
427  // discard faces with the wrong type
428  if (mesh.GetFaceInformation(f).IsInterior()) continue;
429 
430  auto geom = mesh.GetFaceGeometry(f);
431 
432  switch (geom) {
433  case mfem::Geometry::SEGMENT:
434  output.edge_ids_.push_back(edge_id++);
435  break;
436  case mfem::Geometry::TRIANGLE:
437  output.tri_ids_.push_back(tri_id++);
438  break;
439  case mfem::Geometry::SQUARE:
440  output.quad_ids_.push_back(quad_id++);
441  break;
442  default:
443  SLIC_ERROR("unsupported element type");
444  break;
445  }
446  }
447 
448  return output;
449 }
450 
452 using c_iter = std::vector<int>::const_iterator;
453 using b_iter = std::back_insert_iterator<std::vector<int>>;
454 using set_op = std::function<b_iter(c_iter, c_iter, c_iter, c_iter, b_iter)>;
455 
456 set_op union_op = std::set_union<c_iter, c_iter, b_iter>;
457 set_op intersection_op = std::set_intersection<c_iter, c_iter, b_iter>;
458 set_op difference_op = std::set_difference<c_iter, c_iter, b_iter>;
460 
462 std::vector<int> set_operation(set_op op, const std::vector<int>& a, const std::vector<int>& b)
463 {
464  std::vector<int> output;
465  op(a.begin(), a.end(), b.begin(), b.end(), back_inserter(output));
466  return output;
467 }
468 
470 Domain set_operation(set_op op, const Domain& a, const Domain& b)
471 {
472  assert(&a.mesh_ == &b.mesh_);
473  assert(a.dim_ == b.dim_);
474 
475  Domain output{a.mesh_, a.dim_};
476 
477  if (output.dim_ == 0) {
478  output.vertex_ids_ = set_operation(op, a.vertex_ids_, b.vertex_ids_);
479  }
480 
481  if (output.dim_ == 1) {
482  output.edge_ids_ = set_operation(op, a.edge_ids_, b.edge_ids_);
483  }
484 
485  if (output.dim_ == 2) {
486  output.tri_ids_ = set_operation(op, a.tri_ids_, b.tri_ids_);
487  output.quad_ids_ = set_operation(op, a.quad_ids_, b.quad_ids_);
488  }
489 
490  if (output.dim_ == 3) {
491  output.tet_ids_ = set_operation(op, a.tet_ids_, b.tet_ids_);
492  output.hex_ids_ = set_operation(op, a.hex_ids_, b.hex_ids_);
493  }
494 
495  return output;
496 }
497 
498 Domain operator|(const Domain& a, const Domain& b) { return set_operation(union_op, a, b); }
499 Domain operator&(const Domain& a, const Domain& b) { return set_operation(intersection_op, a, b); }
500 Domain operator-(const Domain& a, const Domain& b) { return set_operation(difference_op, a, b); }
501 
502 } // namespace serac
many of the functions in this file amount to extracting element indices from an mfem::Mesh like
Accelerator functionality.
Definition: serac.cpp:38
Domain EntireBoundary(const mfem::Mesh &mesh)
constructs a domain from all the boundary elements in a mesh
Definition: domain.cpp:418
Domain operator-(const Domain &a, const Domain &b)
create a new domain that is the set difference of a and b
Definition: domain.cpp:500
std::vector< tensor< double, d > > gather(const mfem::Vector &coordinates, mfem::Array< int > ids)
gather vertex coordinates for a list of vertices
Definition: domain.cpp:39
Domain operator&(const Domain &a, const Domain &b)
create a new domain that is the intersection of a and b
Definition: domain.cpp:499
Domain operator|(const Domain &a, const Domain &b)
create a new domain that is the union of a and b
Definition: domain.cpp:498
Domain EntireDomain(const mfem::Mesh &mesh)
constructs a domain from all the elements in a mesh
Definition: domain.cpp:382
std::vector< int > set_operation(set_op op, const std::vector< int > &a, const std::vector< int > &b)
return a std::vector that is the result of applying (a op b)
Definition: domain.cpp:462
a class for representing a geometric region that can be used for integration
Definition: domain.hpp:21
static Domain ofFaces(const mfem::Mesh &mesh, std::function< bool(std::vector< vec2 >, int)> func)
create a domain from some subset of the faces in an mfem::Mesh
Definition: domain.cpp:213
static Domain ofBoundaryElements(const mfem::Mesh &mesh, std::function< bool(std::vector< vec2 >, int)> func)
create a domain from some subset of the boundary elements (spatial dim == geometry dim + 1) in an mfe...
Definition: domain.cpp:367
static Domain ofVertices(const mfem::Mesh &mesh, std::function< bool(vec2)> func)
create a domain from some subset of the vertices in an mfem::Mesh
Definition: domain.cpp:79
int dim_
the geometric dimension of the domain
Definition: domain.hpp:35
const mfem::Mesh & mesh_
the underyling mesh for this domain
Definition: domain.hpp:32
static Domain ofElements(const mfem::Mesh &mesh, std::function< bool(std::vector< vec2 >, int)> func)
create a domain from some subset of the elements (spatial dim == geometry dim) in an mfem::Mesh
Definition: domain.cpp:290
static Domain ofEdges(const mfem::Mesh &mesh, std::function< bool(std::vector< vec2 >, int)> func)
create a domain from some subset of the edges in an mfem::Mesh
Definition: domain.cpp:132
Arbitrary-rank tensor class.
Definition: tensor.hpp:29