Chaste Commit::f2ff7ee04e70ac9d06c57344df8d017dbb12b97b
Cylindrical2dVertexMesh.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 "Cylindrical2dVertexMesh.hpp"
37#include "Cylindrical2dMesh.hpp"
38
40 std::vector<Node<2>*> nodes,
41 std::vector<VertexElement<2, 2>*> vertexElements,
42 double cellRearrangementThreshold,
43 double t2Threshold)
44 : MutableVertexMesh<2,2>(nodes, vertexElements, cellRearrangementThreshold, t2Threshold),
45 mWidth(width),
46 mpMeshForVtk(nullptr)
47{
48 // Call ReMesh() to remove any deleted nodes and relabel
49 ReMesh();
50}
51
53 : mWidth(rMesh.GetWidth(0)),
54 mpMeshForVtk(nullptr)
55{
56 mpDelaunayMesh = &rMesh;
57
58 // Reset member variables and clear mNodes, mFaces and mElements
59 Clear();
60
61 if (!isBounded)
62 {
63 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
64 unsigned num_nodes = mpDelaunayMesh->GetNumAllElements();
65
66 // Allocate memory for mNodes and mElements
67 this->mNodes.reserve(num_nodes);
68
69 // Create as many elements as there are nodes in the mesh
70 mElements.reserve(num_elements);
71
72 for (unsigned elem_index=0; elem_index<num_elements; elem_index++)
73 {
74 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index);
75 mElements.push_back(p_element);
76 }
77
78 // Populate mNodes
80
81 // Loop over all nodes and check the x locations not outside [0,mWidth]
82 for (unsigned i=0; i<mNodes.size(); i++)
83 {
85 }
86
87 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
88 for (unsigned i=0; i<num_nodes; i++)
89 {
90 // Loop over nodes owned by this triangular element in the Delaunay mesh
91 // Add this node/vertex to each of the 3 vertex elements
92 for (unsigned local_index=0; local_index<3; local_index++)
93 {
94 unsigned elem_index = mpDelaunayMesh->GetElement(i)->GetNodeGlobalIndex(local_index);
95 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
96 unsigned end_index = num_nodes_in_elem>0 ? num_nodes_in_elem-1 : 0;
97
98 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
99 }
100 }
101 }
102 else // Is Bounded
103 {
104 // First create an extended mesh to include points extended from the boundary
105 std::vector<Node<2> *> nodes;
106 for (typename TetrahedralMesh<2,2>::NodeIterator node_iter = mpDelaunayMesh->GetNodeIteratorBegin();
107 node_iter != mpDelaunayMesh->GetNodeIteratorEnd();
108 ++node_iter)
109 {
110 nodes.push_back(new Node<2>(node_iter->GetIndex(), node_iter->rGetLocation(),node_iter->IsBoundaryNode()));
111 }
112
113 // Add new nodes
114 unsigned new_node_index = mpDelaunayMesh->GetNumNodes();
115 for (TetrahedralMesh<2,2>::ElementIterator elem_iter = mpDelaunayMesh->GetElementIteratorBegin();
116 elem_iter != mpDelaunayMesh->GetElementIteratorEnd();
117 ++elem_iter)
118 {
119 for (unsigned j=0; j<3; j++)
120 {
121 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
122 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
123
124 std::set<unsigned> node_a_element_indices = p_node_a->rGetContainingElementIndices();
125 std::set<unsigned> node_b_element_indices = p_node_b->rGetContainingElementIndices();
126
127 std::set<unsigned> shared_elements;
128 std::set_intersection(node_a_element_indices.begin(),
129 node_a_element_indices.end(),
130 node_b_element_indices.begin(),
131 node_b_element_indices.end(),
132 std::inserter(shared_elements, shared_elements.begin()));
133
134
135 /*
136 * Note using boundary nodes to identify the boundary edges won't work with
137 * triangles which have 3 boundary nodes
138 * if ((p_node_a->IsBoundaryNode() && p_node_b->IsBoundaryNode()))
139 * wont work with triangles which have 3 boundary nodes so we use a more
140 * general method.
141 */
142
143 if (shared_elements.size() == 1) // It's a boundary edge
144 {
145 c_vector<double,2> edge = mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation());
146 c_vector<double,2> normal_vector;
147
148 normal_vector[0]= edge[1];
149 normal_vector[1]= -edge[0];
150
151 double dij = norm_2(normal_vector);
152 assert(dij>1e-5); //Sanity check
153 normal_vector /= dij;
154
155 /*
156 * Here we only add one extra node.
157 *
158 * ____ one extra image node
159 * /\
160 * /__\ edge of mesh
161 *
162 * Changing this can cause issues with stitching the left and right back together.
163 */
164
165 double ratio = 0.5;
166 c_vector<double,2> new_node_location = normal_vector + p_node_a->rGetLocation() + ratio * edge;
167
168 // Check if near other nodes
169 bool node_clear = true;
170 double node_clearance = 0.01;
171
172 for (unsigned i=0; i<nodes.size(); i++)
173 {
174 double distance = norm_2(mpDelaunayMesh->GetVectorFromAtoB(nodes[i]->rGetLocation(), new_node_location));
175 if (distance < node_clearance)
176 {
177 node_clear = false;
178 break;
179 }
180 }
181
182 if (node_clear)
183 {
184 nodes.push_back(new Node<2>(new_node_index, new_node_location));
185 new_node_index++;
186 }
187 }
188 }
189 }
190
191 // Add new nodes for voids (note these nodes won't be labeled as boundary nodes here)
192 for (TetrahedralMesh<2,2>::ElementIterator elem_iter = mpDelaunayMesh->GetElementIteratorBegin();
193 elem_iter != mpDelaunayMesh->GetElementIteratorEnd();
194 ++elem_iter)
195 {
196 bool bad_element = false;
197 double edge_threshold = 1.5; //TODO think about making this a setable variable!
198
199 for (unsigned j=0; j<3; j++)
200 {
201 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
202 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
203 if (norm_2(mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation()))>edge_threshold)
204 {
205 bad_element = true;
206 break;
207 }
208 }
209
210 if (bad_element)
211 {
212 for (unsigned j=0; j<3; j++)
213 {
214 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
215 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
216
217 c_vector<double,2> edge = mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation());
218 double edge_length = norm_2(edge);
219
220 // The short edges in these elements are the boundaries of the void
221 if (edge_length<edge_threshold)
222 {
223 /*
224 * Here we only add one extra node.
225 *
226 * ____ one extra image node
227 * /\
228 * /__\ edge of mesh
229 *
230 * Changing this can cause issues with stitching the left and right back together.
231 */
232
233 // Short Edge so add new node
234 c_vector<double,2> normal_vector;
235
236 // Outward Normal
237 normal_vector[0]= edge[1];
238 normal_vector[1]= -edge[0];
239
240 double dij = norm_2(normal_vector);
241 assert(dij>1e-5); //Sanity check
242 normal_vector /= dij;
243
244 double bound_offset = 1.0; //TODO Think about makeing this a setable variable!
245 c_vector<double,2> new_node_location = -bound_offset*normal_vector + p_node_a->rGetLocation() + 0.5 * edge;
246
247 nodes.push_back(new Node<2>(new_node_index, new_node_location));
248 new_node_index++;
249 }
250 }
251 }
252 }
253
254 // Loop over all nodes and check they're not outside [0,mWidth)
255 for (unsigned i=0; i<nodes.size(); i++)
256 {
257 CheckNodeLocation(nodes[i]);
258 }
259
260 Cylindrical2dMesh extended_mesh(mpDelaunayMesh->GetWidth(0),nodes);
261
262 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
263 unsigned num_nodes = extended_mesh.GetNumAllElements();
264
265 // Allocate memory for mNodes and mElements
266 this->mNodes.reserve(num_nodes);
267
268 // Create as many elements as there are nodes in the mesh
269 mElements.reserve(num_elements);
270 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
271 {
272 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
273 mElements.push_back(p_element);
274 }
275
276 // Populate mNodes
278
279 // Loop over all nodes and check the x locations not outside [0,mWidth]
280 for (unsigned i=0; i<mNodes.size(); i++)
281 {
283 }
284 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
285 for (unsigned i = 0; i < num_nodes; i++)
286 {
287 // Loop over nodes owned by this triangular element in the Delaunay mesh
288 // Add this node/vertex to each of the 3 vertex elements
289 for (unsigned local_index = 0; local_index < 3; local_index++)
290 {
291 unsigned elem_index = extended_mesh.GetElement(i)->GetNodeGlobalIndex(local_index);
292
293 if (elem_index < num_elements)
294 {
295 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
296 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
297
298 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
299 }
300 }
301 }
302 }
303
304 // Reorder mNodes anticlockwise
305 for (unsigned elem_index=0; elem_index<mElements.size(); elem_index++)
306 {
312 std::vector<std::pair<double, unsigned> > index_angle_list;
313 for (unsigned local_index=0; local_index<mElements[elem_index]->GetNumNodes(); local_index++)
314 {
315 c_vector<double, 2> vectorA = mpDelaunayMesh->GetNode(elem_index)->rGetLocation();
316 c_vector<double, 2> vectorB = mElements[elem_index]->GetNodeLocation(local_index);
317 c_vector<double, 2> centre_to_vertex = mpDelaunayMesh->GetVectorFromAtoB(vectorA, vectorB);
318
319 double angle = atan2(centre_to_vertex(1), centre_to_vertex(0));
320 unsigned global_index = mElements[elem_index]->GetNodeGlobalIndex(local_index);
321
322 std::pair<double, unsigned> pair(angle, global_index);
323 index_angle_list.push_back(pair);
324 }
325
326 // Sort the list in order of increasing angle
327 sort(index_angle_list.begin(), index_angle_list.end());
328
329 // Create a new Voronoi element and pass in the appropriate Nodes, ordered anticlockwise
330 VertexElement<2,2>* p_new_element = new VertexElement<2,2>(elem_index);
331 for (unsigned count = 0; count < index_angle_list.size(); count++)
332 {
333 unsigned local_index = count>1 ? count-1 : 0;
334 p_new_element->AddNode(mNodes[index_angle_list[count].second], local_index);
335 }
336
337 // Replace the relevant member of mElements with this Voronoi element
338 delete mElements[elem_index];
339 mElements[elem_index] = p_new_element;
340 }
341
342 this->mMeshChangesDuringSimulation = false;
343}
344
346 : mpMeshForVtk(nullptr)
347{
348}
349
351{
352 if (mpMeshForVtk != nullptr)
353 {
354 delete mpMeshForVtk;
355 }
356}
357
358c_vector<double, 2> Cylindrical2dVertexMesh::GetVectorFromAtoB(const c_vector<double, 2>& rLocation1, const c_vector<double, 2>& rLocation2)
359{
360 assert(mWidth > 0.0);
361
362 c_vector<double, 2> vector = rLocation2 - rLocation1;
363 vector[0] = fmod(vector[0], mWidth);
364
365 // If the points are more than halfway around the cylinder apart, measure the other way
366 if (vector[0] > 0.5 * mWidth)
367 {
368 vector[0] -= mWidth;
369 }
370 else if (vector[0] < -0.5 * mWidth)
371 {
372 vector[0] += mWidth;
373 }
374 return vector;
375}
376
378{
379 double x_coord = point.rGetLocation()[0];
380
381 // Perform a periodic movement if necessary
382 if (x_coord >= mWidth)
383 {
384 // Move point to the left
385 point.SetCoordinate(0, x_coord - mWidth);
386 }
387 else if (x_coord < 0.0)
388 {
389 // Move point to the right
390 point.SetCoordinate(0, x_coord + mWidth);
391 }
392
393 // Update the node's location
394 MutableVertexMesh<2,2>::SetNode(nodeIndex, point);
395}
396
397double Cylindrical2dVertexMesh::GetWidth(const unsigned& rDimension) const
398{
399 double width = 0.0;
400 assert(rDimension==0 || rDimension==1);
401 if (rDimension==0)
402 {
403 width = mWidth;
404 }
405 else
406 {
407 width = VertexMesh<2,2>::GetWidth(rDimension);
408 }
409 return width;
410}
411
413{
414 CheckNodeLocation(pNewNode);
415
416 unsigned node_index = MutableVertexMesh<2,2>::AddNode(pNewNode);
417
418 return node_index;
419}
420
422{
423 double x_location = pNode->rGetLocation()[0];
424 if (x_location < 0)
425 {
426 pNode->rGetModifiableLocation()[0] = x_location + mWidth;
427 }
428 else if (x_location >= mWidth)
429 {
430 pNode->rGetModifiableLocation()[0] = x_location - mWidth;
431 }
432}
433
434void Cylindrical2dVertexMesh::Scale(const double xScale, const double yScale, const double zScale)
435{
436 assert(zScale == 1.0);
437
438 AbstractMesh<2, 2>::Scale(xScale, yScale);
439
440 // Also rescale the width of the mesh (this effectively scales the domain)
441 mWidth *= xScale;
442}
443
445{
446 unsigned num_nodes = GetNumNodes();
447
448 std::vector<Node<2>*> temp_nodes(3 * num_nodes);
449 std::vector<VertexElement<2, 2>*> elements;
450
451 // Create three copies of each node
452 for (unsigned index=0; index<num_nodes; index++)
453 {
454 c_vector<double, 2> location;
455 location = GetNode(index)->rGetLocation();
456
457 // Node copy at original location
458 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
459 temp_nodes[index] = p_node;
460
461 // Node copy shifted right
462 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
463 temp_nodes[num_nodes + index] = p_node;
464
465 // Node copy shifted left
466 p_node = new Node<2>(2 * num_nodes + index, false, location[0] - mWidth, location[1]);
467 temp_nodes[2*num_nodes + index] = p_node;
468 }
469
470 // Iterate over elements
472 elem_iter != GetElementIteratorEnd();
473 ++elem_iter)
474 {
475 unsigned elem_index = elem_iter->GetIndex();
476 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
477
478 std::vector<Node<2>*> elem_nodes;
479
480 // Compute whether the element straddles either periodic boundary
481 bool element_straddles_left_right_boundary = false;
482
483 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
484 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
485 {
486 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
487 c_vector<double, 2> vector;
488 vector = r_next_node_location - r_this_node_location;
489
490 if (fabs(vector[0]) > 0.5 * mWidth)
491 {
492 element_straddles_left_right_boundary = true;
493 }
494 }
495
496 /* If this is a voronoi tesselation make sure the elements contain
497 * the original Delaunay node
498 */
499 bool element_centre_on_right = true;
501 {
502 unsigned delaunay_index = this->GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(elem_index);
503 double element_centre_x_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[0];
504 if (element_centre_x_location < 0.5 * mWidth)
505 {
506 element_centre_on_right = false;
507 }
508 }
509
510 // Use the above information when duplicating the element in the vtk mesh
511 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
512 {
513 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
514
515 // If the element straddles the left/right periodic boundary...
516 if (element_straddles_left_right_boundary)
517 {
518 // ...and this node is located to the left of the centre of the mesh...
519 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5 * mWidth > 0);
520 if (!node_is_right_of_centre && element_centre_on_right)
521 {
522 // ...then choose the equivalent node to the right
523 this_node_index += num_nodes;
524 }
525 else if (node_is_right_of_centre && !element_centre_on_right)
526 {
527 // ...then choose the equivalent node to the left
528 this_node_index += 2 * num_nodes;
529 }
530 }
531
532 elem_nodes.push_back(temp_nodes[this_node_index]);
533 }
534
535 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
536 elements.push_back(p_element);
537 }
538
539 // Now delete any nodes from the mesh for VTK that are not contained in any elements
540 std::vector<Node<2>*> nodes;
541 unsigned count = 0;
542 for (unsigned index=0; index<temp_nodes.size(); index++)
543 {
544 unsigned num_elems_containing_this_node = temp_nodes[index]->rGetContainingElementIndices().size();
545
546 if (num_elems_containing_this_node == 0)
547 {
548 // Avoid memory leak
549 delete temp_nodes[index];
550 }
551 else
552 {
553 temp_nodes[index]->SetIndex(count);
554 nodes.push_back(temp_nodes[index]);
555 count++;
556 }
557 }
558
559 if (mpMeshForVtk != nullptr)
560 {
561 delete mpMeshForVtk;
562 }
563
564 mpMeshForVtk = new VertexMesh<2,2>(nodes, elements);
565 return mpMeshForVtk;
566}
567
568// Serialization for Boost >= 1.36
#define CHASTE_CLASS_EXPORT(T)
bool mMeshChangesDuringSimulation
virtual double GetWidth(const unsigned &rDimension) const
Node< SPACE_DIM > * GetNode(unsigned index) const
std::vector< Node< SPACE_DIM > * > mNodes
virtual void Scale(const double xFactor=1.0, const double yFactor=1.0, const double zFactor=1.0)
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
c_vector< double, DIM > & rGetLocation()
void SetCoordinate(unsigned i, double value)
void SetNode(unsigned nodeIndex, ChastePoint< 2 > point)
double GetWidth(const unsigned &rDimension) const
c_vector< double, 2 > GetVectorFromAtoB(const c_vector< double, 2 > &rLocation1, const c_vector< double, 2 > &rLocation2)
unsigned AddNode(Node< 2 > *pNewNode)
void Scale(const double xScale=1.0, const double yScale=1.0, const double zScale=1.0)
void CheckNodeLocation(Node< 2 > *pNode)
VertexMesh< 2, 2 > * GetMeshForVtk()
void AddNode(Node< SPACE_DIM > *pNode, const unsigned &rIndex)
unsigned AddNode(Node< SPACE_DIM > *pNewNode)
virtual void SetNode(unsigned nodeIndex, ChastePoint< SPACE_DIM > point)
Definition Node.hpp:59
std::set< unsigned > & rGetContainingElementIndices()
Definition Node.cpp:300
c_vector< double, SPACE_DIM > & rGetModifiableLocation()
Definition Node.cpp:151
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
unsigned GetIndex() const
Definition Node.cpp:158
VertexElementIterator GetElementIteratorEnd()
TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > * mpDelaunayMesh
VertexElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
unsigned GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(unsigned elementIndex)
std::vector< VertexElement< ELEMENT_DIM, SPACE_DIM > * > mElements
void GenerateVerticesFromElementCircumcentres(TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)