www.mooseframework.org
MeshGeneratorPD.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://www.mooseframework.org
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 #include "MeshGeneratorPD.h"
11 #include "PeridynamicsMesh.h"
12 #include "CastUniquePointer.h"
13 
14 #include "libmesh/edge_edge2.h"
15 #include "libmesh/face_tri3.h"
16 #include "libmesh/cell_tet4.h"
17 
18 registerMooseObject("PeridynamicsApp", MeshGeneratorPD);
19 
20 template <>
21 InputParameters
23 {
24  InputParameters params = validParams<MeshGenerator>();
25  params.addClassDescription("Mesh generator class to convert FE mesh to Peridynamics mesh");
26 
27  params.addRequiredParam<MeshGeneratorName>("input",
28  "The mesh based on which PD mesh will be created");
29  params.addParam<std::vector<SubdomainID>>("convert_block_ids",
30  "IDs of the FE mesh blocks to be converted to PD mesh");
31  params.addParam<std::vector<SubdomainID>>(
32  "non_convert_block_ids",
33  "IDs of the FE mesh blocks to not be converted to PD mesh. This should only be used when the "
34  "number of to-be-converted FE blocks is considerable.");
35  params.addRequiredParam<bool>(
36  "retain_fe_mesh", "Whether to retain the FE mesh or not after conversion into PD mesh");
37  params.addParam<bool>("single_converted_block",
38  false,
39  "Whether to combine converted PD mesh blocks into a single block. This is "
40  "used when all PD blocks have the same properties");
41  params.addParam<bool>(
42  "construct_peridynamics_sideset",
43  false,
44  "Whether to construct peridynamics sidesets based on the sidesets in original FE mesh");
45  params.addParam<std::vector<SubdomainID>>(
46  "connect_block_id_pairs",
47  "List of block id pairs between which will be connected via interfacial bonds");
48  params.addParam<std::vector<SubdomainID>>(
49  "non_connect_block_id_pairs", "List of block pairs between which will not be connected");
50  params.addParam<bool>("single_interface_block",
51  false,
52  "Whether to combine interface blocks into a single block. This is used "
53  "when all interface blocks have the same properties");
54 
55  return params;
56 }
57 
58 MeshGeneratorPD::MeshGeneratorPD(const InputParameters & parameters)
59  : MeshGenerator(parameters),
60  _input(getMesh("input")),
61  _has_conv_blk_ids(isParamValid("convert_block_ids")),
62  _has_non_conv_blk_ids(isParamValid("non_convert_block_ids")),
63  _retain_fe_mesh(getParam<bool>("retain_fe_mesh")),
64  _single_converted_blk(getParam<bool>("single_converted_block")),
65  _construct_pd_sideset(getParam<bool>("construct_peridynamics_sideset")),
66  _has_connect_blk_id_pairs(isParamValid("connect_block_id_pairs")),
67  _has_non_connect_blk_id_pairs(isParamValid("non_connect_block_id_pairs")),
68  _single_interface_blk(getParam<bool>("single_interface_block"))
69 {
71  mooseError("Please specifiy either 'convert_block_ids' or 'non_convert_block_ids'!");
72 
74  {
75  std::vector<SubdomainID> ids = getParam<std::vector<SubdomainID>>("non_convert_block_ids");
76  for (unsigned int i = 0; i < ids.size(); ++i)
77  _non_conv_blk_ids.insert(ids[i]);
78  }
79 
81  {
82  std::vector<SubdomainID> ids = getParam<std::vector<SubdomainID>>("convert_block_ids");
83  for (unsigned int i = 0; i < ids.size(); ++i)
84  _conv_blk_ids.insert(ids[i]);
85  }
86 
88  mooseError("Please specifiy either 'connect_block_id_pairs' or 'non_connect_block_id_pairs'!");
89 
90  _connect_blk_id_pairs.clear();
92  {
93  std::vector<SubdomainID> ids = getParam<std::vector<SubdomainID>>("connect_block_id_pairs");
94 
95  if (ids.size() % 2 != 0)
96  mooseError("Input parameter 'connect_block_id_pairs' must contain even number of entries!");
97 
98  const unsigned int pairs = ids.size() / 2;
99  for (unsigned int i = 0; i < pairs; ++i) // consider the renumbering of IDs of converted blocks
100  _connect_blk_id_pairs.insert(std::make_pair(ids[2 * i] + 1000, ids[2 * i + 1] + 1000));
101  }
102 
105  {
106  std::vector<SubdomainID> ids = getParam<std::vector<SubdomainID>>("non_connect_block_id_pairs");
107 
108  if (ids.size() % 2 != 0)
109  mooseError(
110  "Input parameter 'non_connect_block_id_pairs' must contain even number of entries!");
111 
112  const unsigned int pairs = ids.size() / 2;
113  for (unsigned int i = 0; i < pairs; ++i) // consider the renumbering of IDs of converted blocks
114  _non_connect_blk_id_pairs.insert(std::make_pair(ids[2 * i] + 1000, ids[2 * i + 1] + 1000));
115  }
116 }
117 
118 std::unique_ptr<MeshBase>
120 {
121  // get the MeshBase object this generator will be working on
122  std::unique_ptr<MeshBase> old_mesh = std::move(_input);
123 
124  // STEP 1: obtain FE block(s) and elements to be converted to PD mesh
125 
126  // get the IDs of all available blocks in the input FE mesh
127  std::set<SubdomainID> all_blk_ids;
128  for (const auto & old_elem : old_mesh->element_ptr_range())
129  all_blk_ids.insert(old_elem->subdomain_id());
130 
131  // the maximum FE block ID, which will be used in determine the block ID for interfacial bond in
132  // the case of single interface block
133  const unsigned int max_fe_blk_id = *all_blk_ids.rbegin();
134 
135  // categorize mesh blocks into converted and non-converted blocks
136  if (_has_conv_blk_ids)
137  std::set_difference(all_blk_ids.begin(),
138  all_blk_ids.end(),
139  _conv_blk_ids.begin(),
140  _conv_blk_ids.end(),
141  std::inserter(_non_conv_blk_ids, _non_conv_blk_ids.begin()));
142  else if (_has_non_conv_blk_ids)
143  std::set_difference(all_blk_ids.begin(),
144  all_blk_ids.end(),
145  _non_conv_blk_ids.begin(),
146  _non_conv_blk_ids.end(),
147  std::inserter(_conv_blk_ids, _conv_blk_ids.begin()));
148  else // if no block ids provided by user, by default, convert all FE mesh to PD mesh
149  _conv_blk_ids = all_blk_ids;
150 
151  // the minimum converted FE block ID, which will be used to assign block ID for non-interfacial
152  // bond in the case of combine converted blocks
153  const unsigned int min_converted_fe_blk_id = *_conv_blk_ids.begin();
154 
155  // IDs of to-be-converted FE elems
156  std::set<dof_id_type> conv_elem_ids;
157  // retained FE mesh and non-converted FE mesh, if any
158  std::set<dof_id_type> fe_nodes_ids;
159  std::set<dof_id_type> fe_elems_ids;
160  for (const auto & old_elem : old_mesh->element_ptr_range())
161  if (_conv_blk_ids.count(old_elem->subdomain_id())) // record to-be-converted FE elem IDs
162  {
163  conv_elem_ids.insert(old_elem->id());
164  if (_retain_fe_mesh) // save converted elems and their nodes if need to be retained
165  {
166  fe_elems_ids.insert(old_elem->id());
167  for (unsigned int i = 0; i < old_elem->n_nodes(); ++i)
168  fe_nodes_ids.insert(old_elem->node_id(i));
169  }
170  }
171  else // save non-converted elements and their nodes
172  {
173  fe_elems_ids.insert(old_elem->id());
174  for (unsigned int i = 0; i < old_elem->n_nodes(); ++i)
175  fe_nodes_ids.insert(old_elem->node_id(i));
176  }
177 
178  // number of FE elements and nodes in the old mesh to be saved in the new mesh
179  dof_id_type n_fe_nodes = fe_nodes_ids.size();
180  dof_id_type n_fe_elems = fe_elems_ids.size();
181  dof_id_type n_phantom_elems = 0;
182 
183  // determine the number of phantom elements to be generated in the new mesh based on sideset in
184  // old mesh
185  BoundaryInfo & old_boundary_info = old_mesh->get_boundary_info();
186 
187  // save the IDs of FE sidesets (excluding constructed from nodesets) in old mesh
188  std::set<boundary_id_type> fe_sbnd_ids = old_boundary_info.get_side_boundary_ids();
189  // determine number of FE side elements, the number of actual phantom elements is less than or
190  // equal to the number of FE side elements, this number is used to reserve number of elements
191  // in the new mesh only
192  std::map<boundary_id_type, std::set<dof_id_type>> fe_sbnd_elem_ids;
193  auto fe_sbc_tuples = old_boundary_info.build_side_list();
194  // 0: element ID, 1: side ID, 2: boundary ID
195  for (const auto & sbct : fe_sbc_tuples)
196  {
197  fe_sbnd_elem_ids[std::get<2>(sbct)].insert(std::get<0>(sbct));
198  ++n_phantom_elems;
199  }
200 
201  // STEP 2: generate PD data based on to-be-converted FE mesh and prepare for new mesh
202 
203  PeridynamicsMesh & pd_mesh = dynamic_cast<PeridynamicsMesh &>(*_mesh);
204  // generate PD node data
206  *old_mesh, conv_elem_ids, _connect_blk_id_pairs, _non_connect_blk_id_pairs);
207 
208  // number of PD elements and nodes to be created
209  dof_id_type n_pd_nodes = pd_mesh.nPDNodes();
210  dof_id_type n_pd_bonds = pd_mesh.nPDBonds();
211 
212  // initialize an empty new mesh object
213  std::unique_ptr<MeshBase> new_mesh = _mesh->buildMeshBaseObject();
214  new_mesh->clear();
215  // set new mesh dimension
216  new_mesh->set_mesh_dimension(old_mesh->mesh_dimension());
217  new_mesh->set_spatial_dimension(old_mesh->spatial_dimension());
218  // reserve elements and nodes for the new mesh
219  new_mesh->reserve_nodes(n_pd_nodes + n_fe_nodes);
220  new_mesh->reserve_elem(n_pd_bonds + n_fe_elems + n_phantom_elems);
221 
222  BoundaryInfo & new_boundary_info = new_mesh->get_boundary_info();
223 
224  // STEP 3: add points of PD and FE (retained and/or non-converted) nodes, if any, to new mesh
225 
226  // save PD nodes to new mesh first
227  unsigned int new_node_id = 0;
228  // map of IDs of converted FE elements and PD nodes
229  std::map<dof_id_type, dof_id_type> fe_elem_pd_node_map;
230  for (const auto & eid : conv_elem_ids)
231  {
232  new_mesh->add_point(old_mesh->elem_ptr(eid)->centroid(), new_node_id);
233  fe_elem_pd_node_map.insert(std::make_pair(eid, new_node_id));
234 
235  ++new_node_id;
236  }
237  // then save both retained and non-converted FE nodes, if any, to the new mesh
238  // map of IDs of the same point in old and new meshes
239  std::map<dof_id_type, dof_id_type> fe_nodes_map;
240  for (const auto & nid : fe_nodes_ids)
241  {
242  new_mesh->add_point(*old_mesh->node_ptr(nid), new_node_id);
243  fe_nodes_map.insert(std::make_pair(old_mesh->node_ptr(nid)->id(), new_node_id));
244 
245  ++new_node_id;
246  }
247 
248  // STEP 4: generate PD, phantom, and FE elems using retained and/or non-converted meshes if any
249 
250  // first, generate PD elements for new mesh
251  unsigned int new_elem_id = 0;
252  for (unsigned int i = 0; i < n_pd_nodes; ++i)
253  {
254  std::vector<dof_id_type> pd_node_neighbors = pd_mesh.getNeighbors(i); // neighbors of a PD node
255  for (unsigned int j = 0; j < pd_node_neighbors.size(); ++j)
256  if (pd_node_neighbors[j] > i)
257  {
258  SubdomainID bid_i = pd_mesh.getNodeBlockID(i);
259  SubdomainID bid_j = pd_mesh.getNodeBlockID(pd_node_neighbors[j]);
260  Elem * new_elem = new Edge2;
261  new_elem->set_id(new_elem_id);
262  if (bid_i == bid_j) // assign block ID to PD non-interfacial elems
264  new_elem->subdomain_id() = min_converted_fe_blk_id + 1000;
265  else
266  new_elem->subdomain_id() = bid_i;
267  else if (_single_interface_blk) // assign block ID (max_fe_blk_id + 1 + 1000) to all PD
268  // interfacial elems
269  new_elem->subdomain_id() = max_fe_blk_id + 1 + 1000;
270  else // assign a new block ID (node i blk ID + node j blk ID) to this PD interfacial elems
271  new_elem->subdomain_id() = bid_i + bid_j;
272 
273  new_elem = new_mesh->add_elem(new_elem);
274  new_elem->set_node(0) = new_mesh->node_ptr(i);
275  new_elem->set_node(1) = new_mesh->node_ptr(pd_node_neighbors[j]);
276 
277  ++new_elem_id;
278  }
279  }
280 
281  if (_single_converted_blk) // update PD node block ID
282  pd_mesh.setNodeBlockID(min_converted_fe_blk_id + 1000);
283 
284  // then generate phantom elements for sidesets in PD mesh, this is optional
285  std::map<std::pair<dof_id_type, dof_id_type>, std::set<dof_id_type>> elem_edge_node;
287  for (const auto & bidit : fe_sbnd_ids)
288  for (const auto & eidit : fe_sbnd_elem_ids[bidit])
289  {
290  bool should_add = false;
291  Elem * old_elem = old_mesh->elem_ptr(eidit);
292  if (_conv_blk_ids.count(old_elem->subdomain_id()))
293  {
294  std::vector<dof_id_type> node_ids;
295  if (new_mesh->mesh_dimension() == 2) // 2D
296  {
297  node_ids.resize(3);
298  node_ids[0] = fe_elem_pd_node_map.at(eidit);
299  Point p0 = *new_mesh->node_ptr(node_ids[0]);
300  // search for two more nodes to construct a phantom 3-node triangular element
301  if (old_elem->n_nodes() == 3 ||
302  old_elem->n_nodes() == 6) // original triangular FE elements: 3-node or 6-node
303  {
304  // check existence of counterclockwise nodes ordering
305  for (unsigned int i = 0; i < old_elem->n_neighbors(); ++i)
306  {
307  Elem * nb = old_elem->neighbor_ptr(i);
308  if (nb != nullptr)
309  {
310  for (unsigned int j = 0; j < nb->n_neighbors(); ++j)
311  {
312  Elem * nb_nb = nb->neighbor_ptr(j);
313  if (nb_nb != nullptr && fe_sbnd_elem_ids[bidit].count(nb_nb->id()) &&
314  nb_nb->id() != eidit)
315  {
316  Point p1 = *new_mesh->node_ptr(fe_elem_pd_node_map.at(nb_nb->id()));
317  Point p2 = *new_mesh->node_ptr(fe_elem_pd_node_map.at(nb->id()));
318  Real val =
319  (p1(1) - p0(1)) * (p2(0) - p1(0)) - (p1(0) - p0(0)) * (p2(1) - p1(1));
320  if (val < 0) // counterclockwise
321  {
322  node_ids[1] = fe_elem_pd_node_map.at(nb_nb->id());
323  node_ids[2] = fe_elem_pd_node_map.at(nb->id());
324  should_add = true;
325  }
326  }
327  }
328  }
329  }
330  }
331  else // original quadrilateral FE elements: 4-node, 8-node and 9-node
332  {
333  // find potential nodes for construction of the phantom triangular element
334  std::vector<dof_id_type> boundary_node_ids;
335  for (unsigned int i = 0; i < old_elem->n_neighbors(); ++i)
336  {
337  Elem * nb = old_elem->neighbor_ptr(i);
338  if (nb != nullptr)
339  {
340  if (fe_sbnd_elem_ids[bidit].count(nb->id()))
341  boundary_node_ids.push_back(fe_elem_pd_node_map.at(nb->id()));
342  else
343  node_ids[2] = fe_elem_pd_node_map.at(nb->id());
344  }
345  }
346  // check existence of counterclockwise ordering based on the above found nodes
347  Point p2 = *new_mesh->node_ptr(node_ids[2]);
348  for (unsigned int i = 0; i < boundary_node_ids.size(); ++i)
349  {
350  Point p1 = *new_mesh->node_ptr(boundary_node_ids[i]);
351  Real val = (p1(1) - p0(1)) * (p2(0) - p1(0)) - (p1(0) - p0(0)) * (p2(1) - p1(1));
352  if (val < 0) // counterclockwise
353  {
354  node_ids[1] = boundary_node_ids[i];
355  should_add = true;
356  }
357  }
358  }
359 
360  if (should_add)
361  {
362  Elem * new_elem = new Tri3;
363  new_elem->set_id(new_elem_id);
365  new_elem->subdomain_id() = min_converted_fe_blk_id + 10000;
366  else
367  new_elem->subdomain_id() = old_elem->subdomain_id() + 10000;
368  new_elem = new_mesh->add_elem(new_elem);
369  new_elem->set_node(0) = new_mesh->node_ptr(node_ids[0]);
370  new_elem->set_node(1) = new_mesh->node_ptr(node_ids[1]);
371  new_elem->set_node(2) = new_mesh->node_ptr(node_ids[2]);
372 
373  ++new_elem_id;
374 
375  new_boundary_info.add_side(new_elem, 0, 1000 + bidit);
376  if (old_boundary_info.get_sideset_name(bidit) != "")
377  new_boundary_info.sideset_name(1000 + bidit) =
378  "pd_side_" + old_boundary_info.get_sideset_name(bidit);
379  }
380  }
381  else // 3D
382  {
383  node_ids.resize(4);
384  node_ids[0] = fe_elem_pd_node_map.at(eidit);
385  Point p0 = *new_mesh->node_ptr(node_ids[0]);
386  // search for three more nodes to construct a phantom 4-node tetrahedral element
387 
388  if (old_elem->n_nodes() == 4 ||
389  old_elem->n_nodes() == 10) // original tetrahedral FE elements: 4-node or 10-node
390  {
391  // construct phantom element based on original tet mesh
392  mooseError("Peridynamics sideset generation doesn't accept tetrahedral elements!");
393  }
394  else // original hexahedral FE elements
395  {
396  std::vector<dof_id_type> boundary_elem_ids;
397  for (unsigned int i = 0; i < old_elem->n_neighbors(); ++i)
398  {
399  Elem * nb = old_elem->neighbor_ptr(i);
400  if (nb != nullptr)
401  {
402  if (fe_sbnd_elem_ids[bidit].count(nb->id()))
403  boundary_elem_ids.push_back(nb->id());
404  else
405  node_ids[3] = fe_elem_pd_node_map.at(nb->id());
406  }
407  }
408  // choose three nodes ordered in a way such that the normal points to the fourth node
409  Point p3 = *new_mesh->node_ptr(node_ids[3]);
410  for (unsigned int i = 0; i < boundary_elem_ids.size(); ++i)
411  {
412  Elem * nb_i = old_mesh->elem_ptr(boundary_elem_ids[i]);
413  for (unsigned int j = i + 1; j < boundary_elem_ids.size(); ++j)
414  {
415  should_add = false;
416  Elem * nb_j = old_mesh->elem_ptr(boundary_elem_ids[j]);
417  unsigned int common_nb = 0;
418  for (unsigned int k = 0; k < nb_j->n_neighbors();
419  ++k) // check whether nb_i and nb_j shares two common neighbors
420  {
421  if (nb_j->neighbor_ptr(k) != nullptr &&
422  nb_i->has_neighbor(nb_j->neighbor_ptr(k)))
423  ++common_nb;
424  }
425  if (common_nb == 2)
426  {
427  should_add = true;
428  // check whether this new elem overlaps with already created elems by the saved
429  // edge and nodes of previously created phantom elems
430  std::pair<dof_id_type, dof_id_type> pair;
431  if (eidit < boundary_elem_ids[i])
432  pair = std::make_pair(eidit, boundary_elem_ids[i]);
433  else
434  pair = std::make_pair(boundary_elem_ids[i], eidit);
435 
436  if (elem_edge_node.count(pair))
437  {
438  for (const auto & nbid : elem_edge_node[pair])
439  if (nbid != old_elem->id() && old_mesh->elem_ptr(nbid)->has_neighbor(nb_j))
440  should_add = false;
441  }
442 
443  if (eidit < boundary_elem_ids[j])
444  pair = std::make_pair(eidit, boundary_elem_ids[j]);
445  else
446  pair = std::make_pair(boundary_elem_ids[j], eidit);
447  if (elem_edge_node.count(pair))
448  {
449  for (const auto & nbid : elem_edge_node[pair])
450  if (nbid != old_elem->id() && old_mesh->elem_ptr(nbid)->has_neighbor(nb_i))
451  should_add = false;
452  }
453 
454  if (should_add)
455  {
456  Point p1 = *new_mesh->node_ptr(fe_elem_pd_node_map.at(boundary_elem_ids[i]));
457  Point p2 = *new_mesh->node_ptr(fe_elem_pd_node_map.at(boundary_elem_ids[j]));
458  // check whether the normal of face formed by p0, p1 and p2 points to p3
459  Real val =
460  ((p1(1) - p0(1)) * (p2(2) - p0(2)) - (p1(2) - p0(2)) * (p2(1) - p0(1))) *
461  (p3(0) - p0(0)) +
462  ((p1(2) - p0(2)) * (p2(0) - p0(0)) - (p1(0) - p0(0)) * (p2(2) - p0(2))) *
463  (p3(1) - p0(1)) +
464  ((p1(0) - p0(0)) * (p2(1) - p0(1)) - (p1(1) - p0(1)) * (p2(0) - p0(0))) *
465  (p3(2) - p0(2));
466  if (val > 0) // normal point to p3
467  {
468  node_ids[1] = fe_elem_pd_node_map.at(boundary_elem_ids[i]);
469  node_ids[2] = fe_elem_pd_node_map.at(boundary_elem_ids[j]);
470  }
471  else
472  {
473  node_ids[1] = fe_elem_pd_node_map.at(boundary_elem_ids[j]);
474  node_ids[2] = fe_elem_pd_node_map.at(boundary_elem_ids[i]);
475  }
476 
477  // construct the new phantom element
478  Elem * new_elem = new Tet4;
479  new_elem->set_id(new_elem_id);
481  new_elem->subdomain_id() = min_converted_fe_blk_id + 10000;
482  else
483  new_elem->subdomain_id() = old_elem->subdomain_id() + 10000;
484  new_elem = new_mesh->add_elem(new_elem);
485  new_elem->set_node(0) = new_mesh->node_ptr(node_ids[0]);
486  new_elem->set_node(1) = new_mesh->node_ptr(node_ids[1]);
487  new_elem->set_node(2) = new_mesh->node_ptr(node_ids[2]);
488  new_elem->set_node(3) = new_mesh->node_ptr(node_ids[3]);
489 
490  // save the edges and nodes used in the new phantom elem
491  if (eidit < boundary_elem_ids[i])
492  elem_edge_node[std::make_pair(eidit, boundary_elem_ids[i])].insert(
493  boundary_elem_ids[j]);
494  else
495  elem_edge_node[std::make_pair(boundary_elem_ids[i], eidit)].insert(
496  boundary_elem_ids[j]);
497 
498  if (eidit < boundary_elem_ids[j])
499  elem_edge_node[std::make_pair(eidit, boundary_elem_ids[j])].insert(
500  boundary_elem_ids[i]);
501  else
502  elem_edge_node[std::make_pair(boundary_elem_ids[j], eidit)].insert(
503  boundary_elem_ids[i]);
504 
505  ++new_elem_id;
506 
507  new_boundary_info.add_side(new_elem, 0, 1000 + bidit);
508  if (old_boundary_info.get_sideset_name(bidit) != "")
509  new_boundary_info.sideset_name(1000 + bidit) =
510  "pd_side_" + old_boundary_info.get_sideset_name(bidit);
511  }
512  }
513  }
514  }
515  }
516  }
517  }
518  }
519 
520  // next, save non-converted or retained FE elements if any to new mesh
521  std::map<dof_id_type, dof_id_type> fe_elems_map; // IDs of the same elem in the old and new meshes
522  for (const auto & eid : fe_elems_ids)
523  {
524  Elem * old_elem = old_mesh->elem_ptr(eid);
525  Elem * new_elem = Elem::build(old_elem->type()).release();
526  new_elem->set_id(new_elem_id);
527  new_elem->subdomain_id() = old_elem->subdomain_id();
528  new_elem = new_mesh->add_elem(new_elem);
529  for (unsigned int j = 0; j < old_elem->n_nodes(); ++j)
530  new_elem->set_node(j) = new_mesh->node_ptr(fe_nodes_map.at(old_elem->node_ptr(j)->id()));
531 
532  fe_elems_map.insert(std::make_pair(old_elem->id(), new_elem_id));
533 
534  ++new_elem_id;
535  }
536 
537  // STEP 5: convert old boundary_info to new boundary_info
538 
539  // peridynamics ONLY accept nodesets and sidesets
540  // nodeset consisting single node in the converted FE block will no longer be available
541  if (old_boundary_info.n_edge_conds())
542  mooseError("PeridynamicsMesh doesn't support edgesets!");
543 
544  // check the existence of nodesets, if exist, build sidesets in case this hasn't been done yet
545  if (old_boundary_info.n_nodeset_conds())
546  old_boundary_info.build_side_list_from_node_list();
547 
548  // first, create a tuple to collect all sidesets (including those converted from nodesets) in the
549  // old mesh
550  auto old_fe_sbc_tuples = old_boundary_info.build_side_list();
551  // 0: element ID, 1: side ID, 2: boundary ID
552  // map of set of elem IDs connected to each boundary in the old mesh
553  std::map<boundary_id_type, std::set<dof_id_type>> old_fe_bnd_elem_ids;
554  // map of set of side ID for each elem in the old mesh
555  std::map<dof_id_type, std::map<boundary_id_type, dof_id_type>> old_fe_elem_bnd_side_ids;
556  for (const auto & sbct : old_fe_sbc_tuples)
557  {
558  old_fe_bnd_elem_ids[std::get<2>(sbct)].insert(std::get<0>(sbct));
559  old_fe_elem_bnd_side_ids[std::get<0>(sbct)].insert(
560  std::make_pair(std::get<2>(sbct), std::get<1>(sbct)));
561  }
562 
563  // next, convert element lists in old mesh to PD nodesets in new mesh
564  std::set<boundary_id_type> old_side_bid(old_boundary_info.get_side_boundary_ids());
565 
566  // loop through all old FE _sideset_ boundaries
567  for (const auto & sbid : old_side_bid)
568  for (const auto & beid : old_fe_bnd_elem_ids[sbid])
569  if (conv_elem_ids.count(beid)) // for converted FE mesh
570  {
571  // save corresponding boundaries on converted FE mesh to PD nodes
572  new_boundary_info.add_node(new_mesh->node_ptr(fe_elem_pd_node_map.at(beid)), sbid + 1000);
573  if (old_boundary_info.get_sideset_name(sbid) != "")
574  new_boundary_info.nodeset_name(sbid + 1000) =
575  "pd_nodes_" + old_boundary_info.get_sideset_name(sbid);
576 
577  if (_retain_fe_mesh) // if retained, copy the corresponding boundaries, if any, to new mesh
578  // from old mesh
579  {
580  new_boundary_info.add_side(
581  new_mesh->elem_ptr(fe_elems_map[beid]), old_fe_elem_bnd_side_ids[beid][sbid], sbid);
582  new_boundary_info.sideset_name(sbid) = old_boundary_info.get_sideset_name(sbid);
583  }
584  }
585  else // for non-converted FE mesh, if any, copy the corresponding boundaries to new mesh
586  // from old mesh
587  {
588  new_boundary_info.add_side(
589  new_mesh->elem_ptr(fe_elems_map[beid]), old_fe_elem_bnd_side_ids[beid][sbid], sbid);
590  new_boundary_info.sideset_name(sbid) = old_boundary_info.get_sideset_name(sbid);
591  }
592 
593  // similar for sideset above, save _nodesets_ of non-converted and/or retained FE mesh, if any, to
594  // new mesh
595  auto old_node_bc_tuples = old_boundary_info.build_node_list();
596  // 0: node ID, 1: boundary ID
597  std::map<boundary_id_type, std::set<dof_id_type>> old_bnd_node_ids;
598  for (const auto & nbct : old_node_bc_tuples)
599  old_bnd_node_ids[std::get<1>(nbct)].insert(std::get<0>(nbct));
600 
601  std::set<boundary_id_type> old_node_bid(old_boundary_info.get_node_boundary_ids());
602 
603  for (const auto & nbid : old_node_bid)
604  for (const auto & bnid : old_bnd_node_ids[nbid])
605  if (fe_nodes_ids.count(bnid))
606  {
607  new_boundary_info.add_node(new_mesh->node_ptr(fe_nodes_map.at(bnid)), nbid);
608  new_boundary_info.nodeset_name(nbid) = old_boundary_info.get_sideset_name(nbid);
609  }
610 
611  // create nodesets to include all PD nodes for PD blocks in the new mesh
612  for (unsigned int i = 0; i < n_pd_nodes; ++i)
613  {
614  if (_conv_blk_ids.size() > 1 && !_single_converted_blk)
615  {
616  unsigned int j = 0;
617  for (const auto & blk_id : _conv_blk_ids)
618  {
619  ++j;
620  SubdomainID real_blk_id =
621  blk_id + 1000; // account for the 1000 increment after converting to PD mesh
622  if (pd_mesh.getNodeBlockID(i) == real_blk_id)
623  {
624  new_boundary_info.add_node(new_mesh->node_ptr(i), 999 - j);
625  new_boundary_info.nodeset_name(999 - j) =
626  "pd_nodes_block_" + Moose::stringify(blk_id + 1000);
627  }
628  }
629  }
630 
631  new_boundary_info.add_node(new_mesh->node_ptr(i), 999);
632  new_boundary_info.nodeset_name(999) = "pd_nodes_all";
633  }
634 
635  old_mesh.reset(); // destroy the old_mesh unique_ptr
636 
637  return dynamic_pointer_cast<MeshBase>(new_mesh);
638 }
MeshGeneratorPD::_conv_blk_ids
std::set< SubdomainID > _conv_blk_ids
Definition: MeshGeneratorPD.h:41
MeshGeneratorPD::_retain_fe_mesh
bool _retain_fe_mesh
flag to specify whether the FE mesh should be retained or not in addition to newly created PD mesh
Definition: MeshGeneratorPD.h:50
PeridynamicsMesh::getNodeBlockID
SubdomainID getNodeBlockID(dof_id_type node_id)
Function to return block ID for node node_id.
Definition: PeridynamicsMesh.C:405
MeshGeneratorPD::_input
std::unique_ptr< MeshBase > & _input
Reference to the input finite element mesh.
Definition: MeshGeneratorPD.h:36
validParams< MeshGeneratorPD >
InputParameters validParams< MeshGeneratorPD >()
Definition: MeshGeneratorPD.C:22
MeshGeneratorPD::generate
std::unique_ptr< MeshBase > generate()
Function to convert the finite element mesh to peridynamics mesh.
Definition: MeshGeneratorPD.C:119
MeshGeneratorPD::_non_connect_blk_id_pairs
std::multimap< SubdomainID, SubdomainID > _non_connect_blk_id_pairs
Definition: MeshGeneratorPD.h:67
MeshGeneratorPD::_non_conv_blk_ids
std::set< SubdomainID > _non_conv_blk_ids
Definition: MeshGeneratorPD.h:46
MeshGeneratorPD::_has_connect_blk_id_pairs
bool _has_connect_blk_id_pairs
pairs of converted FE block IDs when only certain blocks need to be connected using interfacial bonds...
Definition: MeshGeneratorPD.h:61
MeshGeneratorPD::_single_converted_blk
bool _single_converted_blk
flag to specify whether to combine converted PD mesh blocks into a single mesh block or not this is u...
Definition: MeshGeneratorPD.h:54
MeshGeneratorPD::_has_conv_blk_ids
bool _has_conv_blk_ids
block ID(s) of input FE mesh when only certain block(s) needs to be converted to PD mesh this is used...
Definition: MeshGeneratorPD.h:40
MeshGeneratorPD::_single_interface_blk
bool _single_interface_blk
flag to specify whether a single block should be used for all interfacial bonds this is used when all...
Definition: MeshGeneratorPD.h:71
MeshGeneratorPD::MeshGeneratorPD
MeshGeneratorPD(const InputParameters &parameters)
Definition: MeshGeneratorPD.C:58
PeridynamicsMesh::createPeridynamicsMeshData
void createPeridynamicsMeshData(MeshBase &fe_mesh, std::set< dof_id_type > converted_elem_id, std::multimap< SubdomainID, SubdomainID > connect_block_id_pairs, std::multimap< SubdomainID, SubdomainID > non_connect_block_id_pairs)
Function to assign values to member variables (PD mesh data) of this class this function will be call...
Definition: PeridynamicsMesh.C:138
PeridynamicsMesh.h
PeridynamicsMesh
Peridynamics mesh class.
Definition: PeridynamicsMesh.h:25
MeshGeneratorPD::_construct_pd_sideset
bool _construct_pd_sideset
flag to specify whether PD sideset should be constructed or not
Definition: MeshGeneratorPD.h:57
PeridynamicsMesh::setNodeBlockID
void setNodeBlockID(SubdomainID id)
Function to set block ID for all PD nodes.
Definition: PeridynamicsMesh.C:414
MeshGeneratorPD::_connect_blk_id_pairs
std::multimap< SubdomainID, SubdomainID > _connect_blk_id_pairs
Definition: MeshGeneratorPD.h:62
PeridynamicsMesh::nPDBonds
dof_id_type nPDBonds() const
Function to return number of PD Edge elements.
Definition: PeridynamicsMesh.C:132
MeshGeneratorPD.h
PeridynamicsMesh::getNeighbors
std::vector< dof_id_type > getNeighbors(dof_id_type node_id)
Function to return neighbor nodes indices for node node_id.
Definition: PeridynamicsMesh.C:362
MeshGeneratorPD::_has_non_connect_blk_id_pairs
bool _has_non_connect_blk_id_pairs
pairs of converted FE block IDs when only certain blocks need NOT to be connected this is usually use...
Definition: MeshGeneratorPD.h:66
registerMooseObject
registerMooseObject("PeridynamicsApp", MeshGeneratorPD)
MeshGeneratorPD
Generate peridynamics mesh based on finite element mesh.
Definition: MeshGeneratorPD.h:23
PeridynamicsMesh::nPDNodes
dof_id_type nPDNodes() const
Function to return number of PD nodes.
Definition: PeridynamicsMesh.C:126
MeshGeneratorPD::_has_non_conv_blk_ids
bool _has_non_conv_blk_ids
block ID(s) of input FE mesh when only certain block(s) needs NOT to be converted to PD mesh this is ...
Definition: MeshGeneratorPD.h:45