Smith  0.1
Smith is an implicit thermal structural mechanics simulation code.
domain.cpp
1 // Copyright (c) Lawrence Livermore National Security, LLC and
2 // other Smith Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
29 
30 #include <cassert>
31 #include <cstdint>
32 #include <cstdlib>
33 #include <iterator>
34 #include <tuple>
35 
36 #include "smith/numerics/functional/element_restriction.hpp"
37 
38 namespace smith {
39 
46 template <int d>
47 std::vector<tensor<double, d>> gather(const mfem::Vector& coordinates, mfem::Array<int> ids)
48 {
49  int num_vertices = coordinates.Size() / d;
50  std::vector<tensor<double, d>> x(std::size_t(ids.Size()));
51  for (int v = 0; v < ids.Size(); v++) {
52  for (int j = 0; j < d; j++) {
53  x[uint32_t(v)][j] = coordinates[j * num_vertices + ids[v]];
54  }
55  }
56  return x;
57 }
58 
61 
62 template <int d, typename T>
63 static Domain domain_of_edges(const mesh_t& mesh, std::function<T> predicate)
64 {
65  assert(mesh.SpaceDimension() == d);
66 
67  Domain output{mesh, 1 /* edges are 1-dimensional */, Domain::Type::Elements};
68 
69  // layout is undocumented, but it seems to be
70  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
71  mfem::Vector vertices;
72  mesh.GetVertices(vertices);
73 
74  mfem::Array<int> edge_id_to_bdr_id;
75  if (d == 2) {
76  edge_id_to_bdr_id = mesh.GetFaceToBdrElMap();
77  }
78 
79  int num_edges = mesh.GetNEdges();
80  for (int i = 0; i < num_edges; i++) {
81  mfem::Array<int> vertex_ids;
82  mesh.GetEdgeVertices(i, vertex_ids);
83 
84  auto x = gather<d>(vertices, vertex_ids);
85 
86  if constexpr (d == 2) {
87  int bdr_id = edge_id_to_bdr_id[i];
88  int attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
89  if (predicate(x, attr)) {
90  output.addElement(i, i, mfem::Geometry::SEGMENT);
91  }
92  } else {
93  if (predicate(x)) {
94  output.addElement(i, i, mfem::Geometry::SEGMENT);
95  }
96  }
97  }
98 
99  return output;
100 }
101 
102 Domain Domain::ofEdges(const mesh_t& mesh, std::function<bool(std::vector<vec2>, int)> func)
103 {
104  return domain_of_edges<2>(mesh, func);
105 }
106 
107 Domain Domain::ofEdges(const mesh_t& mesh, std::function<bool(std::vector<vec3>)> func)
108 {
109  return domain_of_edges<3>(mesh, func);
110 }
111 
114 
115 template <int d>
116 static Domain domain_of_faces(const mesh_t& mesh, std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
117 {
118  assert(mesh.SpaceDimension() == d);
119 
120  Domain output{mesh, 2 /* faces are 2-dimensional */, Domain::Type::Elements};
121 
122  // layout is undocumented, but it seems to be
123  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
124  mfem::Vector vertices;
125  mesh.GetVertices(vertices);
126 
127  mfem::Array<int> face_id_to_bdr_id;
128  if (d == 3) {
129  face_id_to_bdr_id = mesh.GetFaceToBdrElMap();
130  }
131 
132  // faces that satisfy the predicate are added to the domain
133  int num_faces;
134  if (d == 2) {
135  num_faces = mesh.GetNE();
136  } else {
137  num_faces = mesh.GetNumFaces();
138  }
139 
140  int tri_id = 0;
141  int quad_id = 0;
142 
143  for (int i = 0; i < num_faces; i++) {
144  mfem::Array<int> vertex_ids;
145 
146  if (mesh.Dimension() == 2) {
147  mesh.GetElementVertices(i, vertex_ids);
148  } else {
149  mesh.GetFaceVertices(i, vertex_ids);
150  }
151 
152  auto x = gather<d>(vertices, vertex_ids);
153 
154  int attr;
155  if (d == 2) {
156  attr = mesh.GetAttribute(i);
157  } else {
158  int bdr_id = face_id_to_bdr_id[i];
159  attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
160  }
161 
162  if (predicate(x, attr)) {
163  if (x.size() == 3) {
164  output.addElement(tri_id, i, mfem::Geometry::TRIANGLE);
165  }
166  if (x.size() == 4) {
167  output.addElement(quad_id, i, mfem::Geometry::SQUARE);
168  }
169  }
170 
171  if (x.size() == 3) {
172  tri_id++;
173  }
174  if (x.size() == 4) {
175  quad_id++;
176  }
177  }
178 
179  return output;
180 }
181 
182 Domain Domain::ofFaces(const mesh_t& mesh, std::function<bool(std::vector<vec2>, int)> func)
183 {
184  return domain_of_faces(mesh, func);
185 }
186 
187 Domain Domain::ofFaces(const mesh_t& mesh, std::function<bool(std::vector<vec3>, int)> func)
188 {
189  return domain_of_faces(mesh, func);
190 }
191 
194 
195 template <int d>
196 static Domain domain_of_elems(const mesh_t& mesh, std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
197 {
198  assert(mesh.SpaceDimension() == d);
199 
200  Domain output{mesh, mesh.SpaceDimension() /* elems can be 2 or 3 dimensional */, Domain::Type::Elements};
201 
202  // layout is undocumented, but it seems to be
203  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
204  mfem::Vector vertices;
205  mesh.GetVertices(vertices);
206 
207  int tri_id = 0;
208  int quad_id = 0;
209  int tet_id = 0;
210  int hex_id = 0;
211 
212  // elements that satisfy the predicate are added to the domain
213  int num_elems = mesh.GetNE();
214  for (int i = 0; i < num_elems; i++) {
215  mfem::Array<int> vertex_ids;
216  mesh.GetElementVertices(i, vertex_ids);
217 
218  auto x = gather<d>(vertices, vertex_ids);
219 
220  bool add = predicate(x, mesh.GetAttribute(i));
221 
222  switch (x.size()) {
223  case 3:
224  if (add) {
225  output.addElement(tri_id, i, mfem::Geometry::TRIANGLE);
226  }
227  tri_id++;
228  break;
229  case 4:
230  if constexpr (d == 2) {
231  if (add) {
232  output.addElement(quad_id, i, mfem::Geometry::SQUARE);
233  }
234  quad_id++;
235  }
236  if constexpr (d == 3) {
237  if (add) {
238  output.addElement(tet_id, i, mfem::Geometry::TETRAHEDRON);
239  }
240  tet_id++;
241  }
242  break;
243  case 8:
244  if (add) {
245  output.addElement(hex_id, i, mfem::Geometry::CUBE);
246  }
247  hex_id++;
248  break;
249  default:
250  SLIC_ERROR("unsupported element type");
251  break;
252  }
253  }
254 
255  return output;
256 }
257 
258 Domain Domain::ofElements(const mesh_t& mesh, std::function<bool(std::vector<vec2>, int)> func)
259 {
260  return domain_of_elems<2>(mesh, func);
261 }
262 
263 Domain Domain::ofElements(const mesh_t& mesh, std::function<bool(std::vector<vec3>, int)> func)
264 {
265  return domain_of_elems<3>(mesh, func);
266 }
267 
268 void Domain::addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry)
269 {
270  if (element_geometry == mfem::Geometry::SEGMENT) {
271  edge_ids_.push_back(geom_id);
272  mfem_edge_ids_.push_back(elem_id);
273  } else if (element_geometry == mfem::Geometry::TRIANGLE) {
274  tri_ids_.push_back(geom_id);
275  mfem_tri_ids_.push_back(elem_id);
276  } else if (element_geometry == mfem::Geometry::SQUARE) {
277  quad_ids_.push_back(geom_id);
278  mfem_quad_ids_.push_back(elem_id);
279  } else if (element_geometry == mfem::Geometry::TETRAHEDRON) {
280  tet_ids_.push_back(geom_id);
281  mfem_tet_ids_.push_back(elem_id);
282  } else if (element_geometry == mfem::Geometry::CUBE) {
283  hex_ids_.push_back(geom_id);
284  mfem_hex_ids_.push_back(elem_id);
285  } else {
286  SLIC_ERROR("unsupported element type");
287  }
288 }
289 
290 void Domain::addElements(const std::vector<int>& geom_ids, const std::vector<int>& elem_ids,
291  mfem::Geometry::Type element_geometry)
292 {
293  SLIC_ERROR_IF(geom_ids.size() != elem_ids.size(),
294  "To add elements, you must specify a geom_id AND an elem_id for each element");
295 
296  for (std::vector<int>::size_type i = 0; i < geom_ids.size(); ++i) {
297  addElement(geom_ids[i], elem_ids[i], element_geometry);
298  }
299 }
300 
303 
304 template <int d>
305 static Domain domain_of_boundary_elems(const mesh_t& mesh,
306  std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
307 {
308  assert(mesh.SpaceDimension() == d);
309 
310  Domain output{mesh, d - 1, Domain::Type::BoundaryElements};
311 
312  mfem::Array<int> face_id_to_bdr_id = mesh.GetFaceToBdrElMap();
313 
314  // layout is undocumented, but it seems to be
315  // [x1, x2, x3, ..., y1, y2, y3 ..., (z1, z2, z3, ...)]
316  mfem::Vector vertices;
317  mesh.GetVertices(vertices);
318 
319  int edge_id = 0;
320  int tri_id = 0;
321  int quad_id = 0;
322 
323  // faces that satisfy the predicate are added to the domain
324  for (int f = 0; f < mesh.GetNumFaces(); f++) {
325  // discard faces with the wrong type
326  if (mesh.GetFaceInformation(f).IsInterior()) continue;
327 
328  auto geom = mesh.GetFaceGeometry(f);
329 
330  mfem::Array<int> vertex_ids;
331  mesh.GetFaceVertices(f, vertex_ids);
332 
333  auto x = gather<d>(vertices, vertex_ids);
334 
335  int bdr_id = face_id_to_bdr_id[f];
336  int attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
337 
338  bool add = predicate(x, attr);
339 
340  switch (geom) {
341  case mfem::Geometry::SEGMENT:
342  if (add) {
343  output.addElement(edge_id, f, geom);
344  }
345  edge_id++;
346  break;
347  case mfem::Geometry::TRIANGLE:
348  if (add) {
349  output.addElement(tri_id, f, geom);
350  }
351  tri_id++;
352  break;
353  case mfem::Geometry::SQUARE:
354  if (add) {
355  output.addElement(quad_id, f, geom);
356  }
357  quad_id++;
358  break;
359  default:
360  SLIC_ERROR("unsupported element type");
361  break;
362  }
363  }
364 
365  return output;
366 }
367 
368 Domain Domain::ofBoundaryElements(const mesh_t& mesh, std::function<bool(std::vector<vec2>, int)> func)
369 {
370  return domain_of_boundary_elems<2>(mesh, func);
371 }
372 
373 Domain Domain::ofBoundaryElements(const mesh_t& mesh, std::function<bool(std::vector<vec3>, int)> func)
374 {
375  return domain_of_boundary_elems<3>(mesh, func);
376 }
377 
436 void findDomainDofsOnNeighborRanks(const smith::fes_t* fes, mfem::Array<int>& local_dof_ids)
437 {
438  auto par_fes = dynamic_cast<const mfem::ParFiniteElementSpace*>(fes);
439  // There's no work to do unless the finite element space really is parallel
440  if (par_fes) {
441  // As far as I can tell, the parallel communication in mfem only works with
442  // vector field dof indexing. So we need to get the parallel-correct scalar
443  // dof ids, we do the following:
444  // (1) transform scalar ldof ids to vector ldof ids,
445  // (2) transform the vector ldof ids into a boolean "marker" ldof field
446  // (3) do our parallel sync, which applies an OR logic operator to the
447  // boolean fields from all ranks at each dof
448  // (4) get the ldof indices of the TRUE values of the parallel-correct
449  // boolean ldof field
450  // (5) transform the parallel-correct vector ldof ids back to scalar dof ids.
451  fes->DofsToVDofs(0, local_dof_ids);
452 
453  mfem::Array<int> local_dof_markers;
454  mfem::FiniteElementSpace::ListToMarker(local_dof_ids, par_fes->GetVSize(), local_dof_markers, 1);
455 
456  par_fes->Synchronize(local_dof_markers);
457 
458  mfem::FiniteElementSpace::MarkerToList(local_dof_markers, local_dof_ids);
459 
460  for (int i = 0; i < local_dof_ids.Size(); i++) {
461  local_dof_ids[i] = par_fes->VDofToDof(local_dof_ids[i]);
462  }
463  }
464 }
465 
466 mfem::Array<int> Domain::dof_list(const smith::fes_t* fes) const
467 {
468  std::set<int> dof_ids;
469  mfem::Array<int> elem_dofs;
470 
471  std::function<void(int i, mfem::Array<int>&)> GetDofs;
472  if (type_ == Type::Elements) {
473  GetDofs = [&](int i, mfem::Array<int>& vdofs) { return fes->GetElementDofs(i, vdofs); };
474  }
475 
476  if (type_ == Type::BoundaryElements) {
477  GetDofs = [&](int i, mfem::Array<int>& vdofs) { return fes->GetFaceDofs(i, vdofs); };
478  }
479 
480  if (dim_ == 0) {
481  // sam: what to do with vertex sets?
482  }
483 
484  if (dim_ == 1) {
485  for (auto elem_id : mfem_edge_ids_) {
486  GetDofs(elem_id, elem_dofs);
487  for (int i = 0; i < elem_dofs.Size(); i++) {
488  dof_ids.insert(elem_dofs[i]);
489  }
490  }
491  }
492 
493  if (dim_ == 2) {
494  for (auto elem_id : mfem_tri_ids_) {
495  GetDofs(elem_id, elem_dofs);
496  for (int i = 0; i < elem_dofs.Size(); i++) {
497  dof_ids.insert(elem_dofs[i]);
498  }
499  }
500 
501  for (auto elem_id : mfem_quad_ids_) {
502  GetDofs(elem_id, elem_dofs);
503  for (int i = 0; i < elem_dofs.Size(); i++) {
504  dof_ids.insert(elem_dofs[i]);
505  }
506  }
507  }
508 
509  if (dim_ == 3) {
510  for (auto elem_id : mfem_tet_ids_) {
511  GetDofs(elem_id, elem_dofs);
512  for (int i = 0; i < elem_dofs.Size(); i++) {
513  dof_ids.insert(elem_dofs[i]);
514  }
515  }
516 
517  for (auto elem_id : mfem_hex_ids_) {
518  GetDofs(elem_id, elem_dofs);
519  for (int i = 0; i < elem_dofs.Size(); i++) {
520  dof_ids.insert(elem_dofs[i]);
521  }
522  }
523  }
524 
525  mfem::Array<int> uniq_dof_ids(int(dof_ids.size()));
526  int i = 0;
527  for (auto id : dof_ids) {
528  uniq_dof_ids[i++] = id;
529  }
530 
531  findDomainDofsOnNeighborRanks(fes, uniq_dof_ids);
532 
533  return uniq_dof_ids;
534 }
535 
536 void Domain::insert_restriction(const smith::fes_t* fes, FunctionSpace space)
537 {
538  // if we don't already have a BlockElementRestriction for this FunctionSpace, make one
539  if (restriction_operators.count(space) == 0) {
541  }
542 }
543 
545 
547 {
548  // Weights only need to be computed for Domain of InteriorBoundaryElements type
549  SLIC_ERROR_ROOT_IF(type_ != Domain::Type::InteriorBoundaryElements,
550  "This method is only for interior boundary element domains");
551 
552  // make a list if we don't already have one
553  if (shared_interior_face_ids_.empty()) {
554  if (dim_ == 1) {
555  int i = 0;
556  for (int f : mfem_edge_ids_) {
557  mfem::Mesh::FaceInformation info = mesh_.GetFaceInformation(f);
558 
559  if (info.IsShared()) {
560  shared_interior_face_ids_.push_back(i);
561  }
562 
563  ++i;
564  }
565  } else if (dim_ == 2) {
566  int i = 0;
567  for (int f : mfem_tri_ids_) {
568  mfem::Mesh::FaceInformation info = mesh_.GetFaceInformation(f);
569 
570  if (info.IsShared()) {
571  shared_interior_face_ids_.push_back(i);
572  }
573 
574  ++i;
575  }
576 
577  for (int f : mfem_quad_ids_) {
578  mfem::Mesh::FaceInformation info = mesh_.GetFaceInformation(f);
579 
580  if (info.IsShared()) {
581  shared_interior_face_ids_.push_back(i);
582  }
583 
584  ++i;
585  }
586  }
587  }
588 }
589 
594 
595 Domain EntireDomain(const mesh_t& mesh)
596 {
597  switch (mesh.SpaceDimension()) {
598  case 2:
599  return Domain::ofElements(mesh, [](std::vector<vec2>, int) { return true; });
600  break;
601  case 3:
602  return Domain::ofElements(mesh, [](std::vector<vec3>, int) { return true; });
603  break;
604  default:
605  SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes.");
606  exit(-1);
607  }
608 }
609 
610 Domain EntireBoundary(const mesh_t& mesh)
611 {
612  switch (mesh.SpaceDimension()) {
613  case 2:
614  return Domain::ofBoundaryElements(mesh, [](std::vector<vec2>, int) { return true; });
615  break;
616  case 3:
617  return Domain::ofBoundaryElements(mesh, [](std::vector<vec3>, int) { return true; });
618  break;
619  default:
620  SLIC_ERROR("In valid spatial dimension. Domains may only be created on 2D or 3D meshes.");
621  exit(-1);
622  }
623 }
624 
626 Domain EntireInteriorBoundary(const mesh_t& mesh)
627 {
628  Domain output{mesh, mesh.SpaceDimension() - 1, Domain::Type::InteriorBoundaryElements};
629 
630  int edge_id = 0;
631  int tri_id = 0;
632  int quad_id = 0;
633 
634  for (int f = 0; f < mesh.GetNumFaces(); f++) {
635  // discard faces with the wrong type
636  if (!mesh.GetFaceInformation(f).IsInterior()) continue;
637 
638  auto geom = mesh.GetFaceGeometry(f);
639 
640  switch (geom) {
641  case mfem::Geometry::SEGMENT:
642  output.edge_ids_.push_back(edge_id++);
643  output.mfem_edge_ids_.push_back(f);
644  break;
645  case mfem::Geometry::TRIANGLE:
646  output.tri_ids_.push_back(tri_id++);
647  output.mfem_tri_ids_.push_back(f);
648  break;
649  case mfem::Geometry::SQUARE:
650  output.quad_ids_.push_back(quad_id++);
651  output.mfem_quad_ids_.push_back(f);
652  break;
653  default:
654  SLIC_ERROR("unsupported element type");
655  break;
656  }
657  }
658 
659  output.insert_shared_interior_face_list();
660 
661  return output;
662 }
663 
665 template <int d>
667  std::function<bool(std::vector<tensor<double, d>>, int)> predicate)
668 {
669  assert(mesh.SpaceDimension() == d);
670  Domain output{mesh, d - 1, Domain::Type::InteriorBoundaryElements};
671 
672  mfem::Array<int> face_id_to_bdr_id = mesh.GetFaceToBdrElMap();
673  mfem::Vector vertices;
674  mesh.GetVertices(vertices);
675 
676  int edge_id = 0;
677  int tri_id = 0;
678  int quad_id = 0;
679 
680  for (int f = 0; f < mesh.GetNumFaces(); f++) {
681  // discard faces with the wrong type
682  if (!mesh.GetFaceInformation(f).IsInterior()) continue;
683 
684  auto geom = mesh.GetFaceGeometry(f);
685 
686  mfem::Array<int> vertex_ids;
687  mesh.GetFaceVertices(f, vertex_ids);
688 
689  auto x = gather<d>(vertices, vertex_ids);
690 
691  int bdr_id = face_id_to_bdr_id[f];
692  int attr = (bdr_id >= 0) ? mesh.GetBdrAttribute(bdr_id) : -1;
693 
694  bool add = predicate(x, attr);
695 
696  switch (geom) {
697  case mfem::Geometry::SEGMENT:
698  if (add) {
699  output.edge_ids_.push_back(edge_id);
700  output.mfem_edge_ids_.push_back(f);
701  }
702  edge_id++;
703  break;
704  case mfem::Geometry::TRIANGLE:
705  if (add) {
706  output.tri_ids_.push_back(tri_id);
707  output.mfem_tri_ids_.push_back(f);
708  }
709  tri_id++;
710  break;
711  case mfem::Geometry::SQUARE:
712  if (add) {
713  output.quad_ids_.push_back(quad_id);
714  output.mfem_quad_ids_.push_back(f);
715  }
716  quad_id++;
717  break;
718  default:
719  SLIC_ERROR("unsupported element type");
720  break;
721  }
722  }
723 
724  output.insert_shared_interior_face_list();
725 
726  return output;
727 }
728 
729 Domain Domain::ofInteriorBoundaryElements(const mesh_t& mesh, std::function<bool(std::vector<vec2>, int)> func)
730 {
731  return domain_of_interior_boundaries<2>(mesh, func);
732 }
733 
734 Domain Domain::ofInteriorBoundaryElements(const mesh_t& mesh, std::function<bool(std::vector<vec3>, int)> func)
735 {
736  return domain_of_interior_boundaries<3>(mesh, func);
737 }
738 
743 
745 using int2 = std::tuple<int, int>;
746 enum SET_OPERATION
747 {
748  UNION,
749  INTERSECTION,
750  DIFFERENCE
751 };
753 
755 void zip(std::vector<int2>& ab, const std::vector<int>& a, const std::vector<int>& b)
756 {
757  ab.resize(a.size());
758  for (uint32_t i = 0; i < a.size(); i++) {
759  ab[i] = {a[i], b[i]};
760  }
761 }
762 
764 void unzip(const std::vector<int2>& ab, std::vector<int>& a, std::vector<int>& b)
765 {
766  a.resize(ab.size());
767  b.resize(ab.size());
768  for (uint32_t i = 0; i < ab.size(); i++) {
769  auto ab_i = ab[i];
770  a[i] = std::get<0>(ab_i);
771  b[i] = std::get<1>(ab_i);
772  }
773 }
774 
776 template <typename T>
777 std::vector<T> set_operation(SET_OPERATION op, const std::vector<T>& a, const std::vector<T>& b)
778 {
779  using c_iter = typename std::vector<T>::const_iterator;
780  using b_iter = std::back_insert_iterator<std::vector<T>>;
781  using set_op = std::function<b_iter(c_iter, c_iter, c_iter, c_iter, b_iter)>;
782 
783  set_op combine;
784  if (op == SET_OPERATION::UNION) {
785  combine = std::set_union<c_iter, c_iter, b_iter>;
786  }
787  if (op == SET_OPERATION::INTERSECTION) {
788  combine = std::set_intersection<c_iter, c_iter, b_iter>;
789  }
790  if (op == SET_OPERATION::DIFFERENCE) {
791  combine = std::set_difference<c_iter, c_iter, b_iter>;
792  }
793 
794  std::vector<T> combined;
795  combine(a.begin(), a.end(), b.begin(), b.end(), back_inserter(combined));
796  return combined;
797 }
798 
800 Domain set_operation(SET_OPERATION op, const Domain& a, const Domain& b)
801 {
802  assert(&a.mesh_ == &b.mesh_);
803  assert(a.dim_ == b.dim_);
804  assert(a.type_ == b.type_);
805 
806  Domain combined{a.mesh_, a.dim_, a.type_};
807 
808  using Ids = std::vector<int>;
809  auto apply_set_op = [&op](const Ids& x, const Ids& y) { return set_operation(op, x, y); };
810 
811  auto fill_combined_lists = [apply_set_op, &combined](const Ids& a_ids, const Ids& a_mfem_ids, const Ids& b_ids,
812  const Ids& b_mfem_ids, mfem::Geometry::Type g) {
813  auto combined_ids = apply_set_op(a_ids, b_ids);
814  auto combined_mfem_ids = apply_set_op(a_mfem_ids, b_mfem_ids);
815  combined.addElements(combined_ids, combined_mfem_ids, g);
816  };
817 
818  if (combined.dim_ == 1) {
819  fill_combined_lists(a.edge_ids_, a.mfem_edge_ids_, b.edge_ids_, b.mfem_edge_ids_, mfem::Geometry::SEGMENT);
820  }
821 
822  if (combined.dim_ == 2) {
823  fill_combined_lists(a.tri_ids_, a.mfem_tri_ids_, b.tri_ids_, b.mfem_tri_ids_, mfem::Geometry::TRIANGLE);
824  fill_combined_lists(a.quad_ids_, a.mfem_quad_ids_, b.quad_ids_, b.mfem_quad_ids_, mfem::Geometry::SQUARE);
825  }
826 
827  if (combined.dim_ == 3) {
828  fill_combined_lists(a.tet_ids_, a.mfem_tet_ids_, b.tet_ids_, b.mfem_tet_ids_, mfem::Geometry::TETRAHEDRON);
829  fill_combined_lists(a.hex_ids_, a.mfem_hex_ids_, b.hex_ids_, b.mfem_hex_ids_, mfem::Geometry::CUBE);
830  }
831 
832  return combined;
833 }
834 
835 Domain operator|(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::UNION, a, b); }
836 Domain operator&(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::INTERSECTION, a, b); }
837 Domain operator-(const Domain& a, const Domain& b) { return set_operation(SET_OPERATION::DIFFERENCE, a, b); }
838 
839 } // namespace smith
many of the functions in this file amount to extracting element indices from an mesh_t like
Accelerator functionality.
Definition: smith.cpp:36
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:47
Domain EntireInteriorBoundary(const mesh_t &mesh)
constructs a domain from all the interior boundary elements in a mesh
Definition: domain.cpp:626
Domain operator&(const Domain &a, const Domain &b)
create a new domain that is the intersection of a and b
Definition: domain.cpp:836
Domain operator|(const Domain &a, const Domain &b)
create a new domain that is the union of a and b
Definition: domain.cpp:835
Domain EntireBoundary(const mesh_t &mesh)
constructs a domain from all the boundary elements in a mesh
Definition: domain.cpp:610
void unzip(const std::vector< int2 > &ab, std::vector< int > &a, std::vector< int > &b)
split an array of int2 into a pair of arrays of ints, see also: zip()
Definition: domain.cpp:764
std::vector< T > set_operation(SET_OPERATION op, const std::vector< T > &a, const std::vector< T > &b)
return a std::vector that is the result of applying (a op b)
Definition: domain.cpp:777
void zip(std::vector< int2 > &ab, const std::vector< int > &a, const std::vector< int > &b)
combine a pair of arrays of ints into a single array of int2, see also: unzip()
Definition: domain.cpp:755
Domain EntireDomain(const mesh_t &mesh)
constructs a domain from all the elements in a mesh
Definition: domain.cpp:595
void findDomainDofsOnNeighborRanks(const smith::fes_t *fes, mfem::Array< int > &local_dof_ids)
Get local dofs that are part of a domain, but are owned by a neighboring MPI rank.
Definition: domain.cpp:436
Domain domain_of_interior_boundaries(const mesh_t &mesh, std::function< bool(std::vector< tensor< double, d >>, int)> predicate)
constructs a domain from some subset of the interior boundary elements in a mesh
Definition: domain.cpp:666
mfem::ParFiniteElementSpace & space(FieldState field)
Get the space from the primal field of a field states.
FieldStateWeightedSum operator-(const FieldState &x, const FieldState &y)
subtract two FieldState
a generalization of mfem::ElementRestriction that works with multiple kinds of element geometries....
a class for representing a geometric region that can be used for integration
Definition: domain.hpp:33
int dim_
the geometric dimension of the domain
Definition: domain.hpp:48
std::vector< int > shared_interior_face_ids_
Ids of interior faces that lie on the boundary shared by two processors.
Definition: domain.hpp:106
const BlockElementRestriction & get_restriction(FunctionSpace space)
getter for accessing a restriction operator by its function space
Definition: domain.cpp:544
void insert_restriction(const fes_t *fes, FunctionSpace space)
create a restriction operator over this domain, using its FunctionSpace as a key
Definition: domain.cpp:536
static Domain ofElements(const mesh_t &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:258
void addElement(int geom_id, int elem_id, mfem::Geometry::Type element_geometry)
Add an element to the domain.
Definition: domain.cpp:268
mfem::Array< int > dof_list(const fes_t *fes) const
get mfem degree of freedom list for a given FiniteElementSpace
Definition: domain.cpp:466
std::map< FunctionSpace, BlockElementRestriction > restriction_operators
a collection of restriction operators for the different test/trial spaces appearing in integrals eval...
Definition: domain.hpp:103
static Domain ofBoundaryElements(const mesh_t &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:368
Type type_
whether the elements in this domain are on the boundary or not
Definition: domain.hpp:51
void addElements(const std::vector< int > &geom_id, const std::vector< int > &elem_id, mfem::Geometry::Type element_geometry)
Add a batch of elements to the domain.
Definition: domain.cpp:290
static Domain ofFaces(const mesh_t &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:182
static Domain ofInteriorBoundaryElements(const mesh_t &mesh, std::function< bool(std::vector< vec2 >, int)> func)
create a domain from some subset of the interior boundary elements in an a mesh
Definition: domain.cpp:729
void insert_shared_interior_face_list()
Find the list of interior faces shared by two processors to make sure these faces are only integrated...
Definition: domain.cpp:546
static Domain ofEdges(const mesh_t &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:102
const mesh_t & mesh_
the underyling mesh for this domain
Definition: domain.hpp:45
a small POD class for tracking function space metadata
Arbitrary-rank tensor class.
Definition: tensor.hpp:28