Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
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 double node_clearance = 0.01;
170
171 if (!IsNearExistingNodes(new_node_location,nodes,node_clearance))
172 {
173 nodes.push_back(new Node<2>(new_node_index, new_node_location));
174 new_node_index++;
175 }
176 }
177 }
178 }
179
180 // Add new nodes for voids (note these nodes won't be labeled as boundary nodes here)
181 for (TetrahedralMesh<2,2>::ElementIterator elem_iter = mpDelaunayMesh->GetElementIteratorBegin();
182 elem_iter != mpDelaunayMesh->GetElementIteratorEnd();
183 ++elem_iter)
184 {
185 bool bad_element = false;
186 double edge_threshold = 1.5; //TODO think about making this a setable variable!
187
188 for (unsigned j=0; j<3; j++)
189 {
190 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
191 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
192 if (norm_2(mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation()))>edge_threshold)
193 {
194 bad_element = true;
195 break;
196 }
197 }
198
199 if (bad_element)
200 {
201 for (unsigned j=0; j<3; j++)
202 {
203 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
204 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
205
206 c_vector<double,2> edge = mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation());
207 double edge_length = norm_2(edge);
208
209 // The short edges in these elements are the boundaries of the void
210 if (edge_length<edge_threshold)
211 {
212 /*
213 * Here we only add one extra node.
214 *
215 * ____ one extra image node
216 * /\
217 * /__\ edge of mesh
218 *
219 * Changing this can cause issues with stitching the left and right back together.
220 */
221
222 // Short Edge so add new node
223 c_vector<double,2> normal_vector;
224
225 // Outward Normal
226 normal_vector[0]= edge[1];
227 normal_vector[1]= -edge[0];
228
229 double dij = norm_2(normal_vector);
230 assert(dij>1e-5); //Sanity check
231 normal_vector /= dij;
232
233 double bound_offset = 1.0; //TODO Think about makeing this a setable variable!
234 c_vector<double,2> new_node_location = -bound_offset*normal_vector + p_node_a->rGetLocation() + 0.5 * edge;
235
236 nodes.push_back(new Node<2>(new_node_index, new_node_location));
237 new_node_index++;
238 }
239 }
240 }
241 }
242
243 // Loop over all nodes and check they're not outside [0,mWidth)
244 for (unsigned i=0; i<nodes.size(); i++)
245 {
246 CheckNodeLocation(nodes[i]);
247 }
248
249 Cylindrical2dMesh extended_mesh(mpDelaunayMesh->GetWidth(0),nodes);
250
251 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
252 unsigned num_nodes = extended_mesh.GetNumAllElements();
253
254 // Allocate memory for mNodes and mElements
255 this->mNodes.reserve(num_nodes);
256
257 // Create as many elements as there are nodes in the mesh
258 mElements.reserve(num_elements);
259 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
260 {
261 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
262 mElements.push_back(p_element);
263 }
264
265 // Populate mNodes
267
268 // Loop over all nodes and check the x locations not outside [0,mWidth]
269 for (unsigned i=0; i<mNodes.size(); i++)
270 {
272 }
273 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
274 for (unsigned i = 0; i < num_nodes; i++)
275 {
276 // Loop over nodes owned by this triangular element in the Delaunay mesh
277 // Add this node/vertex to each of the 3 vertex elements
278 for (unsigned local_index = 0; local_index < 3; local_index++)
279 {
280 unsigned elem_index = extended_mesh.GetElement(i)->GetNodeGlobalIndex(local_index);
281
282 if (elem_index < num_elements)
283 {
284 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
285 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
286
287 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
288 }
289 }
290 }
291 }
292
293 // Reorder mNodes anticlockwise
294 for (unsigned elem_index=0; elem_index<mElements.size(); elem_index++)
295 {
301 std::vector<std::pair<double, unsigned> > index_angle_list;
302 for (unsigned local_index=0; local_index<mElements[elem_index]->GetNumNodes(); local_index++)
303 {
304 c_vector<double, 2> vectorA = mpDelaunayMesh->GetNode(elem_index)->rGetLocation();
305 c_vector<double, 2> vectorB = mElements[elem_index]->GetNodeLocation(local_index);
306 c_vector<double, 2> centre_to_vertex = mpDelaunayMesh->GetVectorFromAtoB(vectorA, vectorB);
307
308 double angle = atan2(centre_to_vertex(1), centre_to_vertex(0));
309 unsigned global_index = mElements[elem_index]->GetNodeGlobalIndex(local_index);
310
311 std::pair<double, unsigned> pair(angle, global_index);
312 index_angle_list.push_back(pair);
313 }
314
315 // Sort the list in order of increasing angle
316 sort(index_angle_list.begin(), index_angle_list.end());
317
318 // Create a new Voronoi element and pass in the appropriate Nodes, ordered anticlockwise
319 VertexElement<2,2>* p_new_element = new VertexElement<2,2>(elem_index);
320 for (unsigned count = 0; count < index_angle_list.size(); count++)
321 {
322 unsigned local_index = count>1 ? count-1 : 0;
323 p_new_element->AddNode(mNodes[index_angle_list[count].second], local_index);
324 }
325
326 // Replace the relevant member of mElements with this Voronoi element
327 delete mElements[elem_index];
328 mElements[elem_index] = p_new_element;
329 }
330
331 this->mMeshChangesDuringSimulation = false;
332}
333
335 : mpMeshForVtk(nullptr)
336{
337}
338
340{
341 if (mpMeshForVtk != nullptr)
342 {
343 delete mpMeshForVtk;
344 }
345}
346
347c_vector<double, 2> Cylindrical2dVertexMesh::GetVectorFromAtoB(const c_vector<double, 2>& rLocation1, const c_vector<double, 2>& rLocation2)
348{
349 assert(mWidth > 0.0);
350
351 c_vector<double, 2> vector = rLocation2 - rLocation1;
352 vector[0] = fmod(vector[0], mWidth);
353
354 // If the points are more than halfway around the cylinder apart, measure the other way
355 if (vector[0] > 0.5 * mWidth)
356 {
357 vector[0] -= mWidth;
358 }
359 else if (vector[0] < -0.5 * mWidth)
360 {
361 vector[0] += mWidth;
362 }
363 return vector;
364}
365
367{
368 double x_coord = point.rGetLocation()[0];
369
370 // Perform a periodic movement if necessary
371 if (x_coord >= mWidth)
372 {
373 // Move point to the left
374 point.SetCoordinate(0, x_coord - mWidth);
375 }
376 else if (x_coord < 0.0)
377 {
378 // Move point to the right
379 point.SetCoordinate(0, x_coord + mWidth);
380 }
381
382 // Update the node's location
383 MutableVertexMesh<2,2>::SetNode(nodeIndex, point);
384}
385
386double Cylindrical2dVertexMesh::GetWidth(const unsigned& rDimension) const
387{
388 double width = 0.0;
389 assert(rDimension==0 || rDimension==1);
390 if (rDimension==0)
391 {
392 width = mWidth;
393 }
394 else
395 {
396 width = VertexMesh<2,2>::GetWidth(rDimension);
397 }
398 return width;
399}
400
402{
403 CheckNodeLocation(pNewNode);
404
405 unsigned node_index = MutableVertexMesh<2,2>::AddNode(pNewNode);
406
407 return node_index;
408}
409
411{
412 double x_location = pNode->rGetLocation()[0];
413 if (x_location < 0)
414 {
415 pNode->rGetModifiableLocation()[0] = x_location + mWidth;
416 }
417 else if (x_location >= mWidth)
418 {
419 pNode->rGetModifiableLocation()[0] = x_location - mWidth;
420 }
421}
422
423void Cylindrical2dVertexMesh::Scale(const double xScale, const double yScale, const double zScale)
424{
425 assert(zScale == 1.0);
426
427 AbstractMesh<2, 2>::Scale(xScale, yScale);
428
429 // Also rescale the width of the mesh (this effectively scales the domain)
430 mWidth *= xScale;
431}
432
434{
435 unsigned num_nodes = GetNumNodes();
436
437 std::vector<Node<2>*> temp_nodes(3 * num_nodes);
438 std::vector<VertexElement<2, 2>*> elements;
439
440 // Create three copies of each node
441 for (unsigned index=0; index<num_nodes; index++)
442 {
443 c_vector<double, 2> location;
444 location = GetNode(index)->rGetLocation();
445
446 // Node copy at original location
447 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
448 temp_nodes[index] = p_node;
449
450 // Node copy shifted right
451 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
452 temp_nodes[num_nodes + index] = p_node;
453
454 // Node copy shifted left
455 p_node = new Node<2>(2 * num_nodes + index, false, location[0] - mWidth, location[1]);
456 temp_nodes[2*num_nodes + index] = p_node;
457 }
458
459 // Iterate over elements
461 elem_iter != GetElementIteratorEnd();
462 ++elem_iter)
463 {
464 unsigned elem_index = elem_iter->GetIndex();
465 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
466
467 std::vector<Node<2>*> elem_nodes;
468
469 // Compute whether the element straddles either periodic boundary
470 bool element_straddles_left_right_boundary = false;
471
472 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
473 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
474 {
475 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
476 c_vector<double, 2> vector;
477 vector = r_next_node_location - r_this_node_location;
478
479 if (fabs(vector[0]) > 0.5 * mWidth)
480 {
481 element_straddles_left_right_boundary = true;
482 }
483 }
484
485 /* If this is a voronoi tesselation make sure the elements contain
486 * the original Delaunay node
487 */
488 bool element_centre_on_right = true;
490 {
491 unsigned delaunay_index = this->GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(elem_index);
492 double element_centre_x_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[0];
493 if (element_centre_x_location < 0.5 * mWidth)
494 {
495 element_centre_on_right = false;
496 }
497 }
498
499 // Use the above information when duplicating the element in the vtk mesh
500 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
501 {
502 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
503
504 // If the element straddles the left/right periodic boundary...
505 if (element_straddles_left_right_boundary)
506 {
507 // ...and this node is located to the left of the centre of the mesh...
508 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5 * mWidth > 0);
509 if (!node_is_right_of_centre && element_centre_on_right)
510 {
511 // ...then choose the equivalent node to the right
512 this_node_index += num_nodes;
513 }
514 else if (node_is_right_of_centre && !element_centre_on_right)
515 {
516 // ...then choose the equivalent node to the left
517 this_node_index += 2 * num_nodes;
518 }
519 }
520
521 elem_nodes.push_back(temp_nodes[this_node_index]);
522 }
523
524 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
525 elements.push_back(p_element);
526 }
527
528 // Now delete any nodes from the mesh for VTK that are not contained in any elements
529 std::vector<Node<2>*> nodes;
530 unsigned count = 0;
531 for (unsigned index=0; index<temp_nodes.size(); index++)
532 {
533 unsigned num_elems_containing_this_node = temp_nodes[index]->rGetContainingElementIndices().size();
534
535 if (num_elems_containing_this_node == 0)
536 {
537 // Avoid memory leak
538 delete temp_nodes[index];
539 }
540 else
541 {
542 temp_nodes[index]->SetIndex(count);
543 nodes.push_back(temp_nodes[index]);
544 count++;
545 }
546 }
547
548 if (mpMeshForVtk != nullptr)
549 {
550 delete mpMeshForVtk;
551 }
552
553 mpMeshForVtk = new VertexMesh<2,2>(nodes, elements);
554 return mpMeshForVtk;
555}
556
557// 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
bool IsNearExistingNodes(c_vector< double, SPACE_DIM > newNodeLocation, std::vector< Node< SPACE_DIM > * > nodesToCheck, double minClearance)
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)