www.mooseframework.org
PolycrystalVoronoi.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 "PolycrystalVoronoi.h"
11 #include "IndirectSort.h"
12 #include "MooseRandom.h"
13 #include "MooseMesh.h"
14 #include "MooseVariable.h"
15 #include "NonlinearSystemBase.h"
16 #include "DelimitedFileReader.h"
17 
18 registerMooseObject("PhaseFieldApp", PolycrystalVoronoi);
19 
22 {
24  params.addClassDescription(
25  "Random Voronoi tessellation polycrystal (used by PolycrystalVoronoiAction)");
26  params.addParam<unsigned int>(
27  "grain_num", 0, "Number of grains being represented by the order parameters");
28  params.addParam<unsigned int>("rand_seed", 0, "The random seed");
29  params.addParam<bool>(
30  "columnar_3D", false, "3D microstructure will be columnar in the z-direction?");
31  params.addParam<bool>(
32  "use_kdtree", false, "Whether or not to use a KD tree to speedup grain search");
33  params.addRangeCheckedParam<unsigned int>("point_patch_size",
34  1,
35  "point_patch_size > 0",
36  "How many nearest points KDTree should return");
37  params.addRangeCheckedParam<unsigned int>("grain_patch_size",
38  10,
39  "grain_patch_size > 0",
40  "How many nearest grains KDTree should return");
41  params.addParam<FileName>(
42  "file_name",
43  "",
44  "File containing grain centroids, if file_name is provided, the centroids "
45  "from the file will be used.");
46  params.addParam<Real>("int_width", 0.0, "Width of diffuse interfaces");
47  return params;
48 }
49 
51  : PolycrystalUserObjectBase(parameters),
52  _grain_num(getParam<unsigned int>("grain_num")),
53  _columnar_3D(getParam<bool>("columnar_3D")),
54  _rand_seed(getParam<unsigned int>("rand_seed")),
55  _int_width(getParam<Real>("int_width")),
56  _file_name(getParam<FileName>("file_name")),
57  _kd_tree(nullptr),
58  _use_kdtree(getParam<bool>("use_kdtree")),
59  _point_patch_size(getParam<unsigned int>("point_patch_size")),
60  _grain_patch_size(getParam<unsigned int>("grain_patch_size"))
61 {
62  if (_file_name == "" && _grain_num == 0)
63  mooseError("grain_num must be provided if the grain centroids are not read from a file");
64 
65  if (_file_name != "" && _grain_num > 0)
66  mooseWarning("grain_num is ignored and will be determined from the file.");
67 }
68 
69 void
71  std::vector<unsigned int> & grains) const
72 {
73  grains.resize(0);
74  Real d_min = _range.norm();
75  Real distance;
76  auto n_grains = _centerpoints.size();
77  auto min_index = n_grains;
78 
79  if (_use_kdtree)
80  {
81  mooseAssert(_kd_tree, "KD tree is not constructed yet");
82  mooseAssert(_grain_gtl_ids.size() == _new_points.size(),
83  "The number of grain global IDs does not match that of new center points");
84 
85  std::vector<std::size_t> return_index(_point_patch_size);
86  std::vector<Real> return_dist_sqr(_point_patch_size);
87 
88  _kd_tree->neighborSearch(point, _point_patch_size, return_index, return_dist_sqr);
89 
90  min_index = _grain_gtl_ids[return_index[0]];
91 
92  d_min = return_dist_sqr[0];
93 
94  // By default, _point_patch_size is one. A larger _point_patch_size
95  // may be useful if nearest nodes are not unique, and
96  // we want to select the node that has the smallest ID
97  for (unsigned int i = 1; i < _point_patch_size; i++)
98  {
99  if (d_min > return_dist_sqr[i])
100  {
101  min_index = _grain_gtl_ids[return_index[i]];
102  d_min = return_dist_sqr[i];
103  }
104  else if (d_min == return_dist_sqr[i])
105  {
106  min_index = min_index > _grain_gtl_ids[return_index[i]] ? _grain_gtl_ids[return_index[i]]
107  : min_index;
108  }
109  }
110  }
111  else
112  {
113  // Find the closest centerpoint to the current point
114  for (MooseIndex(_centerpoints) grain = 0; grain < n_grains; ++grain)
115  {
116  distance = _mesh.minPeriodicDistance(_vars[0]->number(), _centerpoints[grain], point);
117  if (distance < d_min)
118  {
119  d_min = distance;
120  min_index = grain;
121  }
122  }
123  }
124 
125  mooseAssert(min_index < n_grains, "Couldn't find closest Voronoi cell");
126  Point current_grain = _centerpoints[min_index];
127  grains.push_back(min_index); // closest centerpoint always gets included
128 
129  if (_int_width > 0.0)
130  {
131  if (_use_kdtree)
132  {
133  mooseAssert(_grain_patch_size < _grain_gtl_ids.size(),
134  "Number of neighboring grains should not exceed number of global grains");
135 
136  std::vector<std::size_t> return_index(_grain_patch_size);
137  _kd_tree->neighborSearch(current_grain, _grain_patch_size, return_index);
138 
139  std::set<dof_id_type> neighbor_grains;
140  for (unsigned int i = 0; i < _grain_patch_size; i++)
141  if (_grain_gtl_ids[return_index[i]] != min_index)
142  neighbor_grains.insert(_grain_gtl_ids[return_index[i]]);
143 
144  for (auto it = neighbor_grains.begin(); it != neighbor_grains.end(); ++it)
145  if ((*it) != min_index)
146  {
147  Point next_grain = _centerpoints[*it];
148  Point N = findNormalVector(point, current_grain, next_grain);
149  Point cntr = findCenterPoint(point, current_grain, next_grain);
150  distance = N * (cntr - point);
151  if (distance < _int_width)
152  grains.push_back(*it); // also include all grains with nearby boundaries
153  }
154  }
155  else
156  {
157  for (MooseIndex(_centerpoints) grain = 0; grain < n_grains; ++grain)
158  if (grain != min_index)
159  {
160  Point next_grain = _centerpoints[grain];
161  Point N = findNormalVector(point, current_grain, next_grain);
162  Point cntr = findCenterPoint(point, current_grain, next_grain);
163  distance = N * (cntr - point);
164  if (distance < _int_width)
165  grains.push_back(grain); // also include all grains with nearby boundaries
166  }
167  }
168  }
169 }
170 
171 Real
172 PolycrystalVoronoi::getVariableValue(unsigned int op_index, const Point & p) const
173 {
174  std::vector<unsigned int> grain_ids;
175  getGrainsBasedOnPoint(p, grain_ids);
176 
177  // Now see if any of those grains are represented by the passed in order parameter
178  unsigned int active_grain_on_op = invalid_id;
179  for (auto grain_id : grain_ids)
180  if (op_index == _grain_to_op.at(grain_id))
181  {
182  active_grain_on_op = grain_id;
183  break;
184  }
185 
186  Real profile_val = 0.0;
187  if (active_grain_on_op != invalid_id)
188  profile_val = computeDiffuseInterface(p, active_grain_on_op, grain_ids);
189 
190  return profile_val;
191 }
192 
193 void
195 {
196  // Set up domain bounds with mesh tools
197  for (const auto i : make_range(Moose::dim))
198  {
201  }
203 
204  if (!_file_name.empty())
205  {
207 
208  txt_reader.read();
209  std::vector<std::string> col_names = txt_reader.getNames();
210  std::vector<std::vector<Real>> data = txt_reader.getData();
211  _grain_num = data[0].size();
212  _centerpoints.resize(_grain_num);
213 
214  for (unsigned int i = 0; i < col_names.size(); ++i)
215  {
216  // Check vector lengths
217  if (data[i].size() != _grain_num)
218  paramError("Columns in ", _file_name, " do not have uniform lengths.");
219  }
220 
221  for (unsigned int grain = 0; grain < _grain_num; ++grain)
222  {
223  _centerpoints[grain](0) = data[0][grain];
224  _centerpoints[grain](1) = data[1][grain];
225  if (col_names.size() > 2)
226  _centerpoints[grain](2) = data[2][grain];
227  else
228  _centerpoints[grain](2) = 0.0;
229  }
230  }
231  else
232  {
234 
235  // Randomly generate the centers of the individual grains represented by the Voronoi
236  // tessellation
237  _centerpoints.resize(_grain_num);
238  std::vector<Real> distances(_grain_num);
239 
240  for (auto grain = decltype(_grain_num)(0); grain < _grain_num; grain++)
241  {
242  for (const auto i : make_range(Moose::dim))
243  _centerpoints[grain](i) = _bottom_left(i) + _range(i) * MooseRandom::rand();
244  if (_columnar_3D)
245  _centerpoints[grain](2) = _bottom_left(2) + _range(2) * 0.5;
246  }
247  }
248 
249  // Build a KDTree that is used to speedup point search
250  buildSearchTree();
251 }
252 
253 void
255 {
256  if (!_use_kdtree)
257  return;
258 
259  auto midplane = _bottom_left + _range / 2.0;
260  dof_id_type local_grain_id = 0;
261  _grain_gtl_ids.clear();
262  _grain_gtl_ids.reserve(_centerpoints.size() * std::pow(2.0, _mesh.dimension()));
263  _new_points.clear();
264  // Use more memory. Factor is 2^dim
265  _new_points.reserve(_centerpoints.size() * std::pow(2.0, _mesh.dimension()));
266  // Domain will be extended along the periodic directions
267  // For each direction, a half domain is constructed
268  std::vector<std::vector<Real>> xyzs(LIBMESH_DIM);
269  for (auto & point : _centerpoints)
270  {
271  // Cear up
272  for (unsigned int i = 0; i < LIBMESH_DIM; i++)
273  xyzs[i].clear();
274 
275  // Have at least the original point
276  for (unsigned int i = 0; i < LIBMESH_DIM; i++)
277  xyzs[i].push_back(point(i));
278 
279  // Add new coords when there exists periodic boundary conditions
280  // We extend half domain
281  for (unsigned int i = 0; i < _mesh.dimension(); i++)
282  if (_mesh.isTranslatedPeriodic(_vars[0]->number(), i))
283  xyzs[i].push_back(point(i) <= midplane(i) ? point(i) + _range(i) : point(i) - _range(i));
284 
285  // Construct all combinations
286  for (auto x : xyzs[0])
287  for (auto y : xyzs[1])
288  for (auto z : xyzs[2])
289  {
290  _new_points.push_back(Point(x, y, z));
291  _grain_gtl_ids.push_back(local_grain_id);
292  }
293 
294  local_grain_id++;
295  }
296 
297  // Build a KDTree that is used to speedup point search
298  // We should not destroy _new_points after the tree is contributed
299  // KDTree use a reference to "_new_points"
300  _kd_tree = std::make_unique<KDTree>(_new_points, _mesh.getMaxLeafSize());
301 }
302 
303 Real
305  const unsigned int & gr_index,
306  const std::vector<unsigned int> & grain_ids) const
307 {
308  Real val = 1.0;
309  Point current_grain = _centerpoints[gr_index];
310  for (auto i : grain_ids)
311  if (i != gr_index)
312  {
313  Point next_grain = _centerpoints[i];
314  Point N = findNormalVector(point, current_grain, next_grain);
315  Point cntr = findCenterPoint(point, current_grain, next_grain);
316  for (unsigned int vcomp = 0; vcomp < 3; ++vcomp)
317  if (N(vcomp) != 0.0)
318  {
319  Real L = findLinePoint(point, N, cntr, vcomp);
320  val *= 0.5 * (1.0 - std::tanh(2.0 * (point(vcomp) - L) * N(vcomp) / _int_width));
321  break;
322  }
323  }
324  return val;
325 }
326 
327 Point
328 PolycrystalVoronoi::findNormalVector(const Point & point, const Point & p1, const Point & p2) const
329 {
330  Point pa = point + _mesh.minPeriodicVector(_vars[0]->number(), point, p1);
331  Point pb = point + _mesh.minPeriodicVector(_vars[0]->number(), point, p2);
332  Point N = pb - pa;
333  return N / N.norm();
334 }
335 
336 Point
337 PolycrystalVoronoi::findCenterPoint(const Point & point, const Point & p1, const Point & p2) const
338 {
339  Point pa = point + _mesh.minPeriodicVector(_vars[0]->number(), point, p1);
340  Point pb = point + _mesh.minPeriodicVector(_vars[0]->number(), point, p2);
341  return 0.5 * (pa + pb);
342 }
343 
344 Real
346  const Point & N,
347  const Point & cntr,
348  const unsigned int vcomp) const
349 {
350  const Real l_sum = N((vcomp + 1) % 3) * (point((vcomp + 1) % 3) - cntr((vcomp + 1) % 3)) +
351  N((vcomp + 2) % 3) * (point((vcomp + 2) % 3) - cntr((vcomp + 2) % 3));
352 
353  return cntr(vcomp) - l_sum / N(vcomp);
354 }
virtual Real getMaxInDimension(unsigned int component) const
virtual void getGrainsBasedOnPoint(const Point &point, std::vector< unsigned int > &grains) const override
Method for retrieving active grain IDs based on some point in the mesh.
virtual Real getMinInDimension(unsigned int component) const
std::map< unsigned int, unsigned int > _grain_to_op
A map of the grain_id to op.
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
const std::vector< std::vector< double > > & getData() const
const FileName _file_name
static InputParameters validParams()
RealVectorValue minPeriodicVector(unsigned int nonlinear_var_num, Point p, Point q) const
void seed(std::size_t i, unsigned int seed)
std::vector< Point > _new_points
Original grain center points and duplicated grain center points.
bool isTranslatedPeriodic(unsigned int nonlinear_var_num, unsigned int component) const
virtual Real getVariableValue(unsigned int op_index, const Point &p) const override
Returns the variable value for a given op_index and mesh point.
static constexpr std::size_t dim
Point findCenterPoint(const Point &point, const Point &p1, const Point &p2) const
std::vector< Point > _centerpoints
This object provides the base capability for creating proper polycrystal ICs.
const std::vector< double > y
std::vector< dof_id_type > _grain_gtl_ids
The domain is extended to consider periodic boundary conditions.
const Parallel::Communicator & _communicator
Real distance(const Point &p)
void mooseWarning(Args &&... args) const
Real computeDiffuseInterface(const Point &point, const unsigned int &gr_index, const std::vector< unsigned int > &grain_ids) const
Point findNormalVector(const Point &point, const Point &p1, const Point &p2) const
std::vector< MooseVariable * > _vars
The vector of coupled in variables cast to MooseVariable.
const std::vector< double > x
virtual unsigned int dimension() const
PolycrystalVoronoi(const InputParameters &parameters)
unsigned int getMaxLeafSize() const
Real findLinePoint(const Point &point, const Point &N, const Point &cntr, const unsigned int dim) const
static const unsigned int invalid_id
void paramError(const std::string &param, Args... args) const
Real minPeriodicDistance(unsigned int nonlinear_var_num, Point p, Point q) const
const std::vector< std::string > & getNames() const
std::unique_ptr< KDTree > _kd_tree
KD tree that is used to speedup grain search.
registerMooseObject("PhaseFieldApp", PolycrystalVoronoi)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
int N
virtual void precomputeGrainStructure() override
This callback is triggered after the object is initialized and may be optionally overridden to do pre...
IntRange< T > make_range(T beg, T end)
static Real rand()
void mooseError(Args &&... args) const
void addClassDescription(const std::string &doc_string)
void addRangeCheckedParam(const std::string &name, const T &value, const std::string &parsed_function, const std::string &doc_string)
unsigned int _grain_patch_size
The number of neighboring grains.
bool _use_kdtree
Whether or not to use a KD tree to speedup grain search.
const unsigned int _rand_seed
MooseMesh & _mesh
A reference to the mesh.
MooseUnits pow(const MooseUnits &, int)
unsigned int _grain_num
The number of grains to create.
void ErrorVector unsigned int
uint8_t dof_id_type
static InputParameters validParams()
unsigned int _point_patch_size
The number of nearest points.