https://mooseframework.inl.gov
PolycrystalICTools.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 #include "PolycrystalICTools.h"
11 
12 // MOOSE includes
13 #include "MooseMesh.h"
14 #include "MooseVariable.h"
15 #include "PetscSupport.h"
16 
17 #include "libmesh/mesh_tools.h"
18 #include "libmesh/periodic_boundaries.h"
19 #include "libmesh/point_locator_base.h"
20 
21 using namespace libMesh;
22 
23 namespace GraphColoring
24 {
25 const unsigned int INVALID_COLOR = std::numeric_limits<unsigned int>::max();
26 }
27 
28 namespace PolycrystalICTools
29 {
30 const unsigned int HALO_THICKNESS = 4;
31 }
32 
33 // Forward declarations
34 bool colorGraph(const PolycrystalICTools::AdjacencyMatrix<Real> & adjacency_matrix,
35  std::vector<unsigned int> & colors,
36  unsigned int n_vertices,
37  unsigned int n_ops,
38  unsigned int vertex);
39 bool isGraphValid(const PolycrystalICTools::AdjacencyMatrix<Real> & adjacency_matrix,
40  std::vector<unsigned int> & colors,
41  unsigned int n_vertices,
42  unsigned int vertex,
43  unsigned int color);
44 void visitElementalNeighbors(const Elem * elem,
45  const MeshBase & mesh,
46  const PointLocatorBase & point_locator,
47  const PeriodicBoundaries * pb,
48  std::set<dof_id_type> & halo_ids);
49 
50 std::vector<unsigned int>
51 PolycrystalICTools::assignPointsToVariables(const std::vector<Point> & centerpoints,
52  const Real op_num,
53  const MooseMesh & mesh,
54  const MooseVariable & var)
55 {
56  Real grain_num = centerpoints.size();
57 
58  std::vector<unsigned int> assigned_op(grain_num);
59  std::vector<int> min_op_ind(op_num);
60  std::vector<Real> min_op_dist(op_num);
61 
62  // Assign grains to specific order parameters in a way that maximizes the distance
63  for (unsigned int grain = 0; grain < grain_num; grain++)
64  {
65  // Determine the distance to the closest center assigned to each order parameter
66  if (grain >= op_num)
67  {
68  // We can set the array to the distances to the grains 0..op_num-1 (see assignment in the else
69  // case)
70  for (unsigned int i = 0; i < op_num; ++i)
71  {
72  min_op_dist[i] =
73  mesh.minPeriodicDistance(var.number(), centerpoints[grain], centerpoints[i]);
74  min_op_ind[assigned_op[i]] = i;
75  }
76 
77  // Now check if any of the extra grains are even closer
78  for (unsigned int i = op_num; i < grain; ++i)
79  {
80  Real dist = mesh.minPeriodicDistance(var.number(), centerpoints[grain], centerpoints[i]);
81  if (min_op_dist[assigned_op[i]] > dist)
82  {
83  min_op_dist[assigned_op[i]] = dist;
84  min_op_ind[assigned_op[i]] = i;
85  }
86  }
87  }
88  else
89  {
90  assigned_op[grain] = grain;
91  continue;
92  }
93 
94  // Assign the current center point to the order parameter that is furthest away.
95  unsigned int mx_ind = 0;
96  for (unsigned int i = 1; i < op_num; ++i) // Find index of max
97  if (min_op_dist[mx_ind] < min_op_dist[i])
98  mx_ind = i;
99 
100  assigned_op[grain] = mx_ind;
101  }
102 
103  return assigned_op;
104 }
105 
106 unsigned int
108  const std::vector<Point> & centerpoints,
109  const MooseMesh & mesh,
110  const MooseVariable & var,
111  const Real maxsize)
112 {
113  unsigned int grain_num = centerpoints.size();
114 
115  Real min_distance = maxsize;
116  unsigned int min_index = grain_num;
117  // Loops through all of the grain centers and finds the center that is closest to the point p
118  for (unsigned int grain = 0; grain < grain_num; grain++)
119  {
120  Real distance = mesh.minPeriodicDistance(var.number(), centerpoints[grain], p);
121 
122  if (min_distance > distance)
123  {
124  min_distance = distance;
125  min_index = grain;
126  }
127  }
128 
129  if (min_index >= grain_num)
130  mooseError("ERROR in PolycrystalVoronoiVoidIC: didn't find minimum values in grain_value_calc");
131 
132  return min_index;
133 }
134 
137  const std::map<dof_id_type, unsigned int> & entity_to_grain,
138  MooseMesh & mesh,
139  const PeriodicBoundaries * pb,
140  unsigned int n_grains,
141  bool is_elemental)
142 {
143  if (is_elemental)
144  return buildElementalGrainAdjacencyMatrix(entity_to_grain, mesh, pb, n_grains);
145  else
146  return buildNodalGrainAdjacencyMatrix(entity_to_grain, mesh, pb, n_grains);
147 }
148 
151  const std::map<dof_id_type, unsigned int> & element_to_grain,
152  MooseMesh & mesh,
153  const PeriodicBoundaries * pb,
154  unsigned int n_grains)
155 {
156  AdjacencyMatrix<Real> adjacency_matrix(n_grains);
157 
158  // We can't call this in the constructor, it appears that _mesh_ptr is always NULL there.
159  mesh.errorIfDistributedMesh("advanced_op_assignment = true");
160 
161  std::vector<const Elem *> all_active_neighbors;
162 
163  std::vector<std::set<dof_id_type>> local_ids(n_grains);
164  std::vector<std::set<dof_id_type>> halo_ids(n_grains);
165 
166  std::unique_ptr<PointLocatorBase> point_locator = mesh.getMesh().sub_point_locator();
167  for (const auto & elem : mesh.getMesh().active_element_ptr_range())
168  {
169  std::map<dof_id_type, unsigned int>::const_iterator grain_it =
170  element_to_grain.find(elem->id());
171  mooseAssert(grain_it != element_to_grain.end(), "Element not found in map");
172  unsigned int my_grain = grain_it->second;
173 
174  all_active_neighbors.clear();
175  // Loop over all neighbors (at the the same level as the current element)
176  for (unsigned int i = 0; i < elem->n_neighbors(); ++i)
177  {
178  const Elem * neighbor_ancestor = elem->topological_neighbor(i, mesh, *point_locator, pb);
179  if (neighbor_ancestor)
180  // Retrieve only the active neighbors for each side of this element, append them to the list
181  // of active neighbors
183  all_active_neighbors, elem, mesh, *point_locator, pb, false);
184  }
185 
186  // Loop over all active element neighbors
187  for (std::vector<const Elem *>::const_iterator neighbor_it = all_active_neighbors.begin();
188  neighbor_it != all_active_neighbors.end();
189  ++neighbor_it)
190  {
191  const Elem * neighbor = *neighbor_it;
192  std::map<dof_id_type, unsigned int>::const_iterator grain_it2 =
193  element_to_grain.find(neighbor->id());
194  mooseAssert(grain_it2 != element_to_grain.end(), "Element not found in map");
195  unsigned int their_grain = grain_it2->second;
196 
197  if (my_grain != their_grain)
198  {
207  // First add corresponding element and grain information
208  local_ids[my_grain].insert(elem->id());
209  local_ids[their_grain].insert(neighbor->id());
210 
211  // Now add opposing element and grain information
212  halo_ids[my_grain].insert(neighbor->id());
213  halo_ids[their_grain].insert(elem->id());
214  }
215  // adjacency_matrix[my_grain][their_grain] = 1;
216  }
217  }
218 
219  // Build up the halos
220  std::set<dof_id_type> set_difference;
221  for (unsigned int i = 0; i < n_grains; ++i)
222  {
223  std::set<dof_id_type> orig_halo_ids(halo_ids[i]);
224 
225  for (unsigned int halo_level = 0; halo_level < PolycrystalICTools::HALO_THICKNESS; ++halo_level)
226  {
227  for (std::set<dof_id_type>::iterator entity_it = orig_halo_ids.begin();
228  entity_it != orig_halo_ids.end();
229  ++entity_it)
230  {
231  if (true)
233  mesh.elemPtr(*entity_it), mesh.getMesh(), *point_locator, pb, halo_ids[i]);
234  else
235  mooseError("Unimplemented");
236  }
237 
238  set_difference.clear();
239  std::set_difference(
240  halo_ids[i].begin(),
241  halo_ids[i].end(),
242  local_ids[i].begin(),
243  local_ids[i].end(),
244  std::insert_iterator<std::set<dof_id_type>>(set_difference, set_difference.begin()));
245 
246  halo_ids[i].swap(set_difference);
247  }
248  }
249 
250  // Finally look at the halo intersections to build the connectivity graph
251  std::set<dof_id_type> set_intersection;
252  for (unsigned int i = 0; i < n_grains; ++i)
253  for (unsigned int j = i + 1; j < n_grains; ++j)
254  {
255  set_intersection.clear();
256  std::set_intersection(
257  halo_ids[i].begin(),
258  halo_ids[i].end(),
259  halo_ids[j].begin(),
260  halo_ids[j].end(),
261  std::insert_iterator<std::set<dof_id_type>>(set_intersection, set_intersection.begin()));
262 
263  if (!set_intersection.empty())
264  {
265  adjacency_matrix(i, j) = 1.;
266  adjacency_matrix(j, i) = 1.;
267  }
268  }
269 
270  return adjacency_matrix;
271 }
272 
275  const std::map<dof_id_type, unsigned int> & node_to_grain,
276  MooseMesh & mesh,
277  const PeriodicBoundaries * /*pb*/,
278  unsigned int n_grains)
279 {
280  // Build node to elem map
281  std::vector<std::vector<const Elem *>> nodes_to_elem_map;
282  MeshTools::build_nodes_to_elem_map(mesh.getMesh(), nodes_to_elem_map);
283 
284  AdjacencyMatrix<Real> adjacency_matrix(n_grains);
285 
286  const auto end = mesh.getMesh().active_nodes_end();
287  for (auto nl = mesh.getMesh().active_nodes_begin(); nl != end; ++nl)
288  {
289  const Node * node = *nl;
290  std::map<dof_id_type, unsigned int>::const_iterator grain_it = node_to_grain.find(node->id());
291  mooseAssert(grain_it != node_to_grain.end(), "Node not found in map");
292  unsigned int my_grain = grain_it->second;
293 
294  std::vector<const Node *> nodal_neighbors;
295  MeshTools::find_nodal_neighbors(mesh.getMesh(), *node, nodes_to_elem_map, nodal_neighbors);
296 
297  // Loop over all nodal neighbors
298  for (unsigned int i = 0; i < nodal_neighbors.size(); ++i)
299  {
300  const Node * neighbor_node = nodal_neighbors[i];
301  std::map<dof_id_type, unsigned int>::const_iterator grain_it2 =
302  node_to_grain.find(neighbor_node->id());
303  mooseAssert(grain_it2 != node_to_grain.end(), "Node not found in map");
304  unsigned int their_grain = grain_it2->second;
305 
306  if (my_grain != their_grain)
314  adjacency_matrix(my_grain, their_grain) = 1.;
315  }
316  }
317 
318  return adjacency_matrix;
319 }
320 
321 std::vector<unsigned int>
323  unsigned int n_grains,
324  unsigned int n_ops,
325  const MooseEnum & coloring_algorithm)
326 {
327  std::vector<unsigned int> grain_to_op(n_grains, GraphColoring::INVALID_COLOR);
328 
329  // Use a simple backtracking coloring algorithm
330  if (coloring_algorithm == "bt")
331  {
332  if (!colorGraph(adjacency_matrix, grain_to_op, n_grains, n_ops, 0))
333  mooseError(
334  "Unable to find a valid Grain to op configuration, do you have enough op variables?");
335  }
336  else // PETSc Coloring algorithms
337  {
338  const std::string & ca_str = coloring_algorithm;
339  Real * am_data = adjacency_matrix.rawDataPtr();
341  am_data, n_grains, n_ops, grain_to_op, ca_str.c_str());
342  }
343 
344  return grain_to_op;
345 }
346 
347 MooseEnum
349 {
350  return MooseEnum("legacy bt jp power greedy", "legacy");
351 }
352 
353 std::string
355 {
356  return "The grain neighbor graph coloring algorithm to use. \"legacy\" is the original "
357  "algorithm "
358  "which does not guarantee a valid coloring. \"bt\" is a simple backtracking algorithm "
359  "which will produce a valid coloring but has potential exponential run time. The "
360  "remaining algorithms require PETSc but are recommended for larger problems (See "
361  "MatColoringType)";
362 }
363 
367 void
369  const MeshBase & mesh,
370  const PointLocatorBase & point_locator,
371  const PeriodicBoundaries * pb,
372  std::set<dof_id_type> & halo_ids)
373 {
374  mooseAssert(elem, "Elem is NULL");
375 
376  std::vector<const Elem *> all_active_neighbors;
377 
378  // Loop over all neighbors (at the the same level as the current element)
379  for (unsigned int i = 0; i < elem->n_neighbors(); ++i)
380  {
381  const Elem * neighbor_ancestor = elem->topological_neighbor(i, mesh, point_locator, pb);
382  if (neighbor_ancestor)
383  // Retrieve only the active neighbors for each side of this element, append them to the list
384  // of active neighbors
386  all_active_neighbors, elem, mesh, point_locator, pb, false);
387  }
388 
389  // Loop over all active element neighbors
390  for (std::vector<const Elem *>::const_iterator neighbor_it = all_active_neighbors.begin();
391  neighbor_it != all_active_neighbors.end();
392  ++neighbor_it)
393  if (*neighbor_it)
394  halo_ids.insert((*neighbor_it)->id());
395 }
396 
400 bool
402  std::vector<unsigned int> & colors,
403  unsigned int n_vertices,
404  unsigned int n_colors,
405  unsigned int vertex)
406 {
407  // Base case: All grains are assigned
408  if (vertex == n_vertices)
409  return true;
410 
411  // Consider this grain and try different ops
412  for (unsigned int color_idx = 0; color_idx < n_colors; ++color_idx)
413  {
414  // We'll try to spread these colors around a bit rather than
415  // packing them all on the first few colors if we have several colors.
416  unsigned int color = (vertex + color_idx) % n_colors;
417 
418  if (isGraphValid(adjacency_matrix, colors, n_vertices, vertex, color))
419  {
420  colors[vertex] = color;
421 
422  if (colorGraph(adjacency_matrix, colors, n_vertices, n_colors, vertex + 1))
423  return true;
424 
425  // Backtrack...
426  colors[vertex] = GraphColoring::INVALID_COLOR;
427  }
428  }
429 
430  return false;
431 }
432 
433 bool
435  std::vector<unsigned int> & colors,
436  unsigned int n_vertices,
437  unsigned int vertex,
438  unsigned int color)
439 {
440  // See if the proposed color is valid based on the current neighbor colors
441  for (unsigned int neighbor = 0; neighbor < n_vertices; ++neighbor)
442  if (adjacency_matrix(vertex, neighbor) && color == colors[neighbor])
443  return false;
444  return true;
445 }
void active_family_tree_by_topological_neighbor(std::vector< const Elem * > &family, const Elem *neighbor, const MeshBase &mesh, const PointLocatorBase &point_locator, const PeriodicBoundaries *pb, bool reset=true) const
const unsigned int HALO_THICKNESS
std::unique_ptr< PointLocatorBase > sub_point_locator() const
const Elem * topological_neighbor(const unsigned int i, const MeshBase &mesh, const PointLocatorBase &point_locator, const PeriodicBoundaries *pb) const
AdjacencyMatrix< Real > buildNodalGrainAdjacencyMatrix(const std::map< dof_id_type, unsigned int > &node_to_grain, MooseMesh &mesh, const libMesh::PeriodicBoundaries *pb, unsigned int n_grains)
void mooseError(Args &&... args)
unsigned int number() const
bool isGraphValid(const PolycrystalICTools::AdjacencyMatrix< Real > &adjacency_matrix, std::vector< unsigned int > &colors, unsigned int n_vertices, unsigned int vertex, unsigned int color)
const unsigned int INVALID_COLOR
AdjacencyMatrix< Real > buildGrainAdjacencyMatrix(const std::map< dof_id_type, unsigned int > &entity_to_grain, MooseMesh &mesh, const libMesh::PeriodicBoundaries *pb, unsigned int n_grains, bool is_elemental)
void visitElementalNeighbors(const Elem *elem, const MeshBase &mesh, const PointLocatorBase &point_locator, const PeriodicBoundaries *pb, std::set< dof_id_type > &halo_ids)
Utility routines.
bool colorGraph(const PolycrystalICTools::AdjacencyMatrix< Real > &adjacency_matrix, std::vector< unsigned int > &colors, unsigned int n_vertices, unsigned int n_ops, unsigned int vertex)
Backtracking graph coloring routines.
MeshBase & mesh
void find_nodal_neighbors(const MeshBase &mesh, const Node &n, const std::vector< std::vector< const Elem * >> &nodes_to_elem_map, std::vector< const Node * > &neighbors)
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
The following methods are specializations for using the Parallel::packed_range_* routines for a vecto...
unsigned int assignPointToGrain(const Point &p, const std::vector< Point > &centerpoints, const MooseMesh &mesh, const MooseVariable &var, const Real maxsize)
Real distance(const Point &p)
dof_id_type id() const
std::vector< unsigned int > assignPointsToVariables(const std::vector< Point > &centerpoints, const Real op_num, const MooseMesh &mesh, const MooseVariable &var)
MooseEnum coloringAlgorithms()
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
AdjacencyMatrix< Real > buildElementalGrainAdjacencyMatrix(const std::map< dof_id_type, unsigned int > &element_to_grain, MooseMesh &mesh, const libMesh::PeriodicBoundaries *pb, unsigned int n_grains)
unsigned int n_neighbors() const
void colorAdjacencyMatrix(PetscScalar *adjacency_matrix, unsigned int size, unsigned int colors, std::vector< unsigned int > &vertex_colors, const char *coloring_algorithm)
static const std::complex< double > j(0, 1)
Complex number "j" (also known as "i")
Simple 2D block matrix indicating graph adjacency.
std::vector< unsigned int > assignOpsToGrains(AdjacencyMatrix< Real > &adjacency_matrix, unsigned int n_grains, unsigned int n_ops, const MooseEnum &coloring_algorithm)
std::string coloringAlgorithmDescriptions()