https://mooseframework.inl.gov
MFEMCutTransitionSubMesh.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #ifdef MOOSE_MFEM_ENABLED
11 
13 #include "MFEMProblem.h"
14 
16 
19 {
22  params.addClassDescription(
23  "Class to construct an MFEMSubMesh formed from the set of elements that have least one "
24  "vertex on the specified cut plane, that lie on one side of the plane, "
25  "and that are restricted to the set of user-specified subdomains.");
26  params.addRequiredParam<BoundaryName>("cut_boundary",
27  "The boundary associated with the mesh cut.");
28  params.addRequiredParam<BoundaryName>(
29  "transition_subdomain_boundary",
30  "Name to assign boundary of transition subdomain not shared with cut surface.");
31  params.addRequiredParam<SubdomainName>(
32  "transition_subdomain",
33  "The name of the subdomain to be created on the mesh comprised of the set of elements "
34  "adjacent to the cut surface on one side.");
35  params.addRequiredParam<SubdomainName>(
36  "closed_subdomain",
37  "The name of the subdomain attribute to be created comprised of the set of all elements "
38  "of the closed geometry, including the new transition region.");
39  return params;
40 }
41 
43  : MFEMSubMesh(parameters),
44  MFEMBlockRestrictable(parameters, getMFEMProblem().mesh().getMFEMParMesh()),
45  _cut_boundary(getParam<BoundaryName>("cut_boundary")),
46  _cut_submesh(std::make_shared<mfem::ParSubMesh>(mfem::ParSubMesh::CreateFromBoundary(
47  getMFEMProblem().mesh().getMFEMParMesh(),
48  getMesh().bdr_attribute_sets.GetAttributeSet(_cut_boundary)))),
49  _transition_subdomain_boundary(getParam<BoundaryName>("transition_subdomain_boundary")),
50  _transition_subdomain(getParam<SubdomainName>("transition_subdomain")),
51  _closed_subdomain(getParam<SubdomainName>("closed_subdomain")),
52  _cut_normal(3)
53 {
54 }
55 
56 void
58 {
59  labelMesh(getMFEMProblem().mesh().getMFEMParMesh());
60  _submesh = std::make_shared<mfem::ParSubMesh>(mfem::ParSubMesh::CreateFromDomain(
61  getMesh(), getMesh().attribute_sets.GetAttributeSet(_transition_subdomain)));
62  _submesh->attribute_sets.attr_sets = getMesh().attribute_sets.attr_sets;
63  _submesh->bdr_attribute_sets.attr_sets = getMesh().bdr_attribute_sets.attr_sets;
64 }
65 
66 void
67 MFEMCutTransitionSubMesh::labelMesh(mfem::ParMesh & parent_mesh)
68 {
69  int mpi_comm_rank = 0;
70  int mpi_comm_size = 1;
71  MPI_Comm_rank(getMFEMProblem().mesh().getMFEMParMesh().GetComm(), &mpi_comm_rank);
72  MPI_Comm_size(getMFEMProblem().mesh().getMFEMParMesh().GetComm(), &mpi_comm_size);
73 
74  // First determine face normal based on the first boundary element found on the cut
75  // to use when determining orientation relative to the cut
76  const mfem::Array<int> & parent_cut_element_id_map = _cut_submesh->GetParentElementIDMap();
77  int rank_with_submesh = -1;
78  if (parent_cut_element_id_map.Size() > 0)
79  {
80  int reference_face = parent_cut_element_id_map[0];
81  _cut_normal = findFaceNormal(parent_mesh, parent_mesh.GetBdrElementFaceIndex(reference_face));
82  rank_with_submesh = mpi_comm_rank;
83  }
84  MPI_Allreduce(MPI_IN_PLACE,
85  &rank_with_submesh,
86  1,
87  MPI_INT,
88  MPI_MAX,
89  getMFEMProblem().mesh().getMFEMParMesh().GetComm());
90  MPI_Bcast(_cut_normal.GetData(),
91  _cut_normal.Size(),
92  MPI_DOUBLE,
93  rank_with_submesh,
94  getMFEMProblem().mesh().getMFEMParMesh().GetComm());
95  // Iterate over all vertices on cut, find elements with those vertices,
96  // and declare them transition elements if they are on the +ve side of the cut
97  mfem::Array<int> transition_els;
98  std::vector<HYPRE_BigInt> global_cut_vert_ids;
99  mfem::Array<HYPRE_BigInt> gi;
100  parent_mesh.GetGlobalVertexIndices(gi);
101  std::unique_ptr<mfem::Table> vert_to_elem(parent_mesh.GetVertexToElementTable());
102  const mfem::Array<int> & cut_to_parent_vertex_id_map = _cut_submesh->GetParentVertexIDMap();
103  for (int i = 0; i < _cut_submesh->GetNV(); ++i)
104  {
105  int cut_vert = cut_to_parent_vertex_id_map[i];
106  global_cut_vert_ids.push_back(gi[cut_vert]);
107  int ne = vert_to_elem->RowSize(cut_vert); // number of elements touching cut vertex
108  const int * els_adj_to_cut = vert_to_elem->GetRow(cut_vert); // elements touching cut vertex
109  for (int i = 0; i < ne; i++)
110  {
111  const int el_adj_to_cut = els_adj_to_cut[i];
112  if (isInDomain(el_adj_to_cut, getSubdomainAttributes(), parent_mesh) &&
113  sideOfCut(el_adj_to_cut, cut_vert, parent_mesh) == 1)
114  transition_els.Append(el_adj_to_cut);
115  }
116  }
117 
118  // share cut verts coords or global ids across all procs
119  int n_cut_vertices = global_cut_vert_ids.size();
120  std::vector<int> cut_vert_sizes(mpi_comm_size, 0);
121  MPI_Allgather(&n_cut_vertices,
122  1,
123  MPI_INT,
124  &cut_vert_sizes[0],
125  1,
126  MPI_INT,
127  getMFEMProblem().mesh().getMFEMParMesh().GetComm());
128  // Make an offset array and total the sizes.
129  std::vector<int> n_vert_offset(mpi_comm_size, 0);
130  for (int i = 1; i < mpi_comm_size; i++)
131  n_vert_offset[i] = n_vert_offset[i - 1] + cut_vert_sizes[i - 1];
132  int global_n_cut_vertices = 0;
133  for (int i = 0; i < mpi_comm_size; i++)
134  global_n_cut_vertices += cut_vert_sizes[i];
135 
136  // Gather the queries to all ranks.
137  std::vector<HYPRE_BigInt> all_cut_verts(global_n_cut_vertices, 0);
138  MPI_Allgatherv(&global_cut_vert_ids[0],
139  n_cut_vertices,
140  HYPRE_MPI_BIG_INT,
141  &all_cut_verts[0],
142  &cut_vert_sizes[0],
143  &n_vert_offset[0],
144  HYPRE_MPI_BIG_INT,
145  getMFEMProblem().mesh().getMFEMParMesh().GetComm());
146 
147  // Detect shared vertices and add corresponding elements
148  for (int g = 1, sv = 0; g < parent_mesh.GetNGroups(); g++)
149  {
150  for (int gv = 0; gv < parent_mesh.GroupNVertices(g); gv++, sv++)
151  {
152  // all elements touching this shared vertex should be updated
153  int cut_vert = parent_mesh.GroupVertex(g, gv);
154  for (std::size_t i = 0; i < all_cut_verts.size(); i += 1)
155  {
156  if (gi[cut_vert] == all_cut_verts[i]) // check if shared vertex is on the cut plane
157  {
158  int ne = vert_to_elem->RowSize(cut_vert); // number of elements touching cut vertex
159  const int * els_adj_to_cut =
160  vert_to_elem->GetRow(cut_vert); // elements touching cut vertex
161  for (int i = 0; i < ne; i++)
162  {
163  const int el_adj_to_cut = els_adj_to_cut[i];
164  if (isInDomain(el_adj_to_cut, getSubdomainAttributes(), parent_mesh) &&
165  sideOfCut(el_adj_to_cut, cut_vert, parent_mesh) == 1)
166  transition_els.Append(el_adj_to_cut);
167  }
168  }
169  }
170  }
171  }
172 
173  transition_els.Sort();
174  transition_els.Unique();
175 
176  setAttributes(parent_mesh, transition_els);
177 }
178 
179 mfem::Vector
180 MFEMCutTransitionSubMesh::findFaceNormal(const mfem::ParMesh & mesh, const int & face)
181 {
182  MFEM_ASSERT(mesh->SpatialDimension() == 3,
183  "MFEMCutTransitionSubMesh only works in 3-dimensional meshes!");
184  mfem::Vector normal;
185  mfem::Array<int> face_verts;
186  std::vector<mfem::Vector> v;
187  mesh.GetFaceVertices(face, face_verts);
188 
189  // First we get the coordinates of 3 vertices on the face
190  for (auto vtx : face_verts)
191  {
192  mfem::Vector vtx_coords(3);
193  for (int j = 0; j < 3; ++j)
194  vtx_coords[j] = mesh.GetVertex(vtx)[j];
195  v.push_back(vtx_coords);
196  }
197 
198  // Now we find the unit vector normal to the face
199  v[0] -= v[1];
200  v[1] -= v[2];
201  v[0].cross3D(v[1], normal);
202  normal /= normal.Norml2();
203  return normal;
204 }
205 
206 int
208  const int & el_vertex_on_cut,
209  mfem::ParMesh & parent_mesh)
210 {
211  const int sdim = parent_mesh.SpaceDimension();
212  mfem::Vector el_center(3);
213  parent_mesh.GetElementCenter(el, el_center);
214  mfem::Vector vertex_coords(parent_mesh.GetVertex(el_vertex_on_cut), sdim);
215  mfem::Vector relative_center(sdim);
216  for (int j = 0; j < sdim; j++)
217  relative_center[j] = el_center[j] - vertex_coords[j];
218  double side = _cut_normal * relative_center;
219  if (side > 0)
220  return 1;
221  else
222  return -1;
223 }
224 
225 void
226 MFEMCutTransitionSubMesh::setAttributes(mfem::ParMesh & parent_mesh,
227  mfem::Array<int> & transition_els)
228 {
229  // Generate a set of local new attributes for transition region elements
230  const int old_max_attr = parent_mesh.attributes.Max();
231  mfem::Array<int> new_attrs(old_max_attr);
232  for (int i = 0; i < new_attrs.Size(); ++i)
233  new_attrs[i] = i + 1;
234  for (int i = 0; i < transition_els.Size(); ++i)
235  {
236  const auto & transition_el = transition_els[i];
237  const int old_attr = parent_mesh.GetAttribute(transition_el);
238  new_attrs[old_attr - 1] = old_max_attr + old_attr % old_max_attr + 1;
239  }
240 
241  // Distribute local attribute IDs to other MPI ranks to create set of new
242  // global attribute IDs for the transition region.
243  mfem::Array<int> global_new_attrs(old_max_attr);
244  global_new_attrs = -1;
245  MPI_Allreduce(new_attrs,
246  global_new_attrs,
247  old_max_attr,
248  MPI_INT,
249  MPI_MAX,
250  getMFEMProblem().mesh().getMFEMParMesh().GetComm());
251 
252  // Set the new attribute IDs for transition region elements
253  for (int i = 0; i < transition_els.Size(); ++i)
254  {
255  const auto & transition_el = transition_els[i];
256  const int old_attr = parent_mesh.GetAttribute(transition_el);
257  if (old_attr < old_max_attr + 1)
258  parent_mesh.SetAttribute(transition_el, global_new_attrs[old_attr - 1]);
259  }
260 
261  mfem::AttributeSets & attr_sets = parent_mesh.attribute_sets;
262  mfem::AttributeSets & bdr_attr_sets = parent_mesh.bdr_attribute_sets;
263  // Create attribute set labelling the newly created transition region
264  // on one side of the cut
265  attr_sets.CreateAttributeSet(_transition_subdomain);
266  // Create attribute set labelling the entire closed geometry
267  attr_sets.SetAttributeSet(_closed_subdomain, getSubdomainAttributes());
268  // Add the new domain attributes to new attribute sets
269  const std::set<std::string> attr_set_names = attr_sets.GetAttributeSetNames();
270  for (int old_attr = 1; old_attr < global_new_attrs.Size() + 1; ++old_attr)
271  {
272  int new_attr = global_new_attrs[old_attr - 1];
273  // Add attributes only if they're transition region attributes
274  if (new_attr > old_max_attr)
275  {
276  attr_sets.AddToAttributeSet(_transition_subdomain, new_attr);
277  for (auto const & attr_set_name : attr_set_names)
278  {
279  const mfem::Array<int> & attr_set = attr_sets.GetAttributeSet(attr_set_name);
280  // If attribute set has the old attribute of the transition region, add the new one
281  if (attr_set.Find(old_attr) != -1)
282  attr_sets.AddToAttributeSet(attr_set_name, new_attr);
283  }
284  }
285  }
286 
287  // Create boundary attribute set labelling the exterior of the newly created
288  // transition region, excluding the cut
289  bdr_attr_sets.CreateAttributeSet(_transition_subdomain_boundary);
290  bdr_attr_sets.AddToAttributeSet(_transition_subdomain_boundary,
291  parent_mesh.bdr_attributes.Max() + 1);
292 
293  parent_mesh.SetAttributes();
294 }
295 
296 bool
298  const mfem::Array<int> & subdomains,
299  const mfem::ParMesh & mesh)
300 {
301  // element<0 for ghost elements
302  if (element < 0)
303  return true;
304 
305  for (const auto & subdomain : subdomains)
306  if (mesh.GetAttribute(element) == subdomain)
307  return true;
308  return false;
309 }
310 
311 #endif
Modifies the MFEM Mesh to label a subdomain consisting of elements adjacent to an interior surface on...
const SubdomainName & _closed_subdomain
virtual MFEMMesh & mesh() override
Overwritten mesh() method from base MooseMesh to retrieve the correct mesh type, in this case MFEMMes...
Definition: MFEMProblem.C:465
MeshBase & mesh
virtual void buildSubMesh() override
Constructs the submesh.
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
const BoundaryName & _transition_subdomain_boundary
const mfem::Array< int > & getSubdomainAttributes()
void addRequiredParam(const std::string &name, const std::string &doc_string)
This method adds a parameter and documentation string to the InputParameters object that will be extr...
void labelMesh(mfem::ParMesh &parent_mesh)
Add attributes to the parent mesh representing the cut transition region.
registerMooseObject("MooseApp", MFEMCutTransitionSubMesh)
int sideOfCut(const int &el, const int &el_vertex_on_cut, mfem::ParMesh &mesh)
Checks whether an element lies on the positive or negative side of the cut plane. ...
static InputParameters validParams()
Definition: MFEMSubMesh.C:16
MFEMCutTransitionSubMesh(const InputParameters &parameters)
const SubdomainName & _transition_subdomain
Base class for construction of a mfem::ParSubMesh object.
Definition: MFEMSubMesh.h:22
bool isInDomain(const int &el, const mfem::Array< int > &subdomains, const mfem::ParMesh &mesh)
Checks whether a given element is within a certain domain or vector of domains.
std::shared_ptr< mfem::ParSubMesh > _cut_submesh
void setAttributes(mfem::ParMesh &parent_mesh, mfem::Array< int > &transition_els)
Set new attributes for the provided transition region elements.
mfem::ParMesh & getMFEMParMesh()
Accessors for the _mfem_par_mesh object.
Definition: MFEMMesh.h:36
MFEMProblem & getMFEMProblem()
Returns a reference to the MFEMProblem instance.
static InputParameters validParams()
void addClassDescription(const std::string &doc_string)
This method adds a description of the class that will be displayed in the input file syntax dump...
const mfem::ParMesh & getMesh() const
mfem::Vector findFaceNormal(const mfem::ParMesh &mesh, const int &face)
Finds the normal vector of a face in the mesh from its vertices.
std::shared_ptr< mfem::ParSubMesh > _submesh
Stores the constructed submesh.
Definition: MFEMSubMesh.h:39
static InputParameters validParams()
Base class for construction of an object that is restricted to a subset of subdomains of the problem ...