Chaste Commit::f2ff7ee04e70ac9d06c57344df8d017dbb12b97b
Toroidal2dVertexMesh.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 "Toroidal2dVertexMesh.hpp"
37#include "Toroidal2dMesh.hpp"
38
40 double height,
41 std::vector<Node<2>*> nodes,
42 std::vector<VertexElement<2, 2>*> vertexElements,
43 double cellRearrangementThreshold,
44 double t2Threshold)
45 : MutableVertexMesh<2,2>(nodes, vertexElements, cellRearrangementThreshold, t2Threshold),
46 mWidth(width),
47 mHeight(height),
48 mpMeshForVtk(nullptr)
49{
50 // Call ReMesh() to remove any deleted nodes and relabel
51 ReMesh();
52}
53
55 : mWidth(rMesh.GetWidth(0)),
56 mHeight(rMesh.GetWidth(1)),
57 mpMeshForVtk(nullptr)
58{
59 mpDelaunayMesh = &rMesh;
60
61 // Reset member variables and clear mNodes, mFaces and mElements
62 Clear();
63
64 if (!isBounded)
65 {
66 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
67 unsigned num_nodes = mpDelaunayMesh->GetNumAllElements();
68
69 // Allocate memory for mNodes and mElements
70 this->mNodes.reserve(num_nodes);
71
72 // Create as many elements as there are nodes in the mesh
73 mElements.reserve(num_elements);
74
75 for (unsigned elem_index=0; elem_index<num_elements; elem_index++)
76 {
77 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index);
78 mElements.push_back(p_element);
79 }
80
81 // Populate mNodes
83
84 // Loop over all generated nodes and check they're not outside [0,mWidth]x[0,mHeight]
85 for (unsigned i=0; i<num_nodes; i++)
86 {
88 }
89
90 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
91 for (unsigned i=0; i<num_nodes; i++)
92 {
93 // Loop over nodes owned by this triangular element in the Delaunay mesh
94 // Add this node/vertex to each of the 3 vertex elements
95 for (unsigned local_index=0; local_index<3; local_index++)
96 {
97 unsigned elem_index = mpDelaunayMesh->GetElement(i)->GetNodeGlobalIndex(local_index);
98 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
99 unsigned end_index = num_nodes_in_elem>0 ? num_nodes_in_elem-1 : 0;
100
101 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
102 }
103 }
104 }
105 else // Is Bounded
106 {
107 // First create an extended mesh to include points extended from the boundary
108 std::vector<Node<2> *> nodes;
109 for (typename TetrahedralMesh<2,2>::NodeIterator node_iter = mpDelaunayMesh->GetNodeIteratorBegin();
110 node_iter != mpDelaunayMesh->GetNodeIteratorEnd();
111 ++node_iter)
112 {
113 nodes.push_back(new Node<2>(node_iter->GetIndex(), node_iter->rGetLocation(),node_iter->IsBoundaryNode()));
114 }
115
116 // Add new nodes
117 unsigned new_node_index = mpDelaunayMesh->GetNumNodes();
118 for (TetrahedralMesh<2,2>::ElementIterator elem_iter = mpDelaunayMesh->GetElementIteratorBegin();
119 elem_iter != mpDelaunayMesh->GetElementIteratorEnd();
120 ++elem_iter)
121 {
122 bool bad_element = false;
123 double edge_threshold = 1.5; //TODO Make settable variable!
124
125 for (unsigned j=0; j<3; j++)
126 {
127 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
128 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
129 if (norm_2(mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation()))>edge_threshold)
130 {
131 bad_element = true;
132 break;
133 }
134 }
135
136 if (bad_element)
137 {
138 for (unsigned j=0; j<3; j++)
139 {
140 Node<2>* p_node_a = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex(j));
141 Node<2>* p_node_b = mpDelaunayMesh->GetNode(elem_iter->GetNodeGlobalIndex((j+1)%3));
142
143 c_vector<double,2> edge = mpDelaunayMesh->GetVectorFromAtoB(p_node_a->rGetLocation(), p_node_b->rGetLocation());
144 double edge_length = norm_2(edge);
145
146 // The short edges in these elements are the boundaries of the void
147 if (edge_length<edge_threshold)
148 {
149 // Short Edge so add new node
150 c_vector<double,2> normal_vector;
151
152 // Outward Normal
153 normal_vector[0]= edge[1];
154 normal_vector[1]= -edge[0];
155
156 double dij = norm_2(normal_vector);
157 assert(dij>1e-5); //Sanity check
158 normal_vector /= dij;
159
160 double bound_offset = 0.5; //TODO Make settable variable!
161 c_vector<double,2> new_node_location = -bound_offset*normal_vector + p_node_a->rGetLocation() + 0.5*edge;
162
163 nodes.push_back(new Node<2>(new_node_index, new_node_location));
164 new_node_index++;
165
166 // Now add extra end nodes if appropriate.
167 unsigned num_sections = 1; // I.e. only at ends.
168 for (unsigned section=0; section <= num_sections; section++)
169 {
170 double ratio = (double)section/(double)num_sections;
171 c_vector<double,2> new_node_location = -normal_vector + ratio*p_node_a->rGetLocation() + (1-ratio)*p_node_b->rGetLocation();
172
173 //Check if near other nodes (could be inefficient)
174 bool node_clear = true;
175 double node_clearance = 0.3; // Make settable variable??
176
177 for (unsigned i=0; i<nodes.size(); i++)
178 {
179 double distance = norm_2(mpDelaunayMesh->GetVectorFromAtoB(nodes[i]->rGetLocation(), new_node_location));
180 if (distance < node_clearance)
181 {
182 node_clear = false;
183 break;
184 }
185 }
186
187 if (node_clear)
188 {
189 nodes.push_back(new Node<2>(new_node_index, new_node_location));
190 new_node_index++;
191 }
192 }
193 }
194 }
195 }
196 }
197
198 // Loop over all nodes and check they're not outside [0,mWidth]x[0,mHeight]
199 for (unsigned i=0; i<nodes.size(); i++)
200 {
201 CheckNodeLocation(nodes[i]);
202 }
203
204 Toroidal2dMesh extended_mesh(mpDelaunayMesh->GetWidth(0),mpDelaunayMesh->GetWidth(1), nodes);
205
206 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
207 unsigned num_nodes = extended_mesh.GetNumAllElements();
208
209 // Allocate memory for mNodes and mElements
210 this->mNodes.reserve(num_nodes);
211
212 // Create as many elements as there are nodes in the mesh
213 mElements.reserve(num_elements);
214 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
215 {
216 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
217 mElements.push_back(p_element);
218 }
219
220 // Populate mNodes
222
223 // Loop over all generated nodes and check they're not outside [0,mWidth]x[0,mHeight]
224 for (unsigned i=0; i<num_nodes; i++)
225 {
227 }
228
229
230 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
231 for (unsigned i = 0; i < num_nodes; i++)
232 {
233 // Loop over nodes owned by this triangular element in the Delaunay mesh
234 // Add this node/vertex to each of the 3 vertex elements
235 for (unsigned local_index = 0; local_index < 3; local_index++)
236 {
237 unsigned elem_index = extended_mesh.GetElement(i)->GetNodeGlobalIndex(local_index);
238
239 if (elem_index < num_elements)
240 {
241 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
242 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
243
244 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
245 }
246 }
247 }
248 }
249
250 // Reorder mNodes anticlockwise
251 for (unsigned elem_index=0; elem_index<mElements.size(); elem_index++)
252 {
258 std::vector<std::pair<double, unsigned> > index_angle_list;
259 for (unsigned local_index=0; local_index<mElements[elem_index]->GetNumNodes(); local_index++)
260 {
261 c_vector<double, 2> vectorA = mpDelaunayMesh->GetNode(elem_index)->rGetLocation();
262 c_vector<double, 2> vectorB = mElements[elem_index]->GetNodeLocation(local_index);
263 c_vector<double, 2> centre_to_vertex = mpDelaunayMesh->GetVectorFromAtoB(vectorA, vectorB);
264
265 double angle = atan2(centre_to_vertex(1), centre_to_vertex(0));
266 unsigned global_index = mElements[elem_index]->GetNodeGlobalIndex(local_index);
267
268 std::pair<double, unsigned> pair(angle, global_index);
269 index_angle_list.push_back(pair);
270 }
271
272 // Sort the list in order of increasing angle
273 sort(index_angle_list.begin(), index_angle_list.end());
274
275 // Create a new Voronoi element and pass in the appropriate Nodes, ordered anticlockwise
276 VertexElement<2,2>* p_new_element = new VertexElement<2,2>(elem_index);
277 for (unsigned count = 0; count < index_angle_list.size(); count++)
278 {
279 unsigned local_index = count>1 ? count-1 : 0;
280 p_new_element->AddNode(mNodes[index_angle_list[count].second], local_index);
281 }
282
283 // Replace the relevant member of mElements with this Voronoi element
284 delete mElements[elem_index];
285 mElements[elem_index] = p_new_element;
286 }
287
288 this->mMeshChangesDuringSimulation = false;
289}
290
292 : mpMeshForVtk(nullptr)
293{
294}
295
297{
298 if (mpMeshForVtk != NULL)
299 {
300 delete mpMeshForVtk;
301 }
302}
303
304c_vector<double, 2> Toroidal2dVertexMesh::GetVectorFromAtoB(const c_vector<double, 2>& rLocation1, const c_vector<double, 2>& rLocation2)
305{
306 assert(mWidth > 0.0);
307 assert(mHeight > 0.0);
308
309 c_vector<double, 2> vector = rLocation2 - rLocation1;
310 vector[0] = fmod(vector[0], mWidth);
311 vector[1] = fmod(vector[1], mHeight);
312
313 // If the points are more than halfway across the domain, measure the other way
314 if (vector[0] > 0.5*mWidth)
315 {
316 vector[0] -= mWidth;
317 }
318 else if (vector[0] < -0.5*mWidth)
319 {
320 vector[0] += mWidth;
321 }
322
323 // If the points are more than halfway up the domain, measure the other way
324 if (vector[1] > 0.5*mHeight)
325 {
326 vector[1] -= mHeight;
327 }
328 else if (vector[1] < -0.5*mHeight)
329 {
330 vector[1] += mHeight;
331 }
332 return vector;
333}
334
335void Toroidal2dVertexMesh::SetNode(unsigned nodeIndex, ChastePoint<2> point)
336{
337 double x_coord = point.rGetLocation()[0];
338 double y_coord = point.rGetLocation()[1];
339
340 // Perform a periodic movement if necessary
341 if (x_coord >= mWidth)
342 {
343 // Move point left
344 point.SetCoordinate(0, x_coord - mWidth);
345 }
346 else if (x_coord < 0.0)
347 {
348 // Move point right
349 point.SetCoordinate(0, x_coord + mWidth);
350 }
351 if (y_coord >= mHeight)
352 {
353 // Move point down
354 point.SetCoordinate(1, y_coord - mHeight);
355 }
356 else if (y_coord < 0.0)
357 {
358 // Move point up
359 point.SetCoordinate(1, y_coord + mHeight);
360 }
361
362 // Update the node's location
363 MutableVertexMesh<2,2>::SetNode(nodeIndex, point);
364}
365
366double Toroidal2dVertexMesh::GetWidth(const unsigned& rDimension) const
367{
368 assert(rDimension==0 || rDimension==1);
369
370 double width = mWidth;
371 if (rDimension == 1)
372 {
373 width = mHeight;
374 }
375
376 return width;
377}
378
380{
381 assert(height > 0);
382 mHeight = height;
383}
384
386{
387 assert(width > 0);
388 mWidth = width;
389}
390
392{
393 // If necessary move it to be back onto the torus
394 CheckNodeLocation(pNewNode);
395
396 unsigned node_index = MutableVertexMesh<2,2>::AddNode(pNewNode);
397
398 return node_index;
399}
400
402{
403 double x_location = pNode->rGetLocation()[0];
404 if (x_location < 0)
405 {
406 pNode->rGetModifiableLocation()[0] = x_location + mWidth;
407 }
408 else if (x_location > mWidth)
409 {
410 pNode->rGetModifiableLocation()[0] = x_location - mWidth;
411 }
412 double y_location = pNode->rGetLocation()[1];
413 if (y_location < 0)
414 {
415 pNode->rGetModifiableLocation()[1] = y_location + mHeight;
416 }
417 else if (y_location > mHeight)
418 {
419 pNode->rGetModifiableLocation()[1] = y_location - mHeight;
420 }
421}
422
424{
425 unsigned num_nodes = GetNumNodes();
426
427 std::vector<Node<2>*> temp_nodes;
428 std::vector<VertexElement<2, 2>*> elements;
429
430 if(!mpDelaunayMesh) // No Delaunay mesh so less copies as all too top right
431 {
432 temp_nodes.resize(4 * num_nodes);
433
434 // Create four copies of each node.
435 for (unsigned index = 0; index < num_nodes; index++)
436 {
437 c_vector<double, 2> location;
438 location = GetNode(index)->rGetLocation();
439
440 // Node copy at original location
441 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
442 temp_nodes[index] = p_node;
443
444 // Node copy shifted right
445 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
446 temp_nodes[num_nodes + index] = p_node;
447
448 // Node copy shifted up
449 p_node = new Node<2>(2*num_nodes + index, false, location[0], location[1] + mHeight);
450 temp_nodes[2*num_nodes + index] = p_node;
451
452 // Node copy shifted right and up
453 p_node = new Node<2>(3*num_nodes + index, false, location[0] + mWidth, location[1] + mHeight);
454 temp_nodes[3*num_nodes + index] = p_node;
455 }
456
457 // Iterate over elements
459 elem_iter != GetElementIteratorEnd();
460 ++elem_iter)
461 {
462 unsigned elem_index = elem_iter->GetIndex();
463 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
464
465 std::vector<Node<2>*> elem_nodes;
466
467 // Compute whether the element straddles either periodic boundary
468 bool element_straddles_left_right_boundary = false;
469 bool element_straddles_top_bottom_boundary = false;
470
471 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
472 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
473 {
474 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
475 c_vector<double, 2> vector;
476 vector = r_next_node_location - r_this_node_location;
477
478 if (fabs(vector[0]) > 0.5*mWidth)
479 {
480 element_straddles_left_right_boundary = true;
481 }
482 if (fabs(vector[1]) > 0.5*mHeight)
483 {
484 element_straddles_top_bottom_boundary = true;
485 }
486 }
487
488 // Use the above information when duplicating the element
489 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
490 {
491 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
492
493 // If the element straddles the left/right periodic boundary...
494 if (element_straddles_left_right_boundary)
495 {
496 // ...and this node is located to the left of the centre of the mesh...
497 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
498 if (!node_is_right_of_centre)
499 {
500 // ...then choose the equivalent node to the right
501 this_node_index += num_nodes;
502 }
503 }
504
505 // If the element straddles the top/bottom periodic boundary...
506 if (element_straddles_top_bottom_boundary)
507 {
508 // ...and this node is located below the centre of the mesh...
509 bool node_is_above_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
510 if (!node_is_above_centre)
511 {
512 // ...then choose the equivalent node above
513 this_node_index += 2*num_nodes;
514 }
515 }
516
517 elem_nodes.push_back(temp_nodes[this_node_index]);
518 }
519
520 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
521 elements.push_back(p_element);
522 }
523 }
524 else // Has Delaunay mesh so match elements to centres
525 {
526 temp_nodes.resize(9*num_nodes);
527
528 // Create nine copies of each node.
529 for (unsigned index=0; index<num_nodes; index++)
530 {
531 c_vector<double, 2> location;
532 location = GetNode(index)->rGetLocation();
533
534 // Node copy at original location
535 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
536 temp_nodes[index] = p_node;
537
538 // Node copy shifted right
539 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
540 temp_nodes[num_nodes + index] = p_node;
541
542 // Node copy shifted up and right
543 p_node = new Node<2>(2*num_nodes + index, false, location[0] + mWidth, location[1] + mHeight);
544 temp_nodes[2*num_nodes + index] = p_node;
545
546 // Node copy shifted up
547 p_node = new Node<2>(3*num_nodes + index, false, location[0], location[1] + mHeight);
548 temp_nodes[3*num_nodes + index] = p_node;
549
550 // Node copy shifted up and left
551 p_node = new Node<2>(4*num_nodes + index, false, location[0] - mWidth, location[1] + mHeight);
552 temp_nodes[4*num_nodes + index] = p_node;
553
554 // Node copy shifted left
555 p_node = new Node<2>(5*num_nodes + index, false, location[0] - mWidth, location[1]);
556 temp_nodes[5*num_nodes + index] = p_node;
557
558 // Node copy shifted left and down
559 p_node = new Node<2>(6*num_nodes + index, false, location[0] - mWidth, location[1] - mHeight);
560 temp_nodes[6*num_nodes + index] = p_node;
561
562 // Node copy shifted down
563 p_node = new Node<2>(7*num_nodes + index, false, location[0], location[1] - mHeight);
564 temp_nodes[7*num_nodes + index] = p_node;
565
566 // Node copy shifted down and right
567 p_node = new Node<2>(8*num_nodes + index, false, location[0] + mWidth, location[1] - mHeight);
568 temp_nodes[8*num_nodes + index] = p_node;
569 }
570
571 // Iterate over elements
573 elem_iter != GetElementIteratorEnd();
574 ++elem_iter)
575 {
576 unsigned elem_index = elem_iter->GetIndex();
577 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
578
579 std::vector<Node<2>*> elem_nodes;
580
581 // Compute whether the element straddles either periodic boundary
582 bool element_straddles_left_right_boundary = false;
583 bool element_straddles_top_bottom_boundary = false;
584
585 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
586 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
587 {
588 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
589 c_vector<double, 2> vector;
590 vector = r_next_node_location - r_this_node_location;
591
592 if (fabs(vector[0]) > 0.5*mWidth)
593 {
594 element_straddles_left_right_boundary = true;
595 }
596 if (fabs(vector[1]) > 0.5*mHeight)
597 {
598 element_straddles_top_bottom_boundary = true;
599 }
600 }
601 /* If this is a voronoi tesselation make sure the elements contain
602 * the original Delaunay node
603 */
604 bool element_centre_on_right = true;
605 bool element_centre_on_top = true;
606
607 unsigned delaunay_index = this->GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(elem_index);
608 double element_centre_x_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[0];
609 double element_centre_y_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[1];
610
611 if (element_centre_x_location < 0.5*mWidth)
612 {
613 element_centre_on_right = false;
614 }
615 if (element_centre_y_location < 0.5*mHeight)
616 {
617 element_centre_on_top = false;
618 }
619
620 // Use the above information when duplicating the element
621 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
622 {
623 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
624
625 // If the element straddles the left/right periodic boundary...
626 if (element_straddles_left_right_boundary && !element_straddles_top_bottom_boundary)
627 {
628 // ...and this node is located to the left of the centre of the mesh...
629 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
630 if (!node_is_right_of_centre && element_centre_on_right)
631 {
632 // ...then choose the equivalent node to the right
633 this_node_index += num_nodes;
634 }
635 else if (node_is_right_of_centre && !element_centre_on_right)
636 {
637 // ...then choose the equivalent node to the left
638 this_node_index += 5*num_nodes;
639 }
640 }
641 else if (!element_straddles_left_right_boundary && element_straddles_top_bottom_boundary)
642 {
643 // ...and this node is located to the bottom of the centre of the mesh...
644 bool node_is_top_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
645 if (!node_is_top_of_centre && element_centre_on_top)
646 {
647 // ...then choose the equivalent node to the top
648 this_node_index += 3*num_nodes;
649 }
650 else if (node_is_top_of_centre && !element_centre_on_top)
651 {
652 // ...then choose the equivalent node to the bottom
653 this_node_index += 7*num_nodes;
654 }
655 }
656 else if (element_straddles_left_right_boundary && element_straddles_top_bottom_boundary)
657 {
658 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
659 bool node_is_top_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
660
661 if (!node_is_top_of_centre && element_centre_on_top)
662 {
663 if (!node_is_right_of_centre && element_centre_on_right)
664 {
665 this_node_index += 2*num_nodes;
666 }
667 else if (node_is_right_of_centre && !element_centre_on_right)
668 {
669 this_node_index += 4*num_nodes;
670 }
671 else
672 {
673 this_node_index += 3*num_nodes;
674 }
675 }
676 else if (node_is_top_of_centre && !element_centre_on_top)
677 {
678 if (!node_is_right_of_centre && element_centre_on_right)
679 {
680 this_node_index += 8*num_nodes;
681 }
682 else if (node_is_right_of_centre && !element_centre_on_right)
683 {
684 this_node_index += 6*num_nodes;
685 }
686 else
687 {
688 this_node_index += 7*num_nodes;
689 }
690 }
691 else
692 {
693 if (!node_is_right_of_centre && element_centre_on_right)
694 {
695 this_node_index += num_nodes;
696 }
697 else if (node_is_right_of_centre && !element_centre_on_right)
698 {
699 this_node_index += 5*num_nodes;
700 }
701 }
702 }
703
704 // If the element straddles the top/bottom periodic boundary...
705 // if (element_straddles_top_bottom_boundary)
706 // {
707 // // ...and this node is located below the centre of the mesh...
708 // bool node_is_above_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
709 // if (!node_is_above_centre)
710 // {
711 // // ...then choose the equivalent node above
712 // this_node_index += 2*num_nodes;
713 // }
714 // }
715
716 elem_nodes.push_back(temp_nodes[this_node_index]);
717 }
718
719 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
720 elements.push_back(p_element);
721 }
722 }
723
724 // Now delete any nodes from the mesh for VTK that are not contained in any elements
725 std::vector<Node<2>*> nodes;
726 unsigned count = 0;
727 for (unsigned index=0; index<temp_nodes.size(); index++)
728 {
729 unsigned num_elems_containing_this_node = temp_nodes[index]->rGetContainingElementIndices().size();
730
731 if (num_elems_containing_this_node == 0)
732 {
733 // Avoid memory leak
734 delete temp_nodes[index];
735 }
736 else
737 {
738 temp_nodes[index]->SetIndex(count);
739 nodes.push_back(temp_nodes[index]);
740 count++;
741 }
742 }
743
744 if (mpMeshForVtk != nullptr)
745 {
746 delete mpMeshForVtk;
747 }
748
749 mpMeshForVtk = new VertexMesh<2,2>(nodes, elements);
750 return mpMeshForVtk;
751}
752
753void Toroidal2dVertexMesh::ConstructFromMeshReader(AbstractMeshReader<2,2>& rMeshReader, double width, double height)
754{
755 assert(rMeshReader.HasNodePermutation() == false);
756
757 // Store numbers of nodes and elements
758 unsigned num_nodes = rMeshReader.GetNumNodes();
759 unsigned num_elements = rMeshReader.GetNumElements();
760
761 // Reserve memory for nodes
762 this->mNodes.reserve(num_nodes);
763
764 rMeshReader.Reset();
765
766 // Add nodes
767 std::vector<double> node_data;
768 for (unsigned node_idx = 0 ; node_idx < num_nodes ; node_idx++)
769 {
770 node_data = rMeshReader.GetNextNode();
771 node_data.pop_back();
772 this->mNodes.push_back(new Node<2>(node_idx, node_data, false));
773 }
774
775 rMeshReader.Reset();
776
777 // Reserve memory for elements
778 mElements.reserve(rMeshReader.GetNumElements());
779
780 // Add elements
781 for (unsigned elem_idx = 0 ; elem_idx < num_elements ; elem_idx++)
782 {
783 // Get the data for this element
784 ElementData element_data = rMeshReader.GetNextElementData();
785
786 // Get the nodes owned by this element
787 std::vector<Node<2>*> nodes;
788 unsigned num_nodes_in_element = element_data.NodeIndices.size();
789 for (unsigned j=0; j<num_nodes_in_element; j++)
790 {
791 assert(element_data.NodeIndices[j] < this->mNodes.size());
792 nodes.push_back(this->mNodes[element_data.NodeIndices[j]]);
793 }
794
795 // Use nodes and index to construct this element
796 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_idx, nodes);
797 mElements.push_back(p_element);
798
799 if (rMeshReader.GetNumElementAttributes() > 0)
800 {
801 assert(rMeshReader.GetNumElementAttributes() == 1);
802 unsigned attribute_value = (unsigned) element_data.AttributeValue;
803 p_element->SetAttribute(attribute_value);
804 }
805 }
806
807 /*
808 * Set width and height from function arguments, and validate by checking area is correct
809 */
810 this->mWidth = width;
811 this->mHeight = height;
812
813 double total_surface_area = 0.0;
814 for (unsigned elem_idx = 0 ; elem_idx < num_elements ; elem_idx++)
815 {
816 total_surface_area += this->GetVolumeOfElement(elem_idx);
817 }
818
819 if (fabs(mWidth * mHeight - total_surface_area) > 1e-6)
820 {
821 EXCEPTION("Mesh width and height do not match sheet surface area.");
822 }
823
824 // Set default parameter values
825 this->mCellRearrangementRatio = 1.5;
826 this->mCellRearrangementThreshold = 0.01;
827 this->mT2Threshold = 0.001;
828 this->mMeshChangesDuringSimulation = true;
829 this->mpMeshForVtk = nullptr;
830}
831
832// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define CHASTE_CLASS_EXPORT(T)
void SetAttribute(double attribute)
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
Node< SPACE_DIM > * GetNode(unsigned index) const
std::vector< Node< SPACE_DIM > * > mNodes
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
c_vector< double, DIM > & rGetLocation()
void SetCoordinate(unsigned i, double value)
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
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
unsigned AddNode(Node< 2 > *pNewNode)
VertexMesh< 2, 2 > * GetMeshForVtk()
VertexMesh< 2, 2 > * mpMeshForVtk
double GetWidth(const unsigned &rDimension) const
void ConstructFromMeshReader(AbstractMeshReader< 2, 2 > &rMeshReader, double width, double height)
void CheckNodeLocation(Node< 2 > *pNode)
void SetHeight(double height)
void SetNode(unsigned nodeIndex, ChastePoint< 2 > point)
c_vector< double, 2 > GetVectorFromAtoB(const c_vector< double, 2 > &rLocation1, const c_vector< double, 2 > &rLocation2)
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
virtual double GetVolumeOfElement(unsigned index)
void GenerateVerticesFromElementCircumcentres(TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
std::vector< unsigned > NodeIndices