Chaste  Release::3.4
VoronoiVertexMeshGenerator.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, are permitted provided that the following conditions are met:
14  * Redistributions of source code must retain the above copyright notice,
15  this list of conditions and the following disclaimer.
16  * Redistributions in binary form must reproduce the above copyright notice,
17  this list of conditions and the following disclaimer in the documentation
18  and/or other materials provided with the distribution.
19  * Neither the name of the University of Oxford nor the names of its
20  contributors may be used to endorse or promote products derived from this
21  software without specific prior written permission.
22 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "VoronoiVertexMeshGenerator.hpp"
37 
38 #if BOOST_VERSION >= 105200
39 
40 using boost::polygon::voronoi_builder;
41 using boost::polygon::voronoi_diagram;
42 typedef boost::polygon::point_data<int> boost_point;
43 
45  unsigned numElementsY,
46  unsigned numRelaxationSteps,
47  double elementTargetArea)
48  : mpMesh(NULL),
49  mpTorMesh(NULL),
50  mNumElementsX(numElementsX),
51  mNumElementsY(numElementsY),
52  mNumRelaxationSteps(numRelaxationSteps),
53  mElementTargetArea(elementTargetArea),
54  mTol(1e-7),
55  mMaxExpectedNumSidesPerPolygon(17)
56 {
57  this->ValidateInputAndSetMembers();
58  this->GenerateVoronoiMesh();
59 }
60 
61 VoronoiVertexMeshGenerator::~VoronoiVertexMeshGenerator()
62 {
63  if (mpMesh)
64  {
65  delete mpMesh;
66  }
67  if (mpTorMesh)
68  {
69  delete mpTorMesh;
70  }
71 }
72 
73 void VoronoiVertexMeshGenerator::GenerateVoronoiMesh()
74 {
81  // Get initial seed locations
82  std::vector<c_vector<double, 2> > seed_locations = this->GetInitialPointLocations();
83  this->ValidateSeedLocations(seed_locations);
84 
85  // We now create the initial Voronoi tessellation. This method updates mpMesh.
86  this->CreateVoronoiTessellation(seed_locations);
87 
92  for (unsigned relaxation = 0 ; relaxation < mNumRelaxationSteps ; relaxation++)
93  {
94  seed_locations.clear();
95  seed_locations = this->GetElementCentroidsFromMesh();
96 
97  this->CreateVoronoiTessellation(seed_locations);
98  }
99 
100  // We need to modify the node locations to achieve the correct target average element area
101  double scale_factor = double(mMaxNumElems) * sqrt(mElementTargetArea);
102  for (unsigned node_idx = 0 ; node_idx < mpMesh->GetNumNodes() ; node_idx++)
103  {
104  c_vector<double, 2>& node_location = mpMesh->GetNode(node_idx)->rGetModifiableLocation();
105  node_location *= scale_factor;
106  }
107 }
108 
109 MutableVertexMesh<2,2>* VoronoiVertexMeshGenerator::GetMesh()
110 {
111  return mpMesh;
112 }
113 
114 MutableVertexMesh<2,2>* VoronoiVertexMeshGenerator::GetMeshAfterReMesh()
115 {
116  mpMesh->ReMesh();
117  return mpMesh;
118 }
119 
120 Toroidal2dVertexMesh* VoronoiVertexMeshGenerator::GetToroidalMesh()
121 {
122  /*
123  * METHOD OUTLINE:
124  *
125  * 1. Copy nodes and elements from mpMesh into a new MutableVertexMesh, so no data is shared between mpMesh and the
126  * nodes and elements we will be working with. There are no available copy constructors, so this is done from
127  * scratch.
128  *
129  * 2. Identify which nodes on the boundary are congruent to each other. This is done by recursively using the
130  * helper function CheckForCongruentNodes().
131  *
132  * 3. Create mpTorMesh by copying the subset of remaining nodes and all elements in the new MutableVertexMesh.
133  * Some elements have been modified to replace nodes by congruent partner nodes and some nodes have been deleted.
134  */
135 
136  // The width and height of the mesh for periodicity purposes
137  double width = mNumElementsX * sqrt(mElementTargetArea);
138  double height = mNumElementsY * sqrt(mElementTargetArea);
139 
140  // We need to construct new nodes and elements so we don't have mpTorMesh sharing data with mpMesh
141  std::vector<Node<2>*> new_nodes(mpMesh->GetNumNodes());
142  std::vector<VertexElement<2,2>*> new_elems(mpMesh->GetNumElements());
143 
144  // Copy nodes
145  for (unsigned node_counter = 0 ; node_counter < mpMesh->GetNumNodes() ; node_counter++)
146  {
147  Node<2>* p_node_to_copy = mpMesh->GetNode(node_counter);
148 
149  // Get all the information about the node we are copying
150  unsigned copy_index = p_node_to_copy->GetIndex();
151  c_vector<double, 2> copy_location = p_node_to_copy->rGetLocation();
152  bool copy_is_boundary = p_node_to_copy->IsBoundaryNode();
153 
154  // There should not be any 'gaps' in node numbering, but we will assert just to make sure
155  assert(copy_index < mpMesh->GetNumNodes());
156 
157  // Create a new node and place it in index order. Every node in a periodic mesh is non-boundary.
158  new_nodes[copy_index] = new Node<2>(copy_index, copy_location, copy_is_boundary);
159  }
160 
161  // Copy elements
162  for (unsigned elem_counter = 0; elem_counter < mpMesh->GetNumElements(); elem_counter++)
163  {
164  VertexElement<2,2>* p_elem_to_copy = mpMesh->GetElement(elem_counter);
165 
166  // Get the information relating to the element we are copying
167  unsigned copy_index = p_elem_to_copy->GetIndex();
168  unsigned copy_num_nodes = p_elem_to_copy->GetNumNodes();
169 
170  // There should not be any 'gaps' in element numbering, but we will assert just to make sure
171  assert(copy_index < mpMesh->GetNumElements());
172 
173  // The vertex element is created from a vector of nodes
174  std::vector<Node<2>*> nodes_this_elem;
175 
176  // Loop through the nodes in p_elem_to_copy and add the corresponding nodes that we have already copied
177  for (unsigned node_local_idx = 0 ; node_local_idx < copy_num_nodes ; node_local_idx++)
178  {
179  Node<2>* p_local_node = p_elem_to_copy->GetNode(node_local_idx);
180  unsigned local_node_global_idx = p_local_node->GetIndex();
181  nodes_this_elem.push_back(new_nodes[local_node_global_idx]);
182  }
183 
184  // Create a new node and place it in index order
185  new_elems[copy_index] = new VertexElement<2,2>(copy_index, nodes_this_elem);
186  }
187 
188  // We can now create the mesh with new_elements and the subset of new_nodes
189  MutableVertexMesh<2,2>* p_temp_mesh = new MutableVertexMesh<2,2>(new_nodes, new_elems);
190 
191  /*
192  * Recursively associate congruent nodes.
193  *
194  * For each node currently on the boundary, we loop through all other boundary nodes and check against all eight
195  * possible congruent locations. If any one coincides, we replace the identified node with its congruent partner,
196  * delete and remove the identified node from the mesh, and start again from scratch.
197  *
198  * If a node has no congruent partners, we simply mark it as non-boundary, as it is then a node that needs to appear
199  * in the final toroidal mesh.
200  *
201  * This recursion is guaranteed to terminate as the number of boundary nodes decreases by precisely one each time
202  * CheckForCongruentNodes() is called, and CheckForCongruentNodes() returns false if there are no boundary nodes.
203  */
204  bool re_check = true;
205 
206  while (re_check)
207  {
208  re_check = this->CheckForCongruentNodes(p_temp_mesh, width, height);
209  }
210 
211  // We now copy the nodes and elements into a new toroidal mesh, and delete p_temp_mesh
212  new_nodes.clear();
213  new_nodes.resize(p_temp_mesh->GetNumNodes());
214 
215  new_elems.clear();
216  new_elems.resize(p_temp_mesh->GetNumElements());
217 
218  // Copy nodes
219  for (unsigned node_counter = 0 ; node_counter < p_temp_mesh->GetNumNodes() ; node_counter++)
220  {
221  Node<2>* p_node_to_copy = p_temp_mesh->GetNode(node_counter);
222 
223  // Get all the information about the node we are copying
224  unsigned copy_index = p_node_to_copy->GetIndex();
225  c_vector<double, 2> copy_location = p_node_to_copy->rGetLocation();
226  bool copy_is_boundary = p_node_to_copy->IsBoundaryNode();
227 
228  // No nodes should be boundary nodes
229  assert(!copy_is_boundary);
230 
231  // There should not be any 'gaps' in node numbering, but we will assert just to make sure
232  assert(copy_index < p_temp_mesh->GetNumNodes());
233 
234  // Create a new node and place it in index order. Every node in a periodic mesh is non-boundary.
235  new_nodes[copy_index] = new Node<2>(copy_index, copy_location, false);
236  }
237 
238  // Copy elements
239  for (unsigned elem_counter = 0; elem_counter < p_temp_mesh->GetNumElements(); elem_counter++)
240  {
241  VertexElement<2,2>* p_elem_to_copy = p_temp_mesh->GetElement(elem_counter);
242 
243  // Get the information relating to the element we are copying
244  unsigned copy_index = p_elem_to_copy->GetIndex();
245  unsigned copy_num_nodes = p_elem_to_copy->GetNumNodes();
246 
247  // There should not be any 'gaps' in element numbering, but we will assert just to make sure
248  assert(copy_index < p_temp_mesh->GetNumElements());
249 
250  // The vertex element is created from a vector of nodes
251  std::vector<Node<2>*> nodes_this_elem;
252 
253  // Loop through the nodes in p_elem_to_copy and add the corresponding nodes that we have already copied
254  for (unsigned node_local_idx = 0 ; node_local_idx < copy_num_nodes ; node_local_idx++)
255  {
256  Node<2>* p_local_node = p_elem_to_copy->GetNode(node_local_idx);
257 
258  unsigned local_node_global_idx = p_local_node->GetIndex();
259 
260  nodes_this_elem.push_back(new_nodes[local_node_global_idx]);
261  }
262 
263  // Create a new node and place it in index order
264  new_elems[copy_index] = new VertexElement<2,2>(copy_index, nodes_this_elem);
265  }
266 
267  delete p_temp_mesh;
268 
269  /*
270  * We now create the mesh with new_elements and new_nodes. We immediately call ReMesh() to tidy up any short edges.
271  *
272  * We then reposition all nodes to be within the box [0, width]x[0, height] to make mesh look nicer when visualised.
273  */
274  mpTorMesh = new Toroidal2dVertexMesh(width, height, new_nodes, new_elems);
275  mpTorMesh->ReMesh();
276 
277  c_vector<double, 2> min_x_y;
278  min_x_y[0] = DBL_MAX;
279  min_x_y[1] = DBL_MAX;
280 
281  // First loop is to calculate the correct offset, min_x_y
282  for (unsigned node_idx = 0 ; node_idx < mpTorMesh->GetNumNodes() ; node_idx++)
283  {
284  c_vector<double, 2> this_node_location = mpTorMesh->GetNode(node_idx)->rGetLocation();
285 
286  if(this_node_location[0] < min_x_y[0])
287  {
288  min_x_y[0] = this_node_location[0];
289  }
290  if(this_node_location[1] < min_x_y[1])
291  {
292  min_x_y[1] = this_node_location[1];
293  }
294  }
295 
296  // Second loop applies the offset, min_x_y, to each node in the mesh
297  for (unsigned node_idx = 0 ; node_idx < mpTorMesh->GetNumNodes() ; node_idx++)
298  {
299  mpTorMesh->GetNode(node_idx)->rGetModifiableLocation() -= min_x_y;
300  }
301 
302  // Third loop is to reposition any nodes that are now outside the bounding rectangle
303  for (unsigned node_idx = 0 ; node_idx < mpTorMesh->GetNumNodes() ; node_idx++)
304  {
305  if (mpTorMesh->GetNode(node_idx)->rGetLocation()[0] >= width)
306  {
307  mpTorMesh->GetNode(node_idx)->rGetModifiableLocation()[0] -= width;
308  }
309  if (mpTorMesh->GetNode(node_idx)->rGetLocation()[1] >= height)
310  {
311  mpTorMesh->GetNode(node_idx)->rGetModifiableLocation()[1] -= height;
312  }
313  }
314 
315  return mpTorMesh;
316 }
317 
318 bool VoronoiVertexMeshGenerator::CheckForCongruentNodes(MutableVertexMesh<2,2>* pMesh, double width, double height)
319 {
320  // First find all the current boundary nodes in pMesh
321  std::vector<Node<2>*> boundary_nodes;
322  for (unsigned node_idx = 0 ; node_idx < pMesh->GetNumNodes() ; node_idx++)
323  {
324  Node<2>* p_node = pMesh->GetNode(node_idx);
325 
326  if (p_node->IsBoundaryNode())
327  {
328  boundary_nodes.push_back(p_node);
329  }
330  }
331 
332  // If there are no boundary nodes, return false (we're done checking congruencies)
333  if (boundary_nodes.empty())
334  {
335  return false;
336  }
337 
338  // Otherwise, calculate the eight possible congruent locations for the current node
339  Node<2>* p_node_a = *(boundary_nodes.begin());
340  c_vector<double, 2> node_a_pos = p_node_a->rGetLocation();
341  std::vector<c_vector<double,2> > congruent_locations(8, node_a_pos);
342 
343  congruent_locations[0][0] += width;
344 
345  congruent_locations[1][1] += height;
346 
347  congruent_locations[2][0] -= width;
348 
349  congruent_locations[3][1] -= height;
350 
351  congruent_locations[4][0] += width;
352  congruent_locations[4][1] += height;
353 
354  congruent_locations[5][0] -= width;
355  congruent_locations[5][1] += height;
356 
357  congruent_locations[6][0] -= width;
358  congruent_locations[6][1] -= height;
359 
360  congruent_locations[7][0] += width;
361  congruent_locations[7][1] -= height;
362 
363  // Loop over all other boundary nodes
364  for (unsigned node_b_counter = 0; node_b_counter < boundary_nodes.size(); node_b_counter++)
365  {
366  // Get the index of the current boundary node, and the corresponding node from the mesh
367  unsigned node_b_idx = boundary_nodes[node_b_counter]->GetIndex();
368  Node<2>* p_mesh_node_b = pMesh->GetNode(node_b_idx);
369 
370  if (p_node_a == p_mesh_node_b)
371  {
372  continue;
373  }
374 
375  c_vector<double, 2> node_b_pos = p_mesh_node_b->rGetLocation();
376 
377  // Loop over the eight possible congruent locations and check for coincidence of location
378  for (unsigned pos = 0 ; pos < congruent_locations.size() ; pos++)
379  {
380  if (norm_inf(node_b_pos - congruent_locations[pos]) < mTol)
381  {
382  // Once we find a congruent location, we replace that node in all containing elements
383  std::set<unsigned> containing_elems = p_mesh_node_b->rGetContainingElementIndices();
384 
385  assert(containing_elems.size() > 0);
386 
387  for (std::set<unsigned>::iterator it = containing_elems.begin() ; it != containing_elems.end() ; ++it)
388  {
389  VertexElement<2,2>* p_this_elem = pMesh->GetElement(*it);
390  unsigned local_idx = p_this_elem->GetNodeLocalIndex(p_mesh_node_b->GetIndex());
391 
392  assert(local_idx < UINT_MAX);
393 
394  p_this_elem->AddNode(p_node_a, local_idx);
395  p_this_elem->DeleteNode(local_idx);
396  }
397 
398  // Delete the node_b and remove it from the mesh, and return true to start checking again from scratch
399  pMesh->DeleteNodePriorToReMesh(p_mesh_node_b->GetIndex());
400  pMesh->RemoveDeletedNodes();
401  return true;
402  }
403  }
404  }
405 
406  /*
407  * If we have checked p_node_a against all node_b candidates and have not found a congruent location, p_node_a can
408  * be re-tagged as a non-boundary node, and so will not get checked again next time this function is called.
409  */
410  p_node_a->SetAsBoundaryNode(false);
411 
412  return true;
413 }
414 
415 std::vector<c_vector<double, 2> > VoronoiVertexMeshGenerator::GetInitialPointLocations()
416 {
417  // Create a vector which contains mTotalNumElements spaces
418  std::vector<c_vector<double, 2> > seed_points(mTotalNumElements);
419 
421 
422  // Create the correct number of suitably scaled random numbers
423  for (unsigned point_idx = 0 ; point_idx < mTotalNumElements ; point_idx++)
424  {
425  seed_points[point_idx][0] = p_rand_gen->ranf() * mMultiplierInX;
426  seed_points[point_idx][1] = p_rand_gen->ranf() * mMultiplierInY;
427  }
428 
429  return seed_points;
430 }
431 
432 std::vector<c_vector<double, 2> > VoronoiVertexMeshGenerator::GetElementCentroidsFromMesh()
433 {
434  assert(mpMesh->GetNumElements() == mNumElementsX * mNumElementsY);
435 
436  std::vector<c_vector<double, 2> > element_centroids;
437 
438  // Loop over all elements in the mesh
439  for (unsigned elem_idx = 0; elem_idx < mpMesh->GetNumElements(); elem_idx++)
440  {
441  // Get the current centroid of the element
442  c_vector<double, 2> this_centroid = mpMesh->GetCentroidOfElement(elem_idx);
443 
450  // Account for possible wrap-around in the x-direction
451  if (this_centroid[0] < 0.0)
452  {
453  this_centroid[0] += mMultiplierInX;
454  }
455  else if (this_centroid[0] > mMultiplierInX)
456  {
457  this_centroid[0] -= mMultiplierInX;
458  }
459 
460  // Account for possible wrap-around in the y-direction
461  if (this_centroid[1] < 0.0)
462  {
463  this_centroid[1] += mMultiplierInY;
464  }
465  else if (this_centroid[1] > mMultiplierInY)
466  {
467  this_centroid[1] -= mMultiplierInY;
468  }
469 
470  element_centroids.push_back(this_centroid);
471  }
472 
473  return element_centroids;
474 }
475 
476 void VoronoiVertexMeshGenerator::CreateVoronoiTessellation(std::vector<c_vector<double, 2> >& rSeedLocations)
477 {
478  // Clear the mesh nodes and elements, as they will be replaced in this method
479  std::vector<Node<2>*> nodes;
480  std::vector<VertexElement<2, 2>* > elements;
481 
489  // Datatype is boost::polygon::point_data<int>
490  std::vector<boost_point> points;
491 
492  // Take the locations and map them to integers in a subset of [0, INT_MAX/2] x [0, INT_MAX/2]
493  for (unsigned point_idx = 0 ; point_idx < rSeedLocations.size() ; point_idx++)
494  {
495  // Calculate the correct integer gridpoints
496  int point_x = int( floor( (rSeedLocations[point_idx][0] * mSamplingMultiplier) + 0.5) );
497  int point_y = int( floor( (rSeedLocations[point_idx][1] * mSamplingMultiplier) + 0.5) );
498 
499  points.push_back( boost_point(point_x, point_y) );
500  }
501 
502  // Define offset vectors for the 3 by 3 tessellation
503  std::vector<boost_point> offsets;
504  offsets.push_back( boost_point(-mMaxIntX, mMaxIntY) );
505  offsets.push_back( boost_point( 0, mMaxIntY) );
506  offsets.push_back( boost_point( mMaxIntX, mMaxIntY) );
507  offsets.push_back( boost_point( mMaxIntX, 0) );
508  offsets.push_back( boost_point( mMaxIntX, -mMaxIntY) );
509  offsets.push_back( boost_point( 0, -mMaxIntY) );
510  offsets.push_back( boost_point(-mMaxIntX, -mMaxIntY) );
511  offsets.push_back( boost_point(-mMaxIntX, 0) );
512 
513  // Add all the points in the tessellation to the vector of boost points so in total there are 9 copies of each
514  // seed location, suitably tiled.
515  for (unsigned rep = 0; rep < offsets.size(); rep++)
516  {
517  boost_point offset = offsets[rep];
518  for (unsigned point_idx = 0; point_idx < rSeedLocations.size(); point_idx++)
519  {
520  boost_point new_point = boost_point(points[point_idx].x() + offset.x(), points[point_idx].y() + offset.y());
521  points.push_back(new_point);
522  }
523  }
524 
540  // Construct the Voronoi tessellation of these 9 x mTotalNumElements points
541  voronoi_diagram<double> vd;
542  construct_voronoi(points.begin(), points.end(), &vd);
543 
544  // Loop over the cells in the voronoi diagram to find nodes for our mesh
545  for (voronoi_diagram<double>::const_cell_iterator it = vd.cells().begin();
546  it != vd.cells().end();
547  ++it)
548  {
549  // Get a reference to the current cell
550  const voronoi_diagram<double>::cell_type& cell = *it;
551 
552  // The cells we care about are exactly those whose source_index is less than the size of the locations vector
553  // (i.e. those cells in the central portion of the 3x3 tessellation of source points)
554  if (cell.source_index() < rSeedLocations.size())
555  {
556  // We create a vector of nodes, which will be used to create a MutableElement
557  std::vector<Node<2>*> nodes_this_elem;
558 
559  // Loop over the edges of the current cell
560  const voronoi_diagram<double>::edge_type *edge = cell.incident_edge();
561 
562  do
563  {
564  if (edge->is_primary())
565  {
566  // Calculate the location corresponding to the location of vertex0 of the current edge
567  double x_location = (edge->vertex0()->x()) / mSamplingMultiplier;
568  double y_location = (edge->vertex0()->y()) / mSamplingMultiplier;
569 
570  // Create a node at this location. Default to non-boundary node; this will be updated later
571  Node<2>* p_this_node = new Node<2>(nodes.size(), false, x_location, y_location);
572 
580  unsigned existing_node_idx = UINT_MAX;
581 
582  for (unsigned node_idx = 0; node_idx < nodes.size(); node_idx++)
583  {
584  // Grab the existing node location
585  c_vector<double, 2> existing_node_location = nodes[node_idx]->rGetLocation();
586 
587  // Equality here is determined entirely on coincidence of position
588  if ( fabs(existing_node_location[0] - p_this_node->rGetLocation()[0]) < DBL_EPSILON )
589  {
590  if ( fabs(existing_node_location[1] - p_this_node->rGetLocation()[1]) < DBL_EPSILON )
591  {
592  // If the nodes match, return the existing node index
593  existing_node_idx = node_idx;
594  }
595  }
596  }
597 
598  if (existing_node_idx < UINT_MAX)
599  {
600  // The node was already in nodes vector, and its index is the variable 'existing_node'
601  nodes_this_elem.push_back(nodes[existing_node_idx]);
602  delete p_this_node;
603  }
604  else
605  {
606  // The node does not yet exist - we add it to the nodes vector
607  nodes.push_back(p_this_node);
608  nodes_this_elem.push_back(p_this_node);
609  }
610 
611  // Move to the next edge
612  edge = edge->next();
613  }
614 
615  } while (edge != cell.incident_edge());
616 
617  // Add a new VertexElement to the mElements vector
618  elements.push_back(new VertexElement<2,2>(elements.size(), nodes_this_elem));
619  }
620  }
621 
622  // Loop over the cells in the voronoi diagram to identify boundary nodes
623  for (voronoi_diagram<double>::const_cell_iterator it = vd.cells().begin();
624  it != vd.cells().end();
625  ++it)
626  {
627  // Get a reference to the current cell
628  const voronoi_diagram<double>::cell_type& cell = *it;
629 
630  // The cells we care about are exactly those whose source_index is greater than the size of the locations vector
631  // (i.e. those cells in the outside eight portions of the 3x3 tessellation of source points)
632  if (cell.source_index() >= rSeedLocations.size())
633  {
634  // Loop over the edges of the current cell
635  const voronoi_diagram<double>::edge_type *edge = cell.incident_edge();
636 
637  do
638  {
639  /*
640  * Break out of the do-while if there is an infinite edge; we needn't care about cells at the very edge.
641  */
642  if (edge->is_infinite())
643  {
644  break;
645  }
646 
647  if (edge->is_primary())
648  {
649  c_vector<double, 2> vertex_location;
650 
651  // Get the location of vertex0 of the current edge
652  vertex_location[0] = (edge->vertex0()->x()) / mSamplingMultiplier;
653  vertex_location[1] = (edge->vertex0()->y()) / mSamplingMultiplier;
654 
655  /*
656  * Check whether this location coincides with one of our nodes; if it does, it must be a boundary
657  * node
658  */
659  for (unsigned node_idx = 0 ; node_idx < nodes.size() ; node_idx++)
660  {
661  // Grab the existing node location
662  c_vector<double, 2> existing_node_location = nodes[node_idx]->rGetLocation();
663 
664  // Equality here is determined entirely on coincidence of position
665  if ( fabs(existing_node_location[0] - vertex_location[0]) < DBL_EPSILON )
666  {
667  if ( fabs(existing_node_location[1] - vertex_location[1]) < DBL_EPSILON )
668  {
669  // If the locations match, tag the node as being on the boundary
670  nodes[node_idx]->SetAsBoundaryNode(true);
671  }
672  }
673  }
674  // Move to the next edge
675  edge = edge->next();
676  }
677 
678  } while (edge != cell.incident_edge());
679  }
680  }
681 
682  // Create a new mesh with the current vector of nodes and elements
683  if (mpMesh)
684  {
685  delete mpMesh;
686  }
687  mpMesh = new MutableVertexMesh<2,2>(nodes, elements);
688 }
689 
690 void VoronoiVertexMeshGenerator::ValidateInputAndSetMembers()
691 {
692  // Validate inputs
693  if ( (mNumElementsX < 2) || (mNumElementsY < 2) )
694  {
695  EXCEPTION("Need at least 2 by 2 cells");
696  }
697 
698  if ( mElementTargetArea <= 0.0 )
699  {
700  EXCEPTION("Specified target area must be strictly positive");
701  }
702 
703  // The total number of elements requested
704  mTotalNumElements = mNumElementsX * mNumElementsY;
705 
706  // Max of the numbers of elements across and up
707  mMaxNumElems = std::max(mNumElementsX, mNumElementsY);
708 
709  // The multipliers necessary in x and y to ensure all seed points are in [0,1]x[0,1]
710  mMultiplierInX = double(mNumElementsX) / double(mMaxNumElems);
711  mMultiplierInY = double(mNumElementsY) / double(mMaxNumElems);
712 
713  // Floating point representation of INT_MAX/2
714  mSamplingMultiplier = 0.5 * double(INT_MAX);
715 
716  // The max integer that a seed point could be mapped to when discretising
717  mMaxIntX = int( floor( (mMultiplierInX * mSamplingMultiplier) + 0.5 ) );
718  mMaxIntY = int( floor( (mMultiplierInY * mSamplingMultiplier) + 0.5 ) );
719 }
720 
721 void VoronoiVertexMeshGenerator::ValidateSeedLocations(std::vector<c_vector<double, 2> >& rSeedLocations)
722 {
723  unsigned num_seeds = rSeedLocations.size();
724 
725  /*
726  * Seeds at least 1.0 / mSamplingMultiplier apart from each other
727  * are acceptable; the 1.5 term below could just as well be any
728  * number that is strictly greater than 1.0.
729  */
730  double safe_distance = 1.5 / mSamplingMultiplier;
731 
732  // If we find a seed that needs to move position, we will move it and start checking again from the beginning
733  bool recheck = true;
734 
735  while (recheck)
736  {
737  // If no seeds needs moving (as is overwhelmingly likely), we do not need to check again
738  recheck = false;
739 
740  // We check each seed against every other, without double counting
741  for (unsigned seed_idx_one = 0; seed_idx_one < num_seeds; seed_idx_one++)
742  {
743  for (unsigned seed_idx_two = seed_idx_one + 1; seed_idx_two < num_seeds; seed_idx_two++)
744  {
745  // We get the distance between the two seeds currently being considered
746  c_vector<double, 2> one_to_two = rSeedLocations[seed_idx_two] - rSeedLocations[seed_idx_one];
747  double distance = norm_2(one_to_two);
748 
749  if (distance < safe_distance)
750  {
751  // We will now need to re-check
752  recheck = true;
753 
754  /*
755  * If the distance is non-zero, we will move the second seed away along the line joining the two
756  * seeds until it's at the safe distance. If the distance is somehow zero, we will just move the
757  * x-location of the second seed by the safe distance.
758  *
759  * In all cases, we also need to ensure the new location is within the boundary box.
760  */
761  if (distance > DBL_EPSILON)
762  {
763  double multiplier = safe_distance / distance;
764  rSeedLocations[seed_idx_two] += (one_to_two * multiplier);
765 
766  rSeedLocations[seed_idx_two][0] = fmod(rSeedLocations[seed_idx_two][0] + mMultiplierInX, mMultiplierInX);
767  rSeedLocations[seed_idx_two][1] = fmod(rSeedLocations[seed_idx_two][1] + mMultiplierInX, mMultiplierInX);
768  }
769  else
770  {
771  rSeedLocations[seed_idx_two][0] += safe_distance;
772  rSeedLocations[seed_idx_two][0] = fmod(rSeedLocations[seed_idx_two][0], mMultiplierInX);
773  }
774  }
775  }
776 
777  // We will re-check now rather than finishing the outer loop first
778  if (recheck)
779  {
780  break;
781  }
782  }
783  }
784 }
785 
786 std::vector<double> VoronoiVertexMeshGenerator::GetPolygonDistribution()
787 {
788  assert(mpMesh != NULL);
789 
790  // Number of elements in the mesh
791  unsigned num_elems = mpMesh->GetNumElements();
792 
793  // Store the number of each class of polygons
794  std::vector<unsigned> num_polygons(mMaxExpectedNumSidesPerPolygon - 2, 0);
795 
796  // Container to return the polygon distribution
797  std::vector<double> polygon_dist;
798 
799  // Loop over elements in the mesh to get the number of each class of polygon
800  for (unsigned elem_idx = 0; elem_idx < num_elems; elem_idx++)
801  {
802  unsigned num_nodes_this_elem = mpMesh->GetElement(elem_idx)->GetNumNodes();
803 
804  // All polygons are assumed to have 3, 4, 5, ..., mMaxExpectedNumSidesPerPolygon sides
805  assert(num_nodes_this_elem > 2);
806  assert(num_nodes_this_elem <= mMaxExpectedNumSidesPerPolygon);
807 
808  // Increment correct place in counter - triangles in place 0, squares in 1, etc
809  num_polygons[num_nodes_this_elem - 3]++;
810  }
811 
812  // Loop over the vector of polygon numbers and calculate the distribution vector to return
813  unsigned elems_accounted_for = 0;
814  for (unsigned polygon = 0 ; polygon < num_polygons.size() ; polygon++)
815  {
816  elems_accounted_for += num_polygons[polygon];
817 
818  polygon_dist.push_back(static_cast<double>(num_polygons[polygon]) / static_cast<double>(num_elems));
819 
820  // Only fill the vector of polygon distributions to the point where there are none of higher class in the mesh
821  if (elems_accounted_for == num_elems)
822  {
823  break;
824  }
825  }
826 
827  return polygon_dist;
828 }
829 
830 double VoronoiVertexMeshGenerator::GetAreaCoefficientOfVariation()
831 {
832  assert(mpMesh != NULL);
833 
834  // Number of elements in the mesh, and check there are at least two
835  unsigned num_elems = mpMesh->GetNumElements();
836  assert(num_elems > 1);
837 
838  double var = 0.0;
839 
840  // Loop over elements in the mesh to get the contributions to the variance
841  for (unsigned elem_idx = 0 ; elem_idx < num_elems ; elem_idx++)
842  {
843  double deviation = mpMesh->GetVolumeOfElement(elem_idx) - mElementTargetArea;
844  var += deviation * deviation;
845  }
846 
847  var /= static_cast<double>(num_elems - 1);
848 
849  return sqrt(var) / mElementTargetArea;
850 }
851 
852 void VoronoiVertexMeshGenerator::RefreshSeedsAndRegenerateMesh()
853 {
854  this->GenerateVoronoiMesh();
855 }
856 
857 void VoronoiVertexMeshGenerator::SetMaxExpectedNumSidesPerPolygon(unsigned maxExpectedNumSidesPerPolygon)
858 {
859  mMaxExpectedNumSidesPerPolygon = maxExpectedNumSidesPerPolygon;
860 }
861 
862 unsigned VoronoiVertexMeshGenerator::GetMaxExpectedNumSidesPerPolygon()
863 {
864  return mMaxExpectedNumSidesPerPolygon;
865 }
866 
867 #endif // BOOST_VERSION >= 105200
868 #if BOOST_VERSION < 105200
869 
875 {
876  EXCEPTION("This is a dummy class. Build with Boost version 1.52 or above for functionality.");
877 }
878 
879 #endif // BOOST_VERSION < 105200
Definition: Node.hpp:58
void SetAsBoundaryNode(bool value=true)
Definition: Node.cpp:127
bool IsBoundaryNode() const
Definition: Node.cpp:165
Node< SPACE_DIM > * GetNode(unsigned index) const
#define EXCEPTION(message)
Definition: Exception.hpp:143
std::set< unsigned > & rGetContainingElementIndices()
Definition: Node.cpp:301
VertexElement< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
Definition: VertexMesh.cpp:639
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
void AddNode(Node< SPACE_DIM > *pNode, const unsigned &rIndex)
unsigned GetNumNodes() const
unsigned GetNumNodes() const
void DeleteNodePriorToReMesh(unsigned index)
static RandomNumberGenerator * Instance()
virtual void ReMesh(VertexElementMap &rElementMap)
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition: Node.cpp:140
unsigned GetIndex() const
unsigned GetNumElements() const
void DeleteNode(const unsigned &rIndex)
unsigned GetIndex() const
Definition: Node.cpp:159
unsigned GetNodeLocalIndex(unsigned globalIndex) const