Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
VertexMesh.cpp
1/*
2
3Copyright (c) 2005-2024, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, 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
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "VertexMesh.hpp"
37#include "MutableMesh.hpp"
38#include "RandomNumberGenerator.hpp"
40
41#include "VtkMeshWriter.hpp"
42#include "NodesOnlyMesh.hpp"
43
44template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
46 std::vector<VertexElement<ELEMENT_DIM, SPACE_DIM>*> vertexElements)
47 : mpDelaunayMesh(nullptr)
48{
49
50 // Reset member variables and clear mNodes and mElements
51 Clear();
52
53 // Populate mNodes and mElements
54 for (unsigned node_index = 0; node_index < nodes.size(); node_index++)
55 {
56 Node<SPACE_DIM>* p_temp_node = nodes[node_index];
57 this->mNodes.push_back(p_temp_node);
58 }
59 for (unsigned elem_index = 0; elem_index < vertexElements.size(); elem_index++)
60 {
61 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_temp_vertex_element = vertexElements[elem_index];
62 mElements.push_back(p_temp_vertex_element);
63 }
64
65 // In 3D, populate mFaces
66 if (SPACE_DIM == 3)
67 {
68 // Use a std::set to keep track of which faces have been added to mFaces
69 std::set<unsigned> faces_counted;
70
71 // Loop over mElements
72 for (unsigned elem_index = 0; elem_index < mElements.size(); elem_index++)
73 {
74 // Loop over faces of this element
75 for (unsigned face_index = 0; face_index < mElements[elem_index]->GetNumFaces(); face_index++)
76 {
77 VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* p_face = mElements[elem_index]->GetFace(face_index);
78 unsigned global_index = p_face->GetIndex();
79
80 // If this face is not already contained in mFaces, add it, and update faces_counted
81 if (faces_counted.find(global_index) == faces_counted.end())
82 {
83 mFaces.push_back(p_face);
84 faces_counted.insert(global_index);
85 }
86 }
87 }
88 }
89
90 // Register elements with nodes
91 for (unsigned index = 0; index < mElements.size(); index++)
92 {
94
95 unsigned element_index = p_element->GetIndex();
96 unsigned num_nodes_in_element = p_element->GetNumNodes();
97
98 for (unsigned node_index = 0; node_index < num_nodes_in_element; node_index++)
99 {
100 p_element->GetNode(node_index)->AddElement(element_index);
101 }
102 }
103
105
106 this->mMeshChangesDuringSimulation = false;
107}
108
109template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
112 std::vector<VertexElement<ELEMENT_DIM, SPACE_DIM>*> vertexElements)
113 : mpDelaunayMesh(nullptr)
114{
115 // Reset member variables and clear mNodes, mFaces and mElements
116 Clear();
118 // Populate mNodes mFaces and mElements
119 for (unsigned node_index = 0; node_index < nodes.size(); node_index++)
120 {
121 Node<SPACE_DIM>* p_temp_node = nodes[node_index];
122 this->mNodes.push_back(p_temp_node);
123 }
124
125 for (unsigned face_index = 0; face_index < faces.size(); face_index++)
127 VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* p_temp_face = faces[face_index];
128 mFaces.push_back(p_temp_face);
129 }
130
131 for (unsigned elem_index = 0; elem_index < vertexElements.size(); elem_index++)
132 {
133 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_temp_vertex_element = vertexElements[elem_index];
134 mElements.push_back(p_temp_vertex_element);
136
137 // Register elements with nodes
138 for (unsigned index = 0; index < mElements.size(); index++)
139 {
140 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_temp_vertex_element = mElements[index];
141 for (unsigned node_index = 0; node_index < p_temp_vertex_element->GetNumNodes(); node_index++)
142 {
143 Node<SPACE_DIM>* p_temp_node = p_temp_vertex_element->GetNode(node_index);
144 p_temp_node->AddElement(p_temp_vertex_element->GetIndex());
145 }
146 }
147
148 this->mMeshChangesDuringSimulation = false;
149}
155template <>
157 bool isPeriodic,
158 bool isBounded,
159 bool scaleBoundByEdgeLength,
160 double maxDelaunayEdgeLength,
161 bool offsetNewBoundaryNodes)
162 : mpDelaunayMesh(&rMesh)
163{
164 //Note !isPeriodic is not used except through polymorphic calls in rMesh
165
166 // Reset member variables and clear mNodes, mFaces and mElements
167 Clear();
168
169 if (!isBounded)
170 {
171 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
172 unsigned num_nodes = mpDelaunayMesh->GetNumAllElements();
173
174 // Allocate memory for mNodes and mElements
175 this->mNodes.reserve(num_nodes);
176
177 // Create as many elements as there are nodes in the mesh
178 mElements.reserve(num_elements);
179 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
180 {
181 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
182 mElements.push_back(p_element);
184
185 // Populate mNodes
187
188 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
189 for (unsigned i = 0; i < num_nodes; i++)
190 {
191 // Loop over nodes owned by this triangular element in the Delaunay mesh
192 // Add this node/vertex to each of the 3 vertex elements
193 for (unsigned local_index = 0; local_index < 3; local_index++)
194 {
195 unsigned elem_index = mpDelaunayMesh->GetElement(i)->GetNodeGlobalIndex(local_index);
196 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
197 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
198
199 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
200 }
201 }
202 }
203 else // Is Bounded
204 {
205 // First create an extended mesh to include points extended from the boundary
206 std::vector<Node<2> *> nodes;
207 for (typename TetrahedralMesh<2,2>::NodeIterator node_iter = mpDelaunayMesh->GetNodeIteratorBegin();
208 node_iter != mpDelaunayMesh->GetNodeIteratorEnd();
209 ++node_iter)
210 {
211 nodes.push_back(new Node<2>(node_iter->GetIndex(), node_iter->rGetLocation(),node_iter->IsBoundaryNode()));
212 }
213
214 // Add new nodes
215 unsigned new_node_index = mpDelaunayMesh->GetNumNodes();
216
217 // Lop over elements to work out boundary edges
218 for (TetrahedralMesh<2,2>::ElementIterator elem_iter = mpDelaunayMesh->GetElementIteratorBegin();
219 elem_iter != mpDelaunayMesh->GetElementIteratorEnd();
220 ++elem_iter)
221 {
222 bool bad_element = false;
223
224 for (unsigned j=0; j<3; j++)
225 {
226 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
227 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
228 if (norm_2(mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation()))>maxDelaunayEdgeLength)
229 {
230 bad_element = true;
231 break;
232 }
233 }
234
235 for (unsigned j=0; j<3; j++)
236 {
237 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
238 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
239
240 std::set<unsigned> node_a_element_indices = p_node_a->rGetContainingElementIndices();
241 std::set<unsigned> node_b_element_indices = p_node_b->rGetContainingElementIndices();
242
243 std::set<unsigned> shared_elements;
244 std::set_intersection(node_a_element_indices.begin(),
245 node_a_element_indices.end(),
246 node_b_element_indices.begin(),
247 node_b_element_indices.end(),
248 std::inserter(shared_elements, shared_elements.begin()));
249
250 c_vector<double,2> edge = p_node_b->rGetLocation() - p_node_a->rGetLocation();
251 double edge_length = norm_2(edge);
252
253 /*
254 * Note using boundary nodes to identify the boundary edges won't work with
255 * triangles which have 3 boundary nodes
256 * if ((p_node_a->IsBoundaryNode() && p_node_b->IsBoundaryNode()))
257 */
258 bool is_boundary_edge = false;
259 double direction_of_normal = 1.0;
260 if ((edge_length < maxDelaunayEdgeLength) && (shared_elements.size() == 1)) // its a boundary edge
261 {
262 is_boundary_edge = true;
263 }
264 if (bad_element && (edge_length < maxDelaunayEdgeLength))
265 {
266 /*
267 * Here one or more of the edges in the element is longer than maxDelaunayEdgeLength so the other edges are boundary edges
268 */
269
270 assert(!is_boundary_edge); // We shouldnt have short edged which are in 2 elements.
271 is_boundary_edge = true;
272 // Here we're pointing in to the element
273 direction_of_normal = -1.0;
274 }
275
276 if (is_boundary_edge)
277 {
278 c_vector<double,2> normal_vector;
279
280 normal_vector[0]= edge[1];
281 normal_vector[1]= -edge[0];
282
283 double dij = norm_2(normal_vector);
284 assert(dij>1e-5); //Sanity check
285 normal_vector /= dij;
286
287 double new_node_distance = 1.0;
288
289 if (scaleBoundByEdgeLength)
290 {
291 new_node_distance = edge_length;
292 }
293
294 int num_sections = 1;
295 for (int section=0; section<=num_sections; section++)
296 {
297 double ratio;
298 if (offsetNewBoundaryNodes)
299 {
300 ratio = ((double)section+0.5)/((double)num_sections+1);
301 }
302 else
303 {
304 ratio = ((double)section)/((double)num_sections);
305 }
307 assert(ratio>=0.0);
308 assert(ratio<=1.0);
309 c_vector<double,2> new_node_location = direction_of_normal * new_node_distance * normal_vector + ratio*p_node_a->rGetLocation() + (1-ratio)*p_node_b->rGetLocation();
310
311 //Check if near other nodes (could be inefficient)
312 double node_clearance = 0.01;
313 if (!IsNearExistingNodes(new_node_location,nodes,node_clearance))
315 nodes.push_back(new Node<2>(new_node_index, new_node_location));
316 new_node_index++;
317 }
318 }
319 }
321 }
322
323// // Plot new nodes
324// NodesOnlyMesh<2> temp_mesh;
325// temp_mesh.ConstructNodesWithoutMesh(nodes, 1.0);
326// VtkMeshWriter<2, 2> mesh_writer("tempMesh", "ExtendedMesh", false);
327// mesh_writer.WriteFilesUsingMesh(temp_mesh);
328
329 MutableMesh<2,2> extended_mesh(nodes);
331 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
332 unsigned num_nodes = extended_mesh.GetNumAllElements();
333
334 // Allocate memory for mNodes and mElements
335 this->mNodes.reserve(num_nodes);
336
337 // Create as many elements as there are nodes in the mesh
338 mElements.reserve(num_elements);
339 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
341 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
342 mElements.push_back(p_element);
343 }
344
345 // Populate mNodes
348 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
349 for (unsigned i = 0; i < num_nodes; i++)
350 {
351 // Loop over nodes owned by this triangular element in the Delaunay mesh
352 // Add this node/vertex to each of the 3 vertex elements
353 for (unsigned local_index = 0; local_index < 3; local_index++)
355 unsigned elem_index = extended_mesh.GetElement(i)->GetNodeGlobalIndex(local_index);
356
357 if (elem_index < num_elements)
358 {
359 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
360 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
361
362 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
363 }
364 }
365 }
366 }
367
368 // Reorder mNodes anticlockwise
369 for (unsigned elem_index = 0; elem_index < mElements.size(); elem_index++)
370 {
376 std::vector<std::pair<double, unsigned> > index_angle_list;
377 for (unsigned local_index = 0; local_index < mElements[elem_index]->GetNumNodes(); local_index++)
378 {
379 c_vector<double, 2> vectorA = mpDelaunayMesh->GetNode(elem_index)->rGetLocation();
380 c_vector<double, 2> vectorB = mElements[elem_index]->GetNodeLocation(local_index);
381 c_vector<double, 2> centre_to_vertex = mpDelaunayMesh->GetVectorFromAtoB(vectorA, vectorB);
382
383 double angle = atan2(centre_to_vertex(1), centre_to_vertex(0));
384 unsigned global_index = mElements[elem_index]->GetNodeGlobalIndex(local_index);
385
386 std::pair<double, unsigned> pair(angle, global_index);
387 index_angle_list.push_back(pair);
388 }
389
390 // Sort the list in order of increasing angle
391 sort(index_angle_list.begin(), index_angle_list.end());
392
393 // Create a new Voronoi element and pass in the appropriate Nodes, ordered anticlockwise
394 VertexElement<2, 2>* p_new_element = new VertexElement<2, 2>(elem_index);
395 for (unsigned count = 0; count < index_angle_list.size(); count++)
396 {
397 unsigned local_index = count > 1 ? count - 1 : 0;
398 p_new_element->AddNode(mNodes[index_angle_list[count].second], local_index);
400
401 // Replace the relevant member of mElements with this Voronoi element
402 delete mElements[elem_index];
403 mElements[elem_index] = p_new_element;
404 }
405
406 this->mMeshChangesDuringSimulation = false;
407}
408
418template <>
420 : mpDelaunayMesh(&rMesh)
421{
422 // Reset member variables and clear mNodes, mFaces and mElements
424
425 unsigned num_nodes = mpDelaunayMesh->GetNumAllElements();
426
427 // Allocate memory for mNodes
428 this->mNodes.reserve(num_nodes);
429
430 // Populate mNodes
432
433 std::map<unsigned, VertexElement<3, 3>*> index_element_map;
434 unsigned face_index = 0;
435 unsigned element_index = 0;
436
437 // Loop over each edge of the Delaunay mesh and populate mFaces and mElements
438 for (TetrahedralMesh<3, 3>::EdgeIterator edge_iterator = mpDelaunayMesh->EdgesBegin();
439 edge_iterator != mpDelaunayMesh->EdgesEnd();
440 ++edge_iterator)
441 {
442 Node<3>* p_node_a = edge_iterator.GetNodeA();
443 Node<3>* p_node_b = edge_iterator.GetNodeB();
444
445 if (!(p_node_a->IsBoundaryNode() && p_node_b->IsBoundaryNode()))
447 std::set<unsigned>& node_a_element_indices = p_node_a->rGetContainingElementIndices();
448 std::set<unsigned>& node_b_element_indices = p_node_b->rGetContainingElementIndices();
449 std::set<unsigned> edge_element_indices;
450
451 std::set_intersection(node_a_element_indices.begin(),
452 node_a_element_indices.end(),
453 node_b_element_indices.begin(),
454 node_b_element_indices.end(),
455 std::inserter(edge_element_indices, edge_element_indices.begin()));
456
457 c_vector<double, 3> edge_vector;
458 edge_vector = p_node_b->rGetLocation() - p_node_a->rGetLocation();
460 c_vector<double, 3> mid_edge;
461 mid_edge = edge_vector * 0.5 + p_node_a->rGetLocation();
462
463 unsigned element0_index = *(edge_element_indices.begin());
464
465 c_vector<double, 3> basis_vector1;
466 basis_vector1 = mNodes[element0_index]->rGetLocation() - mid_edge;
467
468 c_vector<double, 3> basis_vector2;
469 basis_vector2 = VectorProduct(edge_vector, basis_vector1);
470
476 std::vector<std::pair<double, unsigned> > index_angle_list;
477
478 // Loop over each element containing this edge (i.e. those containing both nodes of the edge)
479 for (std::set<unsigned>::iterator index_iter = edge_element_indices.begin();
480 index_iter != edge_element_indices.end();
481 ++index_iter)
482 {
483 // Calculate angle
484 c_vector<double, 3> vertex_vector = mNodes[*index_iter]->rGetLocation() - mid_edge;
486 double local_vertex_dot_basis_vector1 = inner_prod(vertex_vector, basis_vector1);
487 double local_vertex_dot_basis_vector2 = inner_prod(vertex_vector, basis_vector2);
488
489 double angle = atan2(local_vertex_dot_basis_vector2, local_vertex_dot_basis_vector1);
490
491 std::pair<double, unsigned> pair(angle, *index_iter);
492 index_angle_list.push_back(pair);
493 }
494
495 // Sort the list in order of increasing angle
496 sort(index_angle_list.begin(), index_angle_list.end());
497
498 // Create face
499 VertexElement<2, 3>* p_face = new VertexElement<2, 3>(face_index);
500 face_index++;
501 for (unsigned count = 0; count < index_angle_list.size(); count++)
502 {
503 unsigned local_index = count > 1 ? count - 1 : 0;
504 p_face->AddNode(mNodes[index_angle_list[count].second], local_index);
505 }
506
507 // Add face to list of faces
508 mFaces.push_back(p_face);
509
510 // Add face to appropriate elements
511 if (!p_node_a->IsBoundaryNode())
512 {
513 unsigned node_a_index = p_node_a->GetIndex();
514 if (index_element_map[node_a_index])
515 {
516 // If there is already an element with this index, add the face to it...
517 index_element_map[node_a_index]->AddFace(p_face);
518 }
519 else
520 {
521 // ...otherwise create an element, add the face to it, and add to the map
522 mVoronoiElementIndexMap[node_a_index] = element_index;
523 VertexElement<3, 3>* p_element = new VertexElement<3, 3>(element_index);
524 element_index++;
525 p_element->AddFace(p_face);
526 index_element_map[node_a_index] = p_element;
528 }
529 if (!p_node_b->IsBoundaryNode())
530 {
531 unsigned node_b_index = p_node_b->GetIndex();
532 if (index_element_map[node_b_index])
533 {
534 // If there is already an element with this index, add the face to it...
535 index_element_map[node_b_index]->AddFace(p_face);
536 }
537 else
538 {
539 // ...otherwise create an element, add the face to it, and add to the map
540 mVoronoiElementIndexMap[node_b_index] = element_index;
541 VertexElement<3, 3>* p_element = new VertexElement<3, 3>(element_index);
542 element_index++;
543 p_element->AddFace(p_face);
544 index_element_map[node_b_index] = p_element;
545 }
546 }
548 }
549
550 // Populate mElements
551 unsigned elem_count = 0;
552 for (std::map<unsigned, VertexElement<3, 3>*>::iterator element_iter = index_element_map.begin();
553 element_iter != index_element_map.end();
554 ++element_iter)
555 {
556 mElements.push_back(element_iter->second);
557 mVoronoiElementIndexMap[element_iter->first] = elem_count;
558 elem_count++;
559 }
561 this->mMeshChangesDuringSimulation = false;
562}
568template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
570 std::vector<VertexElement<ELEMENT_DIM, SPACE_DIM>*>& rElements)
572 // Build a list of unique edges from nodes within all the elements
573 for (auto elem : rElements)
574 {
575 elem->SetEdgeHelper(&mEdgeHelper);
576 elem->BuildEdges();
577 }
578}
579
580template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
582{
583 return mEdgeHelper.GetNumEdges();
584}
585
586template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
588{
589 return mEdgeHelper.GetEdge(index);
590}
591
592template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
594{
595 return mEdgeHelper;
596}
597
598template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
600{
601 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
602 c_matrix<double, ELEMENT_DIM, SPACE_DIM> inverse_jacobian;
603 double jacobian_det;
604
605 // Loop over elements of the Delaunay mesh and populate mNodes
606 for (unsigned i = 0; i < rMesh.GetNumElements(); i++)
607 {
608 // Calculate the circumcentre of this element in the Delaunay mesh
609 rMesh.GetInverseJacobianForElement(i, jacobian, jacobian_det, inverse_jacobian);
610 c_vector<double, SPACE_DIM + 1> circumsphere = rMesh.GetElement(i)->CalculateCircumsphere(jacobian, inverse_jacobian);
611
612 c_vector<double, SPACE_DIM> circumcentre;
613 for (unsigned j = 0; j < SPACE_DIM; j++)
614 {
615 circumcentre(j) = circumsphere(j);
616 }
617
618 // Create a node in the Voronoi mesh at the location of this circumcentre
619 this->mNodes.push_back(new Node<SPACE_DIM>(i, circumcentre));
621}
622
623template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
624double VertexMesh<ELEMENT_DIM, SPACE_DIM>::GetEdgeLength(unsigned elementIndex1, unsigned elementIndex2)
625{
626 assert(SPACE_DIM == 2); // LCOV_EXCL_LINE - code will be removed at compile time
627
628 std::set<unsigned> node_indices_1;
629 for (unsigned i = 0; i < mElements[elementIndex1]->GetNumNodes(); i++)
630 {
631 node_indices_1.insert(mElements[elementIndex1]->GetNodeGlobalIndex(i));
632 }
633 std::set<unsigned> node_indices_2;
634 for (unsigned i = 0; i < mElements[elementIndex2]->GetNumNodes(); i++)
635 {
636 node_indices_2.insert(mElements[elementIndex2]->GetNodeGlobalIndex(i));
637 }
639 std::set<unsigned> shared_nodes;
640 std::set_intersection(node_indices_1.begin(), node_indices_1.end(),
641 node_indices_2.begin(), node_indices_2.end(),
642 std::inserter(shared_nodes, shared_nodes.begin()));
643
644 if (shared_nodes.size() == 1)
645 {
646 // It's possible that these two elements are actually infinite but are on the edge of the domain
647 EXCEPTION("Elements " << elementIndex1 << " and " << elementIndex2 << " share only one node.");
648 }
649 assert(shared_nodes.size() == 2);
651 unsigned index1 = *(shared_nodes.begin());
652 unsigned index2 = *(++(shared_nodes.begin()));
653
654 double edge_length = this->GetDistanceBetweenNodes(index1, index2);
655 return edge_length;
656}
657
658template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
660{
661 assert(SPACE_DIM == 2); // LCOV_EXCL_LINE - code will be removed at compile time
662
663 c_vector<double, 3> moments = CalculateMomentsOfElement(index);
664
665 double discriminant = sqrt((moments(0) - moments(1)) * (moments(0) - moments(1)) + 4.0 * moments(2) * moments(2));
666
667 // Note that as the matrix of second moments of area is symmetric, both its eigenvalues are real
668 double largest_eigenvalue = (moments(0) + moments(1) + discriminant) * 0.5;
669 double smallest_eigenvalue = (moments(0) + moments(1) - discriminant) * 0.5;
670
671 double elongation_shape_factor = sqrt(largest_eigenvalue / smallest_eigenvalue);
672 return elongation_shape_factor;
673}
674
675template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
677{
678 mpDelaunayMesh = nullptr;
679 this->mMeshChangesDuringSimulation = false;
680 Clear();
681}
682
683template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
688
689template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
691{
692 assert(index < this->mNodes.size());
693 return index;
694}
695
696template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
698{
699 assert(index < this->mElements.size());
700 return index;
701}
702
703template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
705{
707 // assert(index < this->mBoundaryElements.size() );
708 return index;
709}
710
711template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
713{
714 unsigned node_index = UNSIGNED_UNSET;
715
716 if (mVoronoiElementIndexMap.empty())
717 {
718 node_index = elementIndex;
719 }
720 else
721 {
722 for (std::map<unsigned, unsigned>::iterator iter = mVoronoiElementIndexMap.begin();
723 iter != mVoronoiElementIndexMap.end();
724 ++iter)
725 {
726 if (iter->second == elementIndex)
727 {
728 node_index = iter->first;
729 break;
730 }
731 }
732 }
733 assert(node_index != UNSIGNED_UNSET);
734 return node_index;
735}
736
737template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
739{
740 unsigned element_index = UNSIGNED_UNSET;
741
742 if (mVoronoiElementIndexMap.empty())
743 {
744 element_index = nodeIndex;
745 }
746 else
747 {
748 std::map<unsigned, unsigned>::iterator iter = mVoronoiElementIndexMap.find(nodeIndex);
749
750 if (iter == mVoronoiElementIndexMap.end())
751 {
752 EXCEPTION("This index does not correspond to a VertexElement");
753 }
754 else
755 {
756 element_index = iter->second;
757 }
758 }
759 assert(element_index != UNSIGNED_UNSET);
760 return element_index;
761}
762
763template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
765{
766 assert(SPACE_DIM == 2 || SPACE_DIM == 3); // LCOV_EXCL_LINE - code will be removed at compile time
767
768 // Get pointer to this element
769 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = GetElement(index);
770
771 // Loop over nodes in the current element and find which is contained in the most elements
772 unsigned rosette_rank = 0;
773 for (unsigned node_idx = 0; node_idx < p_element->GetNumNodes(); node_idx++)
774 {
775 unsigned num_elems_this_node = p_element->GetNode(node_idx)->rGetContainingElementIndices().size();
776
777 if (num_elems_this_node > rosette_rank)
778 {
779 rosette_rank = num_elems_this_node;
780 }
781 }
782
783 // Return the rosette rank
784 return rosette_rank;
785}
786
787template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
789{
790 // Delete elements
791 for (unsigned i = 0; i < mElements.size(); i++)
792 {
793 delete mElements[i];
794 }
795 mElements.clear();
796
797 // Delete faces
798 for (unsigned i = 0; i < mFaces.size(); i++)
799 {
800 delete mFaces[i];
801 }
802 mFaces.clear();
803
804
805 // Delete nodes
806 for (unsigned i = 0; i < this->mNodes.size(); i++)
807 {
808 delete this->mNodes[i];
809 }
810 this->mNodes.clear();
811}
812
813template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
815{
816 return this->mNodes.size();
817}
818
819template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
821{
822 return mElements.size();
823}
824
825template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
827{
828 return mElements.size();
829}
830
831template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
833{
834 return mFaces.size();
835}
836
837template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
839{
840 assert(index < mElements.size());
841 return mElements[index];
842}
843
844template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
845VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* VertexMesh<ELEMENT_DIM, SPACE_DIM>::GetFace(unsigned index) const
846{
847 assert(index < mFaces.size());
848 return mFaces[index];
849}
850
851template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
852c_vector<double, SPACE_DIM> VertexMesh<ELEMENT_DIM, SPACE_DIM>::GetCentroidOfElement(unsigned index)
853{
854 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = GetElement(index);
855 unsigned num_nodes = p_element->GetNumNodes();
856
857 c_vector<double, SPACE_DIM> centroid = zero_vector<double>(SPACE_DIM);
858
859 switch (SPACE_DIM)
860 {
861 case 1:
862 {
863 centroid = 0.5 * (p_element->GetNodeLocation(0) + p_element->GetNodeLocation(1));
864 }
865 break;
866 case 2:
867 {
868 double centroid_x = 0;
869 double centroid_y = 0;
870
871 // Note that we cannot use GetVolumeOfElement() below as it returns the absolute, rather than signed, area
872 double element_signed_area = 0.0;
873
874 // Map the first vertex to the origin and employ GetVectorFromAtoB() to allow for periodicity
875 c_vector<double, SPACE_DIM> first_node_location = p_element->GetNodeLocation(0);
876 c_vector<double, SPACE_DIM> pos_1 = zero_vector<double>(SPACE_DIM);
877
878 // Loop over vertices
879 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
880 {
881 c_vector<double, SPACE_DIM> next_node_location = p_element->GetNodeLocation((local_index + 1) % num_nodes);
882 c_vector<double, SPACE_DIM> pos_2 = GetVectorFromAtoB(first_node_location, next_node_location);
883
884 double this_x = pos_1[0];
885 double this_y = pos_1[1];
886 double next_x = pos_2[0];
887 double next_y = pos_2[1];
888
889 double signed_area_term = this_x * next_y - this_y * next_x;
890
891 centroid_x += (this_x + next_x) * signed_area_term;
892 centroid_y += (this_y + next_y) * signed_area_term;
893 element_signed_area += 0.5 * signed_area_term;
894
895 pos_1 = pos_2;
896 }
897
898 assert(element_signed_area != 0.0);
899
900 // Finally, map back and employ GetVectorFromAtoB() to allow for periodicity
901 centroid = first_node_location;
902 centroid(0) += centroid_x / (6.0 * element_signed_area);
903 centroid(1) += centroid_y / (6.0 * element_signed_area);
904 }
905 break;
906 case 3:
907 {
909 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
910 {
911 centroid += p_element->GetNodeLocation(local_index);
912 }
913 centroid /= ((double)num_nodes);
914 }
915 break;
916 default:
918 }
919 return centroid;
920}
921
922template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
924{
925 // Create a set of neighbouring node indices
926 std::set<unsigned> neighbouring_node_indices;
927
928 // Find the indices of the elements owned by this node
929 std::set<unsigned> containing_elem_indices = this->GetNode(nodeIndex)->rGetContainingElementIndices();
930
931 // Iterate over these elements
932 for (std::set<unsigned>::iterator elem_iter = containing_elem_indices.begin();
933 elem_iter != containing_elem_indices.end();
934 ++elem_iter)
935 {
936 // Find the local index of this node in this element
937 unsigned local_index = GetElement(*elem_iter)->GetNodeLocalIndex(nodeIndex);
938
939 // Find the global indices of the preceding and successive nodes in this element
940 unsigned num_nodes = GetElement(*elem_iter)->GetNumNodes();
941 unsigned previous_local_index = (local_index + num_nodes - 1) % num_nodes;
942 unsigned next_local_index = (local_index + 1) % num_nodes;
943
944 // Add the global indices of these two nodes to the set of neighbouring node indices
945 neighbouring_node_indices.insert(GetElement(*elem_iter)->GetNodeGlobalIndex(previous_local_index));
946 neighbouring_node_indices.insert(GetElement(*elem_iter)->GetNodeGlobalIndex(next_local_index));
947 }
948
949 return neighbouring_node_indices;
950}
951
952template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
953std::set<unsigned> VertexMesh<ELEMENT_DIM, SPACE_DIM>::GetNeighbouringNodeNotAlsoInElement(unsigned nodeIndex, unsigned elemIndex)
954{
955 // Get a pointer to this element
956 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = this->GetElement(elemIndex);
957
958 // Get the indices of the nodes contained in this element
959 std::set<unsigned> node_indices_in_this_element;
960 for (unsigned local_index = 0; local_index < p_element->GetNumNodes(); local_index++)
961 {
962 unsigned global_index = p_element->GetNodeGlobalIndex(local_index);
963 node_indices_in_this_element.insert(global_index);
964 }
965
966 // Check that the node is in fact contained in the element
967 if (node_indices_in_this_element.find(nodeIndex) == node_indices_in_this_element.end())
968 {
969 EXCEPTION("The given node is not contained in the given element.");
970 }
971
972 // Create a set of neighbouring node indices
973 std::set<unsigned> neighbouring_node_indices_not_in_this_element;
974
975 // Get the indices of this node's neighbours
976 std::set<unsigned> node_neighbours = GetNeighbouringNodeIndices(nodeIndex);
977
978 // Check if each neighbour is also in this element; if not, add it to the set
979 for (std::set<unsigned>::iterator iter = node_neighbours.begin();
980 iter != node_neighbours.end();
981 ++iter)
982 {
983 if (node_indices_in_this_element.find(*iter) == node_indices_in_this_element.end())
984 {
985 neighbouring_node_indices_not_in_this_element.insert(*iter);
986 }
987 }
988
989 return neighbouring_node_indices_not_in_this_element;
990}
991
992template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
994{
995 // Get a pointer to this element
996 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = this->GetElement(elementIndex);
997
998 // Create a set of neighbouring element indices
999 std::set<unsigned> neighbouring_element_indices;
1000
1001 // Loop over nodes owned by this element
1002 for (unsigned local_index = 0; local_index < p_element->GetNumNodes(); local_index++)
1003 {
1004 // Get a pointer to this node
1005 Node<SPACE_DIM>* p_node = p_element->GetNode(local_index);
1006
1007 // Find the indices of the elements owned by this node
1008 std::set<unsigned> containing_elem_indices = p_node->rGetContainingElementIndices();
1009
1010 // Form the union of this set with the current set of neighbouring element indices
1011 std::set<unsigned> all_elements;
1012 std::set_union(neighbouring_element_indices.begin(), neighbouring_element_indices.end(),
1013 containing_elem_indices.begin(), containing_elem_indices.end(),
1014 std::inserter(all_elements, all_elements.begin()));
1015
1016 // Update the set of neighbouring element indices
1017 neighbouring_element_indices = all_elements;
1018 }
1019
1020 // Lastly remove this element's index from the set of neighbouring element indices
1021 neighbouring_element_indices.erase(elementIndex);
1022
1023 return neighbouring_element_indices;
1024}
1025
1026template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1031
1032template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1033bool VertexMesh<ELEMENT_DIM, SPACE_DIM>::IsNearExistingNodes(c_vector<double,SPACE_DIM> newNodeLocation, std::vector<Node<SPACE_DIM> *> nodesToCheck, double minClearance)
1034{
1035 bool node_near = false;
1036
1037 for (unsigned i=0; i<nodesToCheck.size(); i++)
1038 {
1039 double distance = norm_2(mpDelaunayMesh->GetVectorFromAtoB(nodesToCheck[i]->rGetLocation(), newNodeLocation));
1040 if (distance < minClearance)
1041 {
1042 node_near = true;
1043 break;
1044 }
1045 }
1046
1047 return node_near;
1048}
1049
1051template <>
1054{
1055 EXCEPTION("VertexMesh<1,1>::ConstructFromMeshReader() is not implemented");
1056}
1057
1059template <>
1062{
1063 EXCEPTION("VertexMesh<1,2>::ConstructFromMeshReader() is not implemented");
1064}
1065
1067template <>
1070{
1071 EXCEPTION("VertexMesh<1,3>::ConstructFromMeshReader() is not implemented");
1072}
1073
1075template <>
1078{
1079 EXCEPTION("VertexMesh<2,3>::ConstructFromMeshReader() is not implemented");
1080}
1081
1083template <>
1086{
1087 assert(rMeshReader.HasNodePermutation() == false);
1088 // Store numbers of nodes and elements
1089 unsigned num_nodes = rMeshReader.GetNumNodes();
1090 unsigned num_elements = rMeshReader.GetNumElements();
1091
1092 // Reserve memory for nodes
1093 this->mNodes.reserve(num_nodes);
1094
1095 rMeshReader.Reset();
1096
1097 // Add nodes
1098 std::vector<double> node_data;
1099 for (unsigned i = 0; i < num_nodes; i++)
1100 {
1101 node_data = rMeshReader.GetNextNode();
1102 unsigned is_boundary_node = (bool)node_data[2];
1103 node_data.pop_back();
1104 this->mNodes.push_back(new Node<2>(i, node_data, is_boundary_node));
1105 }
1106
1107 rMeshReader.Reset();
1108
1109 // Reserve memory for elements
1110 mElements.reserve(rMeshReader.GetNumElements());
1111
1112 // Add elements
1113 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
1114 {
1115 // Get the data for this element
1116 ElementData element_data = rMeshReader.GetNextElementData();
1117
1118 // Get the nodes owned by this element
1119 std::vector<Node<2>*> nodes;
1120 unsigned num_nodes_in_element = element_data.NodeIndices.size();
1121 for (unsigned j = 0; j < num_nodes_in_element; j++)
1122 {
1123 assert(element_data.NodeIndices[j] < this->mNodes.size());
1124 nodes.push_back(this->mNodes[element_data.NodeIndices[j]]);
1125 }
1126
1127 // Use nodes and index to construct this element
1128 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index, nodes);
1129 mElements.push_back(p_element);
1130
1131 if (rMeshReader.GetNumElementAttributes() > 0)
1132 {
1133 assert(rMeshReader.GetNumElementAttributes() == 1);
1134 unsigned attribute_value = (unsigned)element_data.AttributeValue;
1135 p_element->SetAttribute(attribute_value);
1136 }
1137 }
1138 GenerateEdgesFromElements(mElements);
1139}
1140
1142template <>
1145{
1146 assert(rMeshReader.HasNodePermutation() == false);
1147
1148 // Store numbers of nodes and elements
1149 unsigned num_nodes = rMeshReader.GetNumNodes();
1150 unsigned num_elements = rMeshReader.GetNumElements();
1151
1152 // Reserve memory for nodes
1153 this->mNodes.reserve(num_nodes);
1154
1155 rMeshReader.Reset();
1156
1157 // Add nodes
1158 std::vector<double> node_data;
1159 for (unsigned i = 0; i < num_nodes; i++)
1160 {
1161 node_data = rMeshReader.GetNextNode();
1162 unsigned is_boundary_node = (bool)node_data[3];
1163 node_data.pop_back();
1164 this->mNodes.push_back(new Node<3>(i, node_data, is_boundary_node));
1165 }
1166
1167 rMeshReader.Reset();
1168
1169 // Reserve memory for nodes
1170 mElements.reserve(rMeshReader.GetNumElements());
1171
1172 // Use a std::set to keep track of which faces have been added to mFaces
1173 std::set<unsigned> faces_counted;
1174
1175 // Add elements
1176 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
1177 {
1179 typedef VertexMeshReader<3, 3> VERTEX_MESH_READER;
1180 assert(dynamic_cast<VERTEX_MESH_READER*>(&rMeshReader) != nullptr);
1181
1182 // Get the data for this element
1183 VertexElementData element_data = static_cast<VERTEX_MESH_READER*>(&rMeshReader)->GetNextElementDataWithFaces();
1184
1185 // Get the nodes owned by this element
1186 std::vector<Node<3>*> nodes;
1187 unsigned num_nodes_in_element = element_data.NodeIndices.size();
1188 for (unsigned j = 0; j < num_nodes_in_element; j++)
1189 {
1190 assert(element_data.NodeIndices[j] < this->mNodes.size());
1191 nodes.push_back(this->mNodes[element_data.NodeIndices[j]]);
1192 }
1193
1194 // Get the faces owned by this element
1195 std::vector<VertexElement<2, 3>*> faces;
1196 unsigned num_faces_in_element = element_data.Faces.size();
1197 for (unsigned i = 0; i < num_faces_in_element; i++)
1198 {
1199 // Get the data for this face
1200 ElementData face_data = element_data.Faces[i];
1201
1202 // Get the face index
1203 unsigned face_index = (unsigned)face_data.AttributeValue;
1204
1205 // Get the nodes owned by this face
1206 std::vector<Node<3>*> nodes_in_face;
1207 unsigned num_nodes_in_face = face_data.NodeIndices.size();
1208 for (unsigned j = 0; j < num_nodes_in_face; j++)
1209 {
1210 assert(face_data.NodeIndices[j] < this->mNodes.size());
1211 nodes_in_face.push_back(this->mNodes[face_data.NodeIndices[j]]);
1212 }
1213
1214 // If this face index is not already encountered, create a new face and update faces_counted...
1215 if (faces_counted.find(face_index) == faces_counted.end())
1216 {
1217 // Use nodes and index to construct this face
1218 VertexElement<2, 3>* p_face = new VertexElement<2, 3>(face_index, nodes_in_face);
1219 mFaces.push_back(p_face);
1220 faces_counted.insert(face_index);
1221 faces.push_back(p_face);
1222 }
1223 else
1224 {
1225 // ... otherwise use the member of mFaces with this index
1226 bool face_added = false;
1227 for (unsigned k = 0; k < mFaces.size(); k++)
1228 {
1229 if (mFaces[k]->GetIndex() == face_index)
1230 {
1231 faces.push_back(mFaces[k]);
1232 face_added = true;
1233 break;
1234 }
1235 }
1236 UNUSED_OPT(face_added);
1237 assert(face_added == true);
1238 }
1239 }
1240
1242 std::vector<bool> orientations = std::vector<bool>(num_faces_in_element, true);
1243
1244 // Use faces and index to construct this element
1245 VertexElement<3, 3>* p_element = new VertexElement<3, 3>(elem_index, faces, orientations, nodes);
1246 mElements.push_back(p_element);
1247
1248 if (rMeshReader.GetNumElementAttributes() > 0)
1249 {
1250 assert(rMeshReader.GetNumElementAttributes() == 1);
1251 unsigned attribute_value = element_data.AttributeValue;
1252 p_element->SetAttribute(attribute_value);
1253 }
1254 }
1255}
1256
1257template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1259 const c_vector<double, SPACE_DIM>& rLocationA, const c_vector<double, SPACE_DIM>& rLocationB)
1260{
1261 c_vector<double, SPACE_DIM> vector {};
1262 if (mpDelaunayMesh)
1263 {
1264 vector = mpDelaunayMesh->GetVectorFromAtoB(rLocationA, rLocationB);
1265 }
1266 else
1267 {
1268 vector = AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetVectorFromAtoB(rLocationA, rLocationB);
1269 }
1270 return vector;
1271}
1272
1273template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1275{
1276 assert(SPACE_DIM == 2 || SPACE_DIM == 3); // LCOV_EXCL_LINE - code will be removed at compile time
1277
1278 // Get pointer to this element
1279 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = GetElement(index);
1280
1281 double element_volume = 0.0;
1282 if (SPACE_DIM == 2)
1283 {
1284 // We map the first vertex to the origin and employ GetVectorFromAtoB() to allow for periodicity
1285 c_vector<double, SPACE_DIM> first_node_location = p_element->GetNodeLocation(0);
1286 c_vector<double, SPACE_DIM> pos_1 = zero_vector<double>(SPACE_DIM);
1287
1288 unsigned num_nodes = p_element->GetNumNodes();
1289 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
1290 {
1291 c_vector<double, SPACE_DIM> next_node_location = p_element->GetNodeLocation((local_index + 1) % num_nodes);
1292 c_vector<double, SPACE_DIM> pos_2 = GetVectorFromAtoB(first_node_location, next_node_location);
1293
1294 double this_x = pos_1[0];
1295 double this_y = pos_1[1];
1296 double next_x = pos_2[0];
1297 double next_y = pos_2[1];
1298
1299 element_volume += 0.5 * (this_x * next_y - next_x * this_y);
1300
1301 pos_1 = pos_2;
1302 }
1303 }
1304 else
1305 {
1306 // Loop over faces and add up pyramid volumes
1307 c_vector<double, SPACE_DIM> pyramid_apex = p_element->GetNodeLocation(0);
1308 for (unsigned face_index = 0; face_index < p_element->GetNumFaces(); face_index++)
1309 {
1310 // Get pointer to face
1311 VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* p_face = p_element->GetFace(face_index);
1312
1313 // Calculate the area of the face and get unit normal to this face
1314 c_vector<double, SPACE_DIM> unit_normal;
1315 double face_area = CalculateUnitNormalToFaceWithArea(p_face, unit_normal);
1316
1317 // Calculate the perpendicular distance from the plane of the face to the chosen apex
1318 c_vector<double, SPACE_DIM> base_to_apex = GetVectorFromAtoB(p_face->GetNodeLocation(0), pyramid_apex);
1319 double perpendicular_distance = fabs(inner_prod(base_to_apex, unit_normal));
1320
1321 // Use these to calculate the volume of the pyramid formed by the face and the point pyramid_apex
1322 element_volume += face_area * perpendicular_distance / 3;
1323 }
1324 }
1325
1326 // We take the absolute value just in case the nodes were really oriented clockwise
1327 return fabs(element_volume);
1328}
1329
1330template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1332{
1333 assert(SPACE_DIM == 2 || SPACE_DIM == 3); // LCOV_EXCL_LINE - code will be removed at compile time
1334
1335 // Get pointer to this element
1336 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = GetElement(index);
1337
1338 double surface_area = 0.0;
1339 if (SPACE_DIM == 2)
1340 {
1341 unsigned num_nodes = p_element->GetNumNodes();
1342 unsigned this_node_index = p_element->GetNodeGlobalIndex(0);
1343 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
1344 {
1345 unsigned next_node_index = p_element->GetNodeGlobalIndex((local_index + 1) % num_nodes);
1346
1347 surface_area += this->GetDistanceBetweenNodes(this_node_index, next_node_index);
1348 this_node_index = next_node_index;
1349 }
1350 }
1351 else
1352 {
1353 // Loop over faces and add up areas
1354 for (unsigned face_index = 0; face_index < p_element->GetNumFaces(); face_index++)
1355 {
1356 surface_area += CalculateAreaOfFace(p_element->GetFace(face_index));
1357 }
1358 }
1359 return surface_area;
1360}
1361
1363// 2D-specific methods //
1365template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1367 [[maybe_unused]] const c_vector<double, SPACE_DIM>& rTestPoint, [[maybe_unused]] unsigned elementIndex)
1368{
1369 if constexpr (ELEMENT_DIM == 2 && SPACE_DIM == 2)
1370 {
1371 // Get the element
1372 VertexElement<2, 2>* p_element = GetElement(elementIndex);
1373 const unsigned num_nodes = p_element->GetNumNodes();
1374
1375 // Initialise boolean
1376 bool element_includes_point = false;
1377
1379 // // Initialise boolean
1380 // bool element_includes_point = true;
1381 //
1382 // unsigned winding_number = 0;
1383 //
1384 // c_vector<double, SPACE_DIM> first_node_location = p_element->GetNodeLocation(0);
1385 // c_vector<double, SPACE_DIM> test_point = this->GetVectorFromAtoB(first_node_location, rTestPoint);
1386 // c_vector<double, SPACE_DIM> this_node_location = zero_vector<double>(SPACE_DIM);
1387 //
1388 // // Loop over edges of the element
1389 // for (unsigned local_index=0; local_index<num_nodes; local_index++)
1390 // {
1391 // c_vector<double, SPACE_DIM> untransformed_vector = p_element->GetNodeLocation((local_index+1)%num_nodes);
1392 // c_vector<double, SPACE_DIM> next_node_location = this->GetVectorFromAtoB(first_node_location, untransformed_vector);
1393 //
1394 // // If this edge is crossing upward...
1395 // if (this_node_location[1] <= test_point[1])
1396 // {
1397 // if (next_node_location[1] > test_point[1])
1398 // {
1399 // double is_left = (next_node_location[0] - this_node_location[0])*(test_point[1] - this_node_location[1])
1400 // - (test_point[0] - this_node_location[0])*(next_node_location[1] - this_node_location[1]);
1401 //
1402 // // ...and the test point is to the left of the edge...
1403 // if (is_left > DBL_EPSILON)
1404 // {
1405 // // ...then there is a valid upward edge-ray intersection to the right of the test point
1406 // winding_number++;
1407 // }
1408 // }
1409 // }
1410 // else
1411 // {
1412 // // ...otherwise, if the edge is crossing downward
1413 // if (next_node_location[1] <= test_point[1])
1414 // {
1415 // double is_left = (next_node_location[0] - this_node_location[0])*(test_point[1] - this_node_location[1])
1416 // - (test_point[0] - this_node_location[0])*(next_node_location[1] - this_node_location[1]);
1417 //
1418 // // ...and the test point is to the right of the edge...
1419 // if (is_left < -DBL_EPSILON)
1420 // {
1421 // // ...then there is a valid downward edge-ray intersection to the right of the test point
1422 // winding_number--;
1423 // }
1424 // }
1425 // }
1426 // this_node_location = next_node_location;
1427 // }
1428 //
1429 // if (winding_number == 0)
1430 // {
1431 // element_includes_point = false;
1432 // }
1434
1435 // Remap the origin to the first vertex to allow alternative distance metrics to be used in subclasses
1436 const c_vector<double, 2> first_vertex = p_element->GetNodeLocation(0);
1437 c_vector<double, 2> test_point = GetVectorFromAtoB(first_vertex, rTestPoint);
1438
1439 // Loop over edges of the element
1440 c_vector<double, 2> vertexA = zero_vector<double>(2);
1441 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
1442 {
1443 // Check if this edge crosses the ray running out horizontally (increasing x, fixed y) from the test point
1444 c_vector<double, 2> vector_a_to_point = GetVectorFromAtoB(vertexA, test_point);
1445
1446 // Pathological case - test point coincides with vertexA
1447 // (we will check vertexB next time we go through the for loop)
1448 if (norm_2(vector_a_to_point) < DBL_EPSILON)
1449 {
1450 return false;
1451 }
1452
1453 c_vector<double, 2> vertexB = GetVectorFromAtoB(first_vertex, p_element->GetNodeLocation((local_index + 1) % num_nodes));
1454 c_vector<double, 2> vector_b_to_point = GetVectorFromAtoB(vertexB, test_point);
1455 c_vector<double, 2> vector_a_to_b = GetVectorFromAtoB(vertexA, vertexB);
1456
1457 // Pathological case - ray coincides with horizontal edge
1458 if ((fabs(vector_a_to_b[1]) < DBL_EPSILON) && (fabs(vector_a_to_point[1]) < DBL_EPSILON) && (fabs(vector_b_to_point[1]) < DBL_EPSILON))
1459 {
1460 if ((vector_a_to_point[0] > 0) != (vector_b_to_point[0] > 0))
1461 {
1462 return false;
1463 }
1464 }
1465
1466 // Non-pathological case
1467 // A and B on different sides of the line y = test_point[1]
1468 if ((vertexA[1] > test_point[1]) != (vertexB[1] > test_point[1]))
1469 {
1470 // Intersection of y=test_point[1] and vector_a_to_b is on the right of test_point
1471 if (test_point[0] < vertexA[0] + vector_a_to_b[0] * vector_a_to_point[1] / vector_a_to_b[1])
1472 {
1473 element_includes_point = !element_includes_point;
1474 }
1475 }
1476
1477 vertexA = vertexB;
1478 }
1479 return element_includes_point;
1480 }
1481 else
1482 {
1484 }
1485}
1486
1487template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1489 [[maybe_unused]] const c_vector<double, SPACE_DIM>& rTestPoint, [[maybe_unused]] unsigned elementIndex)
1490{
1491 if constexpr (ELEMENT_DIM == 2 && SPACE_DIM == 2)
1492 {
1493 // Get the element
1494 VertexElement<2, 2>* p_element = GetElement(elementIndex);
1495 const unsigned num_nodes = p_element->GetNumNodes();
1496
1497 double min_squared_normal_distance = DBL_MAX;
1498 unsigned min_distance_edge_index = UINT_MAX;
1499
1500 // Loop over edges of the element
1501 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
1502 {
1503 // Get the end points of this edge
1504 const c_vector<double, 2> vertexA = p_element->GetNodeLocation(local_index);
1505 const c_vector<double, 2> vertexB = p_element->GetNodeLocation((local_index + 1) % num_nodes);
1506
1507 const c_vector<double, 2> vector_a_to_point = this->GetVectorFromAtoB(vertexA, rTestPoint);
1508 const c_vector<double, 2> vector_a_to_b = this->GetVectorFromAtoB(vertexA, vertexB);
1509 const double distance_a_to_b = norm_2(vector_a_to_b);
1510
1511 const c_vector<double, 2> edge_ab_unit_vector = vector_a_to_b / norm_2(vector_a_to_b);
1512 double distance_parallel_to_edge = inner_prod(vector_a_to_point, edge_ab_unit_vector);
1513
1514 double squared_distance_normal_to_edge = SmallPow(norm_2(vector_a_to_point), 2) - SmallPow(distance_parallel_to_edge, 2);
1515
1516 /*
1517 * If the point lies almost bang on the supporting line of the edge, then snap to the line.
1518 * This allows us to do floating point tie-breaks when line is exactly at a node.
1519 * We adopt a similar approach if the point is at the same position as a point in the
1520 * element.
1521 */
1522 if (squared_distance_normal_to_edge < DBL_EPSILON)
1523 {
1524 squared_distance_normal_to_edge = 0.0;
1525 }
1526
1527 if (fabs(distance_parallel_to_edge) < DBL_EPSILON)
1528 {
1529 distance_parallel_to_edge = 0.0;
1530 }
1531 else if (fabs(distance_parallel_to_edge - distance_a_to_b) < DBL_EPSILON)
1532 {
1533 distance_parallel_to_edge = distance_a_to_b;
1534 }
1535
1536 // Make sure node is within the confines of the edge and is the nearest edge to the node \this breaks for convex elements
1537 if (squared_distance_normal_to_edge < min_squared_normal_distance && distance_parallel_to_edge >= 0 && distance_parallel_to_edge <= distance_a_to_b)
1538 {
1539 min_squared_normal_distance = squared_distance_normal_to_edge;
1540 min_distance_edge_index = local_index;
1541 }
1542 }
1543
1544 assert(min_distance_edge_index < num_nodes);
1545 return min_distance_edge_index;
1546 }
1547 else
1548 {
1550 }
1551}
1552
1553template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1554c_vector<double, 3> VertexMesh<ELEMENT_DIM, SPACE_DIM>::CalculateMomentsOfElement([[maybe_unused]] unsigned index)
1555{
1556 if constexpr (SPACE_DIM == 2)
1557 {
1558 // Define helper variables
1559 VertexElement<ELEMENT_DIM, SPACE_DIM>* p_element = GetElement(index);
1560 unsigned num_nodes = p_element->GetNumNodes();
1561 c_vector<double, 3> moments = zero_vector<double>(3);
1562
1563 // Since we compute I_xx, I_yy and I_xy about the centroid, we must shift each vertex accordingly
1564 c_vector<double, SPACE_DIM> centroid = GetCentroidOfElement(index);
1565
1566 c_vector<double, SPACE_DIM> this_node_location = p_element->GetNodeLocation(0);
1567 c_vector<double, SPACE_DIM> pos_1 = this->GetVectorFromAtoB(centroid, this_node_location);
1568
1569 for (unsigned local_index = 0; local_index < num_nodes; local_index++)
1570 {
1571 unsigned next_index = (local_index + 1) % num_nodes;
1572 c_vector<double, SPACE_DIM> next_node_location = p_element->GetNodeLocation(next_index);
1573 c_vector<double, SPACE_DIM> pos_2 = this->GetVectorFromAtoB(centroid, next_node_location);
1574
1575 double signed_area_term = pos_1(0) * pos_2(1) - pos_2(0) * pos_1(1);
1576 // Ixx
1577 moments(0) += (pos_1(1) * pos_1(1) + pos_1(1) * pos_2(1) + pos_2(1) * pos_2(1)) * signed_area_term;
1578
1579 // Iyy
1580 moments(1) += (pos_1(0) * pos_1(0) + pos_1(0) * pos_2(0) + pos_2(0) * pos_2(0)) * signed_area_term;
1581
1582 // Ixy
1583 moments(2) += (pos_1(0) * pos_2(1) + 2 * pos_1(0) * pos_1(1) + 2 * pos_2(0) * pos_2(1) + pos_2(0) * pos_1(1)) * signed_area_term;
1584
1585 pos_1 = pos_2;
1586 }
1587
1588 moments(0) /= 12;
1589 moments(1) /= 12;
1590 moments(2) /= 24;
1591
1592 /*
1593 * If the nodes owned by the element were supplied in a clockwise rather
1594 * than anticlockwise manner, or if this arose as a result of enforcing
1595 * periodicity, then our computed quantities will be the wrong sign, so
1596 * we need to fix this.
1597 */
1598 if (moments(0) < 0.0)
1599 {
1600 moments(0) = -moments(0);
1601 moments(1) = -moments(1);
1602 moments(2) = -moments(2);
1603 }
1604 return moments;
1605 }
1606 else
1607 {
1609 }
1610}
1611
1612template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1613c_vector<double, SPACE_DIM> VertexMesh<ELEMENT_DIM, SPACE_DIM>::GetShortAxisOfElement([[maybe_unused]] unsigned index)
1614{
1615 if constexpr (SPACE_DIM == 2)
1616 {
1617
1618 c_vector<double, SPACE_DIM> short_axis = zero_vector<double>(SPACE_DIM);
1619
1620 // Calculate the moments of the element about its centroid (recall that I_xx and I_yy must be non-negative)
1621 c_vector<double, 3> moments = CalculateMomentsOfElement(index);
1622
1623 // Normalise the moments vector to remove problem of a very small discriminant (see #2874)
1624 moments /= norm_2(moments);
1625
1626 // If the principal moments are equal...
1627 double discriminant = (moments(0) - moments(1)) * (moments(0) - moments(1)) + 4.0 * moments(2) * moments(2);
1628 if (fabs(discriminant) < DBL_EPSILON)
1629 {
1630 // ...then every axis through the centroid is a principal axis, so return a random unit vector
1631 short_axis(0) = RandomNumberGenerator::Instance()->ranf();
1632 short_axis(1) = sqrt(1.0 - short_axis(0) * short_axis(0));
1633 }
1634 else
1635 {
1636 // If the product of inertia is zero, then the coordinate axes are the principal axes
1637 if (fabs(moments(2)) < DBL_EPSILON)
1638 {
1639 if (moments(0) < moments(1))
1640 {
1641 short_axis(0) = 0.0;
1642 short_axis(1) = 1.0;
1643 }
1644 else
1645 {
1646 short_axis(0) = 1.0;
1647 short_axis(1) = 0.0;
1648 }
1649 }
1650 else
1651 {
1652 // Otherwise we find the eigenvector of the inertia matrix corresponding to the largest eigenvalue
1653 double lambda = 0.5 * (moments(0) + moments(1) + sqrt(discriminant));
1654
1655 short_axis(0) = 1.0;
1656 short_axis(1) = (moments(0) - lambda) / moments(2);
1657
1658 // Normalise the short axis before returning it
1659 short_axis /= norm_2(short_axis);
1660 }
1661 }
1662
1663 return short_axis;
1664 }
1665 else
1666 {
1668 }
1669}
1670
1671template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1673 [[maybe_unused]] VertexElement<ELEMENT_DIM, SPACE_DIM>* pElement, [[maybe_unused]] unsigned localIndex)
1674{
1675 if constexpr (SPACE_DIM == 2)
1676 {
1677 unsigned num_nodes_in_element = pElement->GetNumNodes();
1678 unsigned next_local_index = (localIndex + 1) % num_nodes_in_element;
1679
1680 // We add an extra num_nodes_in_element in the line below as otherwise this term can be negative, which breaks the % operator
1681 unsigned previous_local_index = (num_nodes_in_element + localIndex - 1) % num_nodes_in_element;
1682
1683 c_vector<double, SPACE_DIM> previous_node_location = pElement->GetNodeLocation(previous_local_index);
1684 c_vector<double, SPACE_DIM> next_node_location = pElement->GetNodeLocation(next_local_index);
1685 c_vector<double, SPACE_DIM> difference_vector = this->GetVectorFromAtoB(previous_node_location, next_node_location);
1686
1687 c_vector<double, SPACE_DIM> area_gradient;
1688
1689 area_gradient[0] = 0.5 * difference_vector[1];
1690 area_gradient[1] = -0.5 * difference_vector[0];
1691
1692 return area_gradient;
1693 }
1694 else
1695 {
1697 }
1698}
1699
1700template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1702 [[maybe_unused]] VertexElement<ELEMENT_DIM, SPACE_DIM>* pElement, [[maybe_unused]] unsigned localIndex)
1703{
1704 if constexpr (SPACE_DIM == 2)
1705 {
1706 assert(SPACE_DIM == 2); // LCOV_EXCL_LINE - code will be removed at compile time
1707
1708 const unsigned num_nodes_in_element = pElement->GetNumNodes();
1709
1710 // We add an extra num_nodes_in_element-1 in the line below as otherwise this term can be negative, which breaks the % operator
1711 const unsigned previous_local_index = (num_nodes_in_element + localIndex - 1) % num_nodes_in_element;
1712
1713 const unsigned this_global_index = pElement->GetNodeGlobalIndex(localIndex);
1714 const unsigned previous_global_index = pElement->GetNodeGlobalIndex(previous_local_index);
1715
1716 const double previous_edge_length = this->GetDistanceBetweenNodes(this_global_index, previous_global_index);
1717 assert(previous_edge_length > DBL_EPSILON);
1718
1719 const c_vector<double, SPACE_DIM> previous_edge_gradient = this->GetVectorFromAtoB(pElement->GetNodeLocation(previous_local_index), pElement->GetNodeLocation(localIndex)) / previous_edge_length;
1720
1721 return previous_edge_gradient;
1722 }
1723 else
1724 {
1726 }
1727}
1728
1729template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1731 [[maybe_unused]] VertexElement<ELEMENT_DIM, SPACE_DIM>* pElement, [[maybe_unused]] unsigned localIndex)
1732{
1733 if constexpr (SPACE_DIM == 2)
1734 {
1735 const unsigned next_local_index = (localIndex + 1) % (pElement->GetNumNodes());
1736
1737 const unsigned this_global_index = pElement->GetNodeGlobalIndex(localIndex);
1738 const unsigned next_global_index = pElement->GetNodeGlobalIndex(next_local_index);
1739
1740 const double next_edge_length = this->GetDistanceBetweenNodes(this_global_index, next_global_index);
1741 assert(next_edge_length > DBL_EPSILON);
1742
1743 const c_vector<double, SPACE_DIM> next_edge_gradient = this->GetVectorFromAtoB(pElement->GetNodeLocation(next_local_index), pElement->GetNodeLocation(localIndex)) / next_edge_length;
1744
1745 return next_edge_gradient;
1746 }
1747 else
1748 {
1750 }
1751}
1752
1753template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1755 [[maybe_unused]] VertexElement<ELEMENT_DIM, SPACE_DIM>* pElement, [[maybe_unused]] unsigned localIndex)
1756{
1757 if constexpr (SPACE_DIM == 2)
1758 {
1759
1760 c_vector<double, SPACE_DIM> previous_edge_gradient = GetPreviousEdgeGradientOfElementAtNode(pElement, localIndex);
1761 c_vector<double, SPACE_DIM> next_edge_gradient = GetNextEdgeGradientOfElementAtNode(pElement, localIndex);
1762
1763 return previous_edge_gradient + next_edge_gradient;
1764 }
1765 else
1766 {
1768 }
1769}
1770
1772// 3D-specific methods //
1774
1775template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1777 [[maybe_unused]] VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* pFace,
1778 [[maybe_unused]] c_vector<double, SPACE_DIM>& rNormal)
1779{
1780 if constexpr (SPACE_DIM == 3)
1781 {
1782 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE - code will be removed at compile time
1783
1784 // As we are in 3D, the face must have at least three vertices
1785 assert(pFace->GetNumNodes() >= 3u);
1786
1787 // Reset the answer
1788 rNormal = zero_vector<double>(SPACE_DIM);
1789
1790 c_vector<double, SPACE_DIM> v_minus_v0 = this->GetVectorFromAtoB(pFace->GetNode(0)->rGetLocation(), pFace->GetNode(1)->rGetLocation());
1791 for (unsigned local_index = 2; local_index < pFace->GetNumNodes(); local_index++)
1792 {
1793 c_vector<double, SPACE_DIM> vnext_minus_v0 = this->GetVectorFromAtoB(pFace->GetNode(0)->rGetLocation(), pFace->GetNode(local_index)->rGetLocation());
1794 rNormal += VectorProduct(v_minus_v0, vnext_minus_v0);
1795 v_minus_v0 = vnext_minus_v0;
1796 }
1797 double magnitude = norm_2(rNormal);
1798 if (magnitude != 0.0)
1799 {
1800 // Normalize the normal vector
1801 rNormal /= magnitude;
1802 // If all points are co-located, then magnitude==0.0 and there is potential for a floating point exception
1803 // here if we divide by zero, so we'll move on.
1804 }
1805 return magnitude / 2.0;
1806 }
1807 else
1808 {
1810 }
1811}
1812
1813template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1815 [[maybe_unused]] VertexElement<ELEMENT_DIM - 1, SPACE_DIM>* pFace)
1816{
1817 if constexpr (SPACE_DIM == 3)
1818 {
1819 // Get the unit normal to the plane of this face
1820 c_vector<double, SPACE_DIM> unit_normal;
1821 return CalculateUnitNormalToFaceWithArea(pFace, unit_normal);
1822 }
1823 else
1824 {
1826 }
1827}
1828
1829
1830
1832#if defined(__xlC__)
1833template <>
1835{
1837}
1838#endif
1839
1840// Explicit instantiation
1841template class VertexMesh<1, 1>;
1842template class VertexMesh<1, 2>;
1843template class VertexMesh<1, 3>;
1844template class VertexMesh<2, 2>;
1845template class VertexMesh<2, 3>;
1846template class VertexMesh<3, 3>;
1847
1848// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define UNUSED_OPT(var)
#define NEVER_REACHED
const unsigned UNSIGNED_UNSET
Definition Exception.hpp:53
#define EXPORT_TEMPLATE_CLASS_ALL_DIMS(CLASS)
c_vector< T, 1 > VectorProduct(const c_vector< T, 1 > &rA, const c_vector< T, 1 > &rB)
void SetAttribute(double attribute)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
double GetNodeLocation(unsigned localIndex, unsigned dimension) const
unsigned GetNumNodes() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
unsigned GetIndex() const
virtual void Reset()=0
virtual unsigned GetNumElements() const =0
virtual ElementData GetNextElementData()=0
virtual unsigned GetNumElementAttributes() const
virtual std::vector< double > GetNextNode()=0
virtual bool HasNodePermutation()
virtual unsigned GetNumNodes() const =0
bool mMeshChangesDuringSimulation
virtual c_vector< double, SPACE_DIM > GetVectorFromAtoB(const c_vector< double, SPACE_DIM > &rLocationA, const c_vector< double, SPACE_DIM > &rLocationB)
std::vector< Node< SPACE_DIM > * > mNodes
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
virtual unsigned GetNumElements() const
Definition Edge.hpp:52
void AddNode(Node< SPACE_DIM > *pNode, const unsigned &rIndex)
Definition Node.hpp:59
std::set< unsigned > & rGetContainingElementIndices()
Definition Node.cpp:300
void AddElement(unsigned index)
Definition Node.cpp:268
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
bool IsBoundaryNode() const
Definition Node.cpp:164
unsigned GetIndex() const
Definition Node.cpp:158
static RandomNumberGenerator * Instance()
virtual void GetInverseJacobianForElement(unsigned elementIndex, c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, double &rJacobianDeterminant, c_matrix< double, ELEMENT_DIM, SPACE_DIM > &rInverseJacobian) const
VertexElement< ELEMENT_DIM-1, SPACE_DIM > * GetFace(unsigned index) const
void AddFace(VertexElement< ELEMENT_DIM-1, SPACE_DIM > *pFace)
unsigned GetNumFaces() const
std::vector< VertexElement< ELEMENT_DIM - 1, SPACE_DIM > * > mFaces
virtual c_vector< double, SPACE_DIM > GetVectorFromAtoB(const c_vector< double, SPACE_DIM > &rLocationA, const c_vector< double, SPACE_DIM > &rLocationB)
double CalculateUnitNormalToFaceWithArea(VertexElement< ELEMENT_DIM - 1, SPACE_DIM > *pFace, c_vector< double, SPACE_DIM > &rNormal)
VertexElement< ELEMENT_DIM - 1, SPACE_DIM > * GetFace(unsigned index) const
virtual unsigned GetNumNodes() const
virtual double GetSurfaceAreaOfElement(unsigned index)
std::set< unsigned > GetNeighbouringNodeIndices(unsigned nodeIndex)
std::set< unsigned > GetNeighbouringNodeNotAlsoInElement(unsigned nodeIndex, unsigned elemIndex)
void GenerateEdgesFromElements(std::vector< VertexElement< ELEMENT_DIM, SPACE_DIM > * > &rElements)
virtual double CalculateAreaOfFace(VertexElement< ELEMENT_DIM - 1, SPACE_DIM > *pFace)
const EdgeHelper< SPACE_DIM > & rGetEdgeHelper() const
c_vector< double, SPACE_DIM > GetNextEdgeGradientOfElementAtNode(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, unsigned localIndex)
virtual unsigned GetNumFaces() const
VertexElement< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
unsigned GetLocalIndexForElementEdgeClosestToPoint(const c_vector< double, SPACE_DIM > &rTestPoint, unsigned elementIndex)
double GetElongationShapeFactorOfElement(unsigned elementIndex)
unsigned GetNumAllElements() const
unsigned SolveNodeMapping(unsigned index) const
TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpDelaunayMesh
bool ElementIncludesPoint(const c_vector< double, SPACE_DIM > &rTestPoint, unsigned elementIndex)
c_vector< double, SPACE_DIM > GetAreaGradientOfElementAtNode(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, unsigned localIndex)
bool IsNearExistingNodes(c_vector< double, SPACE_DIM > newNodeLocation, std::vector< Node< SPACE_DIM > * > nodesToCheck, double minClearance)
std::set< unsigned > GetNeighbouringElementIndices(unsigned elementIndex)
virtual ~VertexMesh()
c_vector< double, SPACE_DIM > GetPerimeterGradientOfElementAtNode(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, unsigned localIndex)
double GetEdgeLength(unsigned elementIndex1, unsigned elementIndex2)
c_vector< double, SPACE_DIM > GetShortAxisOfElement(unsigned index)
unsigned GetRosetteRankOfElement(unsigned index)
Edge< SPACE_DIM > * GetEdge(unsigned index) const
unsigned SolveBoundaryElementMapping(unsigned index) const
unsigned SolveElementMapping(unsigned index) const
unsigned GetVoronoiElementIndexCorrespondingToDelaunayNodeIndex(unsigned nodeIndex)
virtual c_vector< double, SPACE_DIM > GetCentroidOfElement(unsigned index)
std::map< unsigned, unsigned > mVoronoiElementIndexMap
void ConstructFromMeshReader(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader)
unsigned GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(unsigned elementIndex)
virtual void Clear()
unsigned GetNumEdges() const
c_vector< double, SPACE_DIM > GetPreviousEdgeGradientOfElementAtNode(VertexElement< ELEMENT_DIM, SPACE_DIM > *pElement, unsigned localIndex)
std::vector< VertexElement< ELEMENT_DIM, SPACE_DIM > * > mElements
virtual c_vector< double, 3 > CalculateMomentsOfElement(unsigned index)
virtual VertexMesh< ELEMENT_DIM, SPACE_DIM > * GetMeshForVtk()
virtual double GetVolumeOfElement(unsigned index)
virtual unsigned GetNumElements() const
void GenerateVerticesFromElementCircumcentres(TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
std::vector< unsigned > NodeIndices
std::vector< ElementData > Faces
std::vector< unsigned > NodeIndices