Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
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 double node_clearance = 0.3; // Make settable variable??
175
176 if (!IsNearExistingNodes(new_node_location,nodes,node_clearance))
177 {
178 nodes.push_back(new Node<2>(new_node_index, new_node_location));
179 new_node_index++;
180 }
181 }
182 }
183 }
184 }
185 }
186
187 // Loop over all nodes and check they're not outside [0,mWidth]x[0,mHeight]
188 for (unsigned i=0; i<nodes.size(); i++)
189 {
190 CheckNodeLocation(nodes[i]);
191 }
192
193 Toroidal2dMesh extended_mesh(mpDelaunayMesh->GetWidth(0),mpDelaunayMesh->GetWidth(1), nodes);
194
195 unsigned num_elements = mpDelaunayMesh->GetNumAllNodes();
196 unsigned num_nodes = extended_mesh.GetNumAllElements();
197
198 // Allocate memory for mNodes and mElements
199 this->mNodes.reserve(num_nodes);
200
201 // Create as many elements as there are nodes in the mesh
202 mElements.reserve(num_elements);
203 for (unsigned elem_index = 0; elem_index < num_elements; elem_index++)
204 {
205 VertexElement<2, 2>* p_element = new VertexElement<2, 2>(elem_index);
206 mElements.push_back(p_element);
207 }
208
209 // Populate mNodes
211
212 // Loop over all generated nodes and check they're not outside [0,mWidth]x[0,mHeight]
213 for (unsigned i=0; i<num_nodes; i++)
214 {
216 }
217
218
219 // Loop over elements of the Delaunay mesh (which are nodes/vertices of this mesh)
220 for (unsigned i = 0; i < num_nodes; i++)
221 {
222 // Loop over nodes owned by this triangular element in the Delaunay mesh
223 // Add this node/vertex to each of the 3 vertex elements
224 for (unsigned local_index = 0; local_index < 3; local_index++)
225 {
226 unsigned elem_index = extended_mesh.GetElement(i)->GetNodeGlobalIndex(local_index);
227
228 if (elem_index < num_elements)
229 {
230 unsigned num_nodes_in_elem = mElements[elem_index]->GetNumNodes();
231 unsigned end_index = num_nodes_in_elem > 0 ? num_nodes_in_elem - 1 : 0;
232
233 mElements[elem_index]->AddNode(this->mNodes[i], end_index);
234 }
235 }
236 }
237 }
238
239 // Reorder mNodes anticlockwise
240 for (unsigned elem_index=0; elem_index<mElements.size(); elem_index++)
241 {
247 std::vector<std::pair<double, unsigned> > index_angle_list;
248 for (unsigned local_index=0; local_index<mElements[elem_index]->GetNumNodes(); local_index++)
249 {
250 c_vector<double, 2> vectorA = mpDelaunayMesh->GetNode(elem_index)->rGetLocation();
251 c_vector<double, 2> vectorB = mElements[elem_index]->GetNodeLocation(local_index);
252 c_vector<double, 2> centre_to_vertex = mpDelaunayMesh->GetVectorFromAtoB(vectorA, vectorB);
253
254 double angle = atan2(centre_to_vertex(1), centre_to_vertex(0));
255 unsigned global_index = mElements[elem_index]->GetNodeGlobalIndex(local_index);
256
257 std::pair<double, unsigned> pair(angle, global_index);
258 index_angle_list.push_back(pair);
259 }
260
261 // Sort the list in order of increasing angle
262 sort(index_angle_list.begin(), index_angle_list.end());
263
264 // Create a new Voronoi element and pass in the appropriate Nodes, ordered anticlockwise
265 VertexElement<2,2>* p_new_element = new VertexElement<2,2>(elem_index);
266 for (unsigned count = 0; count < index_angle_list.size(); count++)
267 {
268 unsigned local_index = count>1 ? count-1 : 0;
269 p_new_element->AddNode(mNodes[index_angle_list[count].second], local_index);
270 }
271
272 // Replace the relevant member of mElements with this Voronoi element
273 delete mElements[elem_index];
274 mElements[elem_index] = p_new_element;
275 }
276
277 this->mMeshChangesDuringSimulation = false;
278}
279
281 : mpMeshForVtk(nullptr)
282{
283}
284
286{
287 if (mpMeshForVtk != NULL)
288 {
289 delete mpMeshForVtk;
290 }
291}
292
293c_vector<double, 2> Toroidal2dVertexMesh::GetVectorFromAtoB(const c_vector<double, 2>& rLocation1, const c_vector<double, 2>& rLocation2)
294{
295 assert(mWidth > 0.0);
296 assert(mHeight > 0.0);
297
298 c_vector<double, 2> vector = rLocation2 - rLocation1;
299 vector[0] = fmod(vector[0], mWidth);
300 vector[1] = fmod(vector[1], mHeight);
301
302 // If the points are more than halfway across the domain, measure the other way
303 if (vector[0] > 0.5*mWidth)
304 {
305 vector[0] -= mWidth;
306 }
307 else if (vector[0] < -0.5*mWidth)
308 {
309 vector[0] += mWidth;
310 }
311
312 // If the points are more than halfway up the domain, measure the other way
313 if (vector[1] > 0.5*mHeight)
314 {
315 vector[1] -= mHeight;
316 }
317 else if (vector[1] < -0.5*mHeight)
318 {
319 vector[1] += mHeight;
320 }
321 return vector;
322}
323
324void Toroidal2dVertexMesh::SetNode(unsigned nodeIndex, ChastePoint<2> point)
325{
326 double x_coord = point.rGetLocation()[0];
327 double y_coord = point.rGetLocation()[1];
328
329 // Perform a periodic movement if necessary
330 if (x_coord >= mWidth)
331 {
332 // Move point left
333 point.SetCoordinate(0, x_coord - mWidth);
334 }
335 else if (x_coord < 0.0)
336 {
337 // Move point right
338 point.SetCoordinate(0, x_coord + mWidth);
339 }
340 if (y_coord >= mHeight)
341 {
342 // Move point down
343 point.SetCoordinate(1, y_coord - mHeight);
344 }
345 else if (y_coord < 0.0)
346 {
347 // Move point up
348 point.SetCoordinate(1, y_coord + mHeight);
349 }
350
351 // Update the node's location
352 MutableVertexMesh<2,2>::SetNode(nodeIndex, point);
353}
354
355double Toroidal2dVertexMesh::GetWidth(const unsigned& rDimension) const
356{
357 assert(rDimension==0 || rDimension==1);
358
359 double width = mWidth;
360 if (rDimension == 1)
361 {
362 width = mHeight;
363 }
364
365 return width;
366}
367
369{
370 assert(height > 0);
371 mHeight = height;
372}
373
375{
376 assert(width > 0);
377 mWidth = width;
378}
379
381{
382 // If necessary move it to be back onto the torus
383 CheckNodeLocation(pNewNode);
384
385 unsigned node_index = MutableVertexMesh<2,2>::AddNode(pNewNode);
386
387 return node_index;
388}
389
391{
392 double x_location = pNode->rGetLocation()[0];
393 if (x_location < 0)
394 {
395 pNode->rGetModifiableLocation()[0] = x_location + mWidth;
396 }
397 else if (x_location > mWidth)
398 {
399 pNode->rGetModifiableLocation()[0] = x_location - mWidth;
400 }
401 double y_location = pNode->rGetLocation()[1];
402 if (y_location < 0)
403 {
404 pNode->rGetModifiableLocation()[1] = y_location + mHeight;
405 }
406 else if (y_location > mHeight)
407 {
408 pNode->rGetModifiableLocation()[1] = y_location - mHeight;
409 }
410}
411
413{
414 unsigned num_nodes = GetNumNodes();
415
416 std::vector<Node<2>*> temp_nodes;
417 std::vector<VertexElement<2, 2>*> elements;
418
419 if(!mpDelaunayMesh) // No Delaunay mesh so less copies as all too top right
420 {
421 temp_nodes.resize(4 * num_nodes);
422
423 // Create four copies of each node.
424 for (unsigned index = 0; index < num_nodes; index++)
425 {
426 c_vector<double, 2> location;
427 location = GetNode(index)->rGetLocation();
428
429 // Node copy at original location
430 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
431 temp_nodes[index] = p_node;
432
433 // Node copy shifted right
434 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
435 temp_nodes[num_nodes + index] = p_node;
436
437 // Node copy shifted up
438 p_node = new Node<2>(2*num_nodes + index, false, location[0], location[1] + mHeight);
439 temp_nodes[2*num_nodes + index] = p_node;
440
441 // Node copy shifted right and up
442 p_node = new Node<2>(3*num_nodes + index, false, location[0] + mWidth, location[1] + mHeight);
443 temp_nodes[3*num_nodes + index] = p_node;
444 }
445
446 // Iterate over elements
448 elem_iter != GetElementIteratorEnd();
449 ++elem_iter)
450 {
451 unsigned elem_index = elem_iter->GetIndex();
452 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
453
454 std::vector<Node<2>*> elem_nodes;
455
456 // Compute whether the element straddles either periodic boundary
457 bool element_straddles_left_right_boundary = false;
458 bool element_straddles_top_bottom_boundary = false;
459
460 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
461 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
462 {
463 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
464 c_vector<double, 2> vector;
465 vector = r_next_node_location - r_this_node_location;
466
467 if (fabs(vector[0]) > 0.5*mWidth)
468 {
469 element_straddles_left_right_boundary = true;
470 }
471 if (fabs(vector[1]) > 0.5*mHeight)
472 {
473 element_straddles_top_bottom_boundary = true;
474 }
475 }
476
477 // Use the above information when duplicating the element
478 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
479 {
480 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
481
482 // If the element straddles the left/right periodic boundary...
483 if (element_straddles_left_right_boundary)
484 {
485 // ...and this node is located to the left of the centre of the mesh...
486 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
487 if (!node_is_right_of_centre)
488 {
489 // ...then choose the equivalent node to the right
490 this_node_index += num_nodes;
491 }
492 }
493
494 // If the element straddles the top/bottom periodic boundary...
495 if (element_straddles_top_bottom_boundary)
496 {
497 // ...and this node is located below the centre of the mesh...
498 bool node_is_above_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
499 if (!node_is_above_centre)
500 {
501 // ...then choose the equivalent node above
502 this_node_index += 2*num_nodes;
503 }
504 }
505
506 elem_nodes.push_back(temp_nodes[this_node_index]);
507 }
508
509 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
510 elements.push_back(p_element);
511 }
512 }
513 else // Has Delaunay mesh so match elements to centres
514 {
515 temp_nodes.resize(9*num_nodes);
516
517 // Create nine copies of each node.
518 for (unsigned index=0; index<num_nodes; index++)
519 {
520 c_vector<double, 2> location;
521 location = GetNode(index)->rGetLocation();
522
523 // Node copy at original location
524 Node<2>* p_node = new Node<2>(index, false, location[0], location[1]);
525 temp_nodes[index] = p_node;
526
527 // Node copy shifted right
528 p_node = new Node<2>(num_nodes + index, false, location[0] + mWidth, location[1]);
529 temp_nodes[num_nodes + index] = p_node;
530
531 // Node copy shifted up and right
532 p_node = new Node<2>(2*num_nodes + index, false, location[0] + mWidth, location[1] + mHeight);
533 temp_nodes[2*num_nodes + index] = p_node;
534
535 // Node copy shifted up
536 p_node = new Node<2>(3*num_nodes + index, false, location[0], location[1] + mHeight);
537 temp_nodes[3*num_nodes + index] = p_node;
538
539 // Node copy shifted up and left
540 p_node = new Node<2>(4*num_nodes + index, false, location[0] - mWidth, location[1] + mHeight);
541 temp_nodes[4*num_nodes + index] = p_node;
542
543 // Node copy shifted left
544 p_node = new Node<2>(5*num_nodes + index, false, location[0] - mWidth, location[1]);
545 temp_nodes[5*num_nodes + index] = p_node;
546
547 // Node copy shifted left and down
548 p_node = new Node<2>(6*num_nodes + index, false, location[0] - mWidth, location[1] - mHeight);
549 temp_nodes[6*num_nodes + index] = p_node;
550
551 // Node copy shifted down
552 p_node = new Node<2>(7*num_nodes + index, false, location[0], location[1] - mHeight);
553 temp_nodes[7*num_nodes + index] = p_node;
554
555 // Node copy shifted down and right
556 p_node = new Node<2>(8*num_nodes + index, false, location[0] + mWidth, location[1] - mHeight);
557 temp_nodes[8*num_nodes + index] = p_node;
558 }
559
560 // Iterate over elements
562 elem_iter != GetElementIteratorEnd();
563 ++elem_iter)
564 {
565 unsigned elem_index = elem_iter->GetIndex();
566 unsigned num_nodes_in_elem = elem_iter->GetNumNodes();
567
568 std::vector<Node<2>*> elem_nodes;
569
570 // Compute whether the element straddles either periodic boundary
571 bool element_straddles_left_right_boundary = false;
572 bool element_straddles_top_bottom_boundary = false;
573
574 const c_vector<double, 2>& r_this_node_location = elem_iter->GetNode(0)->rGetLocation();
575 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
576 {
577 const c_vector<double, 2>& r_next_node_location = elem_iter->GetNode((local_index+1)%num_nodes_in_elem)->rGetLocation();
578 c_vector<double, 2> vector;
579 vector = r_next_node_location - r_this_node_location;
580
581 if (fabs(vector[0]) > 0.5*mWidth)
582 {
583 element_straddles_left_right_boundary = true;
584 }
585 if (fabs(vector[1]) > 0.5*mHeight)
586 {
587 element_straddles_top_bottom_boundary = true;
588 }
589 }
590 /* If this is a voronoi tesselation make sure the elements contain
591 * the original Delaunay node
592 */
593 bool element_centre_on_right = true;
594 bool element_centre_on_top = true;
595
596 unsigned delaunay_index = this->GetDelaunayNodeIndexCorrespondingToVoronoiElementIndex(elem_index);
597 double element_centre_x_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[0];
598 double element_centre_y_location = this->mpDelaunayMesh->GetNode(delaunay_index)->rGetLocation()[1];
599
600 if (element_centre_x_location < 0.5*mWidth)
601 {
602 element_centre_on_right = false;
603 }
604 if (element_centre_y_location < 0.5*mHeight)
605 {
606 element_centre_on_top = false;
607 }
608
609 // Use the above information when duplicating the element
610 for (unsigned local_index=0; local_index<num_nodes_in_elem; local_index++)
611 {
612 unsigned this_node_index = elem_iter->GetNodeGlobalIndex(local_index);
613
614 // If the element straddles the left/right periodic boundary...
615 if (element_straddles_left_right_boundary && !element_straddles_top_bottom_boundary)
616 {
617 // ...and this node is located to the left of the centre of the mesh...
618 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
619 if (!node_is_right_of_centre && element_centre_on_right)
620 {
621 // ...then choose the equivalent node to the right
622 this_node_index += num_nodes;
623 }
624 else if (node_is_right_of_centre && !element_centre_on_right)
625 {
626 // ...then choose the equivalent node to the left
627 this_node_index += 5*num_nodes;
628 }
629 }
630 else if (!element_straddles_left_right_boundary && element_straddles_top_bottom_boundary)
631 {
632 // ...and this node is located to the bottom of the centre of the mesh...
633 bool node_is_top_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
634 if (!node_is_top_of_centre && element_centre_on_top)
635 {
636 // ...then choose the equivalent node to the top
637 this_node_index += 3*num_nodes;
638 }
639 else if (node_is_top_of_centre && !element_centre_on_top)
640 {
641 // ...then choose the equivalent node to the bottom
642 this_node_index += 7*num_nodes;
643 }
644 }
645 else if (element_straddles_left_right_boundary && element_straddles_top_bottom_boundary)
646 {
647 bool node_is_right_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[0] - 0.5*mWidth > 0);
648 bool node_is_top_of_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
649
650 if (!node_is_top_of_centre && element_centre_on_top)
651 {
652 if (!node_is_right_of_centre && element_centre_on_right)
653 {
654 this_node_index += 2*num_nodes;
655 }
656 else if (node_is_right_of_centre && !element_centre_on_right)
657 {
658 this_node_index += 4*num_nodes;
659 }
660 else
661 {
662 this_node_index += 3*num_nodes;
663 }
664 }
665 else if (node_is_top_of_centre && !element_centre_on_top)
666 {
667 if (!node_is_right_of_centre && element_centre_on_right)
668 {
669 this_node_index += 8*num_nodes;
670 }
671 else if (node_is_right_of_centre && !element_centre_on_right)
672 {
673 this_node_index += 6*num_nodes;
674 }
675 else
676 {
677 this_node_index += 7*num_nodes;
678 }
679 }
680 else
681 {
682 if (!node_is_right_of_centre && element_centre_on_right)
683 {
684 this_node_index += num_nodes;
685 }
686 else if (node_is_right_of_centre && !element_centre_on_right)
687 {
688 this_node_index += 5*num_nodes;
689 }
690 }
691 }
692
693 // If the element straddles the top/bottom periodic boundary...
694 // if (element_straddles_top_bottom_boundary)
695 // {
696 // // ...and this node is located below the centre of the mesh...
697 // bool node_is_above_centre = (elem_iter->GetNode(local_index)->rGetLocation()[1] - 0.5*mHeight > 0);
698 // if (!node_is_above_centre)
699 // {
700 // // ...then choose the equivalent node above
701 // this_node_index += 2*num_nodes;
702 // }
703 // }
704
705 elem_nodes.push_back(temp_nodes[this_node_index]);
706 }
707
708 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_index, elem_nodes);
709 elements.push_back(p_element);
710 }
711 }
712
713 // Now delete any nodes from the mesh for VTK that are not contained in any elements
714 std::vector<Node<2>*> nodes;
715 unsigned count = 0;
716 for (unsigned index=0; index<temp_nodes.size(); index++)
717 {
718 unsigned num_elems_containing_this_node = temp_nodes[index]->rGetContainingElementIndices().size();
719
720 if (num_elems_containing_this_node == 0)
721 {
722 // Avoid memory leak
723 delete temp_nodes[index];
724 }
725 else
726 {
727 temp_nodes[index]->SetIndex(count);
728 nodes.push_back(temp_nodes[index]);
729 count++;
730 }
731 }
732
733 if (mpMeshForVtk != nullptr)
734 {
735 delete mpMeshForVtk;
736 }
737
738 mpMeshForVtk = new VertexMesh<2,2>(nodes, elements);
739 return mpMeshForVtk;
740}
741
742void Toroidal2dVertexMesh::ConstructFromMeshReader(AbstractMeshReader<2,2>& rMeshReader, double width, double height)
743{
744 assert(rMeshReader.HasNodePermutation() == false);
745
746 // Store numbers of nodes and elements
747 unsigned num_nodes = rMeshReader.GetNumNodes();
748 unsigned num_elements = rMeshReader.GetNumElements();
749
750 // Reserve memory for nodes
751 this->mNodes.reserve(num_nodes);
752
753 rMeshReader.Reset();
754
755 // Add nodes
756 std::vector<double> node_data;
757 for (unsigned node_idx = 0 ; node_idx < num_nodes ; node_idx++)
758 {
759 node_data = rMeshReader.GetNextNode();
760 node_data.pop_back();
761 this->mNodes.push_back(new Node<2>(node_idx, node_data, false));
762 }
763
764 rMeshReader.Reset();
765
766 // Reserve memory for elements
767 mElements.reserve(rMeshReader.GetNumElements());
768
769 // Add elements
770 for (unsigned elem_idx = 0 ; elem_idx < num_elements ; elem_idx++)
771 {
772 // Get the data for this element
773 ElementData element_data = rMeshReader.GetNextElementData();
774
775 // Get the nodes owned by this element
776 std::vector<Node<2>*> nodes;
777 unsigned num_nodes_in_element = element_data.NodeIndices.size();
778 for (unsigned j=0; j<num_nodes_in_element; j++)
779 {
780 assert(element_data.NodeIndices[j] < this->mNodes.size());
781 nodes.push_back(this->mNodes[element_data.NodeIndices[j]]);
782 }
783
784 // Use nodes and index to construct this element
785 VertexElement<2,2>* p_element = new VertexElement<2,2>(elem_idx, nodes);
786 mElements.push_back(p_element);
787
788 if (rMeshReader.GetNumElementAttributes() > 0)
789 {
790 assert(rMeshReader.GetNumElementAttributes() == 1);
791 unsigned attribute_value = (unsigned) element_data.AttributeValue;
792 p_element->SetAttribute(attribute_value);
793 }
794 }
795
796 /*
797 * Set width and height from function arguments, and validate by checking area is correct
798 */
799 this->mWidth = width;
800 this->mHeight = height;
801
802 double total_surface_area = 0.0;
803 for (unsigned elem_idx = 0 ; elem_idx < num_elements ; elem_idx++)
804 {
805 total_surface_area += this->GetVolumeOfElement(elem_idx);
806 }
807
808 if (fabs(mWidth * mHeight - total_surface_area) > 1e-6)
809 {
810 EXCEPTION("Mesh width and height do not match sheet surface area.");
811 }
812
813 // Set default parameter values
814 this->mCellRearrangementRatio = 1.5;
815 this->mCellRearrangementThreshold = 0.01;
816 this->mT2Threshold = 0.001;
817 this->mMeshChangesDuringSimulation = true;
818 this->mpMeshForVtk = nullptr;
819}
820
821// 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
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
virtual double GetVolumeOfElement(unsigned index)
void GenerateVerticesFromElementCircumcentres(TetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh)
std::vector< unsigned > NodeIndices