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