Chaste Commit::f841a6fa79bd6f7a205054452b95ddf6d10aae23
MutableMesh.cpp
1/*
2
3Copyright (c) 2005-2026, 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 <map>
37#include <cstring>
38
39#include "MutableMesh.hpp"
40#include "OutputFileHandler.hpp"
41
42//Jonathan Shewchuk's triangle and Hang Si's tetgen
43#define REAL double
44#define VOID void
45#include "triangle.h"
46#include "tetgen.h"
47#undef REAL
48#undef VOID
49
50
51template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
53 : mAddedNodes(false)
54{
56}
57
58template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
60{
61 this->mMeshChangesDuringSimulation = true;
62 Clear();
63 for (unsigned index=0; index<nodes.size(); index++)
64 {
65 Node<SPACE_DIM>* p_temp_node = nodes[index];
66 this->mNodes.push_back(p_temp_node);
67 }
68 mAddedNodes = true;
69 NodeMap node_map(nodes.size());
70 ReMesh(node_map);
71}
72
73template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
78
79template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
81{
82 if (mDeletedNodeIndices.empty())
83 {
84 pNewNode->SetIndex(this->mNodes.size());
85 this->mNodes.push_back(pNewNode);
86 }
87 else
88 {
89 unsigned index = mDeletedNodeIndices.back();
90 pNewNode->SetIndex(index);
91 mDeletedNodeIndices.pop_back();
92 delete this->mNodes[index];
93 this->mNodes[index] = pNewNode;
94 }
95 mAddedNodes = true;
96 return pNewNode->GetIndex();
97}
98
99template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
101{
102 unsigned new_elt_index;
103
104 if (mDeletedElementIndices.empty())
105 {
106 new_elt_index = this->mElements.size();
107 this->mElements.push_back(pNewElement);
108 pNewElement->ResetIndex(new_elt_index);
109 }
110 else
111 {
112 unsigned index = mDeletedElementIndices.back();
113 pNewElement->ResetIndex(index);
114 mDeletedElementIndices.pop_back();
115 delete this->mElements[index];
116 this->mElements[index] = pNewElement;
117 }
118
119 return pNewElement->GetIndex();
120}
121
122
123template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
125{
126 mDeletedElementIndices.clear();
127 mDeletedBoundaryElementIndices.clear();
128 mDeletedNodeIndices.clear();
129 mAddedNodes = false;
130
132}
133
134template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
136{
137 return this->mBoundaryElements.size() - mDeletedBoundaryElementIndices.size();
138}
139
140template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
142{
143 return this->mElements.size() - mDeletedElementIndices.size();
144}
145
146template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
148{
149 return this->mNodes.size() - mDeletedNodeIndices.size();
150}
151
158template<>
159void MutableMesh<1, 1>::RescaleMeshFromBoundaryNode(ChastePoint<1> updatedPoint, unsigned boundaryNodeIndex)
160{
161 assert(this->GetNode(boundaryNodeIndex)->IsBoundaryNode());
162 double scaleFactor = updatedPoint[0] / this->GetNode(boundaryNodeIndex)->GetPoint()[0];
163 double temp;
164 for (unsigned i=0; i < boundaryNodeIndex+1; i++)
165 {
166 temp = scaleFactor * this->mNodes[i]->GetPoint()[0];
167 ChastePoint<1> newPoint(temp);
168 this->mNodes[i]->SetPoint(newPoint);
169 }
170 this->RefreshMesh();
172
173template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
176 bool concreteMove)
177{
178 this->mNodes[index]->SetPoint(point);
179
180 if (concreteMove)
182 for (typename Node<SPACE_DIM>::ContainingBoundaryElementIterator it = this->mNodes[index]->ContainingBoundaryElementsBegin();
183 it != this->mNodes[index]->ContainingBoundaryElementsEnd();
184 ++it)
185 {
186 try
187 {
188 this->GetBoundaryElement(*it)->CalculateWeightedDirection(this->mBoundaryElementWeightedDirections[ (*it) ],
189 this->mBoundaryElementJacobianDeterminants[ (*it) ]);
190 }
191 catch (Exception&)
192 {
193 EXCEPTION("Moving node caused a boundary element to have a non-positive Jacobian determinant");
194 }
195 }
196 for (typename Node<SPACE_DIM>::ContainingElementIterator it = this->mNodes[index]->ContainingElementsBegin();
197 it != this->mNodes[index]->ContainingElementsEnd();
198 ++it)
199 {
200 if (ELEMENT_DIM == SPACE_DIM)
202 try
203 {
204 this->GetElement(*it)->CalculateInverseJacobian(this->mElementJacobians[ (*it) ],
205 this->mElementJacobianDeterminants[ (*it) ],
206 this->mElementInverseJacobians[ (*it) ]);
207 }
208 catch (Exception&)
209 {
210 EXCEPTION("Moving node caused an element to have a non-positive Jacobian determinant");
211 }
212 }
213 else
214 {
215 c_vector<double,SPACE_DIM> previous_direction = this->mElementWeightedDirections[ (*it) ];
216
217 this->GetElement(*it)->CalculateWeightedDirection(this->mElementWeightedDirections[ (*it) ],
218 this->mElementJacobianDeterminants[ (*it) ]);
219
220 if (inner_prod(previous_direction, this->mElementWeightedDirections[ (*it) ]) < 0)
221 {
222 EXCEPTION("Moving node caused an subspace element to change direction");
223 }
224
225 }
227 }
228}
229
230template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
232{
233 if (this->mNodes[index]->IsDeleted())
235 EXCEPTION("Trying to delete a deleted node");
236 }
237 unsigned target_index = (unsigned)(-1);
238 bool found_target=false;
239 for (typename Node<SPACE_DIM>::ContainingElementIterator it = this->mNodes[index]->ContainingElementsBegin();
240 !found_target && it != this->mNodes[index]->ContainingElementsEnd();
241 ++it)
242 {
243 Element <ELEMENT_DIM,SPACE_DIM>* p_element = this->GetElement(*it);
244 for (unsigned i=0; i<=ELEMENT_DIM && !found_target; i++)
245 {
246 target_index = p_element->GetNodeGlobalIndex(i);
247 try
248 {
249 MoveMergeNode(index, target_index, false);
250 found_target = true;
251 }
252 catch (Exception&)
253 {
254 // Just try the next node
256 }
257 }
258 if (!found_target)
259 {
260 EXCEPTION("Failure to delete node");
261 }
262
263 MoveMergeNode(index, target_index);
264}
265
266template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
268{
269 assert(!this->mElements[index]->IsDeleted());
270 this->mElements[index]->MarkAsDeleted();
271 mDeletedElementIndices.push_back(index);
272
273 // Delete any nodes that are no longer attached to mesh
274 for (unsigned node_index = 0; node_index < this->mElements[index]->GetNumNodes(); ++node_index)
275 {
276 if (this->mElements[index]->GetNode(node_index)->GetNumContainingElements() == 0u)
277 {
278 if (this->mElements[index]->GetNode(node_index)->GetNumBoundaryElements() == 0u)
279 {
280 this->mElements[index]->GetNode(node_index)->MarkAsDeleted();
281 mDeletedNodeIndices.push_back(this->mElements[index]->GetNode(node_index)->GetIndex());
282 }
283 else if (this->mElements[index]->GetNode(node_index)->GetNumBoundaryElements() == 1u && ELEMENT_DIM == 1)
284 {
285 std::set<unsigned> indices = this->mElements[index]->GetNode(node_index)->rGetContainingBoundaryElementIndices();
286 assert(indices.size() == 1u);
287 this->mBoundaryElements[*indices.begin()]->MarkAsDeleted();
288 mDeletedBoundaryElementIndices.push_back(*indices.begin());
290 this->mElements[index]->GetNode(node_index)->MarkAsDeleted();
291 mDeletedNodeIndices.push_back(this->mElements[index]->GetNode(node_index)->GetIndex());
292 }
293 }
294 }
295}
296
297template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
299{
300 this->mNodes[index]->MarkAsDeleted();
301 mDeletedNodeIndices.push_back(index);
302}
304template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
306 unsigned targetIndex,
307 bool concreteMove)
308{
309 if (this->mNodes[index]->IsDeleted())
310 {
311 EXCEPTION("Trying to move a deleted node");
312 }
313
314 if (index == targetIndex)
315 {
316 EXCEPTION("Trying to merge a node with itself");
317 }
318 if (this->mNodes[index]->IsBoundaryNode())
320 if (!this->mNodes[targetIndex]->IsBoundaryNode())
321 {
322 EXCEPTION("A boundary node can only be moved on to another boundary node");
323 }
324 }
325 std::set<unsigned> unshared_element_indices;
326 std::set_difference(this->mNodes[index]->rGetContainingElementIndices().begin(),
327 this->mNodes[index]->rGetContainingElementIndices().end(),
328 this->mNodes[targetIndex]->rGetContainingElementIndices().begin(),
329 this->mNodes[targetIndex]->rGetContainingElementIndices().end(),
330 std::inserter(unshared_element_indices, unshared_element_indices.begin()));
331
332
333 if (unshared_element_indices.size() == this->mNodes[index]->rGetContainingElementIndices().size())
335 EXCEPTION("These nodes cannot be merged since they are not neighbours");
336 }
337
338 std::set<unsigned> unshared_boundary_element_indices;
339 std::set_difference(this->mNodes[index]->rGetContainingBoundaryElementIndices().begin(),
340 this->mNodes[index]->rGetContainingBoundaryElementIndices().end(),
341 this->mNodes[targetIndex]->rGetContainingBoundaryElementIndices().begin(),
342 this->mNodes[targetIndex]->rGetContainingBoundaryElementIndices().end(),
343 std::inserter(unshared_boundary_element_indices, unshared_boundary_element_indices.begin()));
344
345
346 if (this->mNodes[index]->IsBoundaryNode())
347 {
348 if (unshared_boundary_element_indices.size()
349 == this->mNodes[index]->rGetContainingBoundaryElementIndices().size())
350 {
351 //May be redundant (only thrown in 1D tests)
352 EXCEPTION("These nodes cannot be merged since they are not neighbours on the boundary");
354 }
355
356 this->mNodes[index]->rGetModifiableLocation() = this->mNodes[targetIndex]->rGetLocation();
357
358 for (std::set<unsigned>::const_iterator element_iter=unshared_element_indices.begin();
359 element_iter != unshared_element_indices.end();
360 element_iter++)
361 {
362 try
363 {
364 if (SPACE_DIM == ELEMENT_DIM)
365 {
366 this->GetElement(*element_iter)->CalculateInverseJacobian(this->mElementJacobians[(*element_iter)],
367 this->mElementJacobianDeterminants[(*element_iter)],
368 this->mElementInverseJacobians[ (*element_iter) ]);
369 }
370 else
371 {
372 this->GetElement(*element_iter)->CalculateWeightedDirection(this->mElementWeightedDirections[(*element_iter)],
373 this->mElementJacobianDeterminants[(*element_iter)]);
374 }
375
376 if (concreteMove)
377 {
378 this->GetElement(*element_iter)->ReplaceNode(this->mNodes[index], this->mNodes[targetIndex]);
379 }
380
381 }
382 catch (Exception&)
383 {
384 EXCEPTION("Moving node caused an element to have a non-positive Jacobian determinant");
385 }
386 }
387
388 for (auto boundary_element_iter = unshared_boundary_element_indices.begin();
389 boundary_element_iter != unshared_boundary_element_indices.end();
390 boundary_element_iter++)
391 {
392
393 this->GetBoundaryElement(*boundary_element_iter)->CalculateWeightedDirection(this->mBoundaryElementWeightedDirections[(*boundary_element_iter)],
394 this->mBoundaryElementJacobianDeterminants[(*boundary_element_iter)]);
395
396 if (concreteMove)
397 {
398 this->GetBoundaryElement(*boundary_element_iter)->ReplaceNode(this->mNodes[index], this->mNodes[targetIndex]);
399 }
400 }
401
402 std::set<unsigned> shared_element_indices;
403 std::set_intersection(this->mNodes[index]->rGetContainingElementIndices().begin(),
404 this->mNodes[index]->rGetContainingElementIndices().end(),
405 this->mNodes[targetIndex]->rGetContainingElementIndices().begin(),
406 this->mNodes[targetIndex]->rGetContainingElementIndices().end(),
407 std::inserter(shared_element_indices, shared_element_indices.begin()));
408
409 for (std::set<unsigned>::const_iterator element_iter=shared_element_indices.begin();
410 element_iter != shared_element_indices.end();
411 element_iter++)
412 {
413 if (concreteMove)
414 {
415 this->GetElement(*element_iter)->MarkAsDeleted();
416 this->mElementJacobianDeterminants[ (*element_iter) ] = 0.0; //This used to be done in MarkAsDeleted
417 mDeletedElementIndices.push_back(*element_iter);
418 }
419 else
420 {
421 this->mElementJacobianDeterminants[ (*element_iter) ] = 0.0;
422 }
423 }
424
425
426 std::set<unsigned> shared_boundary_element_indices;
427 std::set_intersection(this->mNodes[index]->rGetContainingBoundaryElementIndices().begin(),
428 this->mNodes[index]->rGetContainingBoundaryElementIndices().end(),
429 this->mNodes[targetIndex]->rGetContainingBoundaryElementIndices().begin(),
430 this->mNodes[targetIndex]->rGetContainingBoundaryElementIndices().end(),
431 std::inserter(shared_boundary_element_indices, shared_boundary_element_indices.begin()));
432
433 for (std::set<unsigned>::const_iterator boundary_element_iter=shared_boundary_element_indices.begin();
434 boundary_element_iter != shared_boundary_element_indices.end();
435 boundary_element_iter++)
436 {
437 if (concreteMove)
438 {
439 this->GetBoundaryElement(*boundary_element_iter)->MarkAsDeleted();
440 this->mBoundaryElementJacobianDeterminants[ (*boundary_element_iter) ] = 0.0; //This used to be done in MarkAsDeleted
441 mDeletedBoundaryElementIndices.push_back(*boundary_element_iter);
442 }
443 else
444 {
445 this->mBoundaryElementJacobianDeterminants[ (*boundary_element_iter) ] = 0.0;
446 this->mBoundaryElementWeightedDirections[ (*boundary_element_iter) ] = zero_vector<double>(SPACE_DIM);
447 }
448 }
449
450 if (concreteMove)
451 {
452 this->mNodes[index]->MarkAsDeleted();
453 mDeletedNodeIndices.push_back(index);
454 }
455}
456
457template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
461{
462 //Check that the point is in the element
463 if (pElement->IncludesPoint(point, true) == false)
464 {
465 EXCEPTION("RefineElement could not be started (point is not in element)");
466 }
467
468 // Add a new node from the point that is passed to RefineElement
469 unsigned new_node_index = AddNode(new Node<SPACE_DIM>(0, point.rGetLocation()));
470 // Note: the first argument is the index of the node, which is going to be
471 // overridden by AddNode, so it can safely be ignored
472
473 // This loop constructs the extra elements which are going to fill the space
474 for (unsigned i = 0; i < ELEMENT_DIM; i++)
475 {
476
477 // First, make a copy of the current element making sure we update its index
478 unsigned new_elt_index;
479 if (mDeletedElementIndices.empty())
480 {
481 new_elt_index = this->mElements.size();
482 }
483 else
484 {
485 new_elt_index = mDeletedElementIndices.back();
486 mDeletedElementIndices.pop_back();
487 }
488
489 auto* p_new_element = new Element<ELEMENT_DIM,SPACE_DIM>(*pElement, new_elt_index);
490
491 // Second, update the node in the element with the new one
492 p_new_element->UpdateNode(ELEMENT_DIM-1-i, this->mNodes[new_node_index]);
493
494 // Third, add the new element to the set
495 if ((unsigned) new_elt_index == this->mElements.size())
496 {
497 this->mElements.push_back(p_new_element);
498 }
499 else
500 {
501 delete this->mElements[new_elt_index];
502 this->mElements[new_elt_index] = p_new_element;
503 }
504 }
505
506 // Lastly, update the last node in the element to be refined
507 pElement->UpdateNode(ELEMENT_DIM, this->mNodes[new_node_index]);
508
509 return new_node_index;
510}
511
512template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
514{
515 if (!this->mNodes[index]->IsBoundaryNode() )
516 {
517 EXCEPTION(" You may only delete a boundary node ");
518 }
519
520 this->mNodes[index]->MarkAsDeleted();
521 mDeletedNodeIndices.push_back(index);
522
523 // Update the boundary node vector
524 auto b_node_iter = std::find(this->mBoundaryNodes.begin(), this->mBoundaryNodes.end(), this->mNodes[index]);
525 this->mBoundaryNodes.erase(b_node_iter);
526
527 // Remove boundary elements containing this node
528 std::set<unsigned> boundary_element_indices = this->mNodes[index]->rGetContainingBoundaryElementIndices();
529 std::set<unsigned>::const_iterator boundary_element_indices_iterator = boundary_element_indices.begin();
530 while (boundary_element_indices_iterator != boundary_element_indices.end())
531 {
532 BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_boundary_element = this->GetBoundaryElement(*boundary_element_indices_iterator);
533 p_boundary_element->MarkAsDeleted();
534 mDeletedBoundaryElementIndices.push_back(*boundary_element_indices_iterator);
535 boundary_element_indices_iterator++;
536 }
537
538 // Remove elements containing this node
539 std::set<unsigned> element_indices = this->mNodes[index]->rGetContainingElementIndices();
540 std::set<unsigned>::const_iterator element_indices_iterator = element_indices.begin();
541 while (element_indices_iterator != element_indices.end())
542 {
543 Element<ELEMENT_DIM, SPACE_DIM>* p_element = this->GetElement(*element_indices_iterator);
544 for (unsigned i=0; i<p_element->GetNumNodes(); i++)
545 {
546 Node<SPACE_DIM>* p_node = p_element->GetNode(i);
547 if (!p_node->IsDeleted())
548 {
549 p_node->SetAsBoundaryNode();
550 // Update the boundary node vector
551 this->mBoundaryNodes.push_back(p_node);
552 }
553 }
554 p_element->MarkAsDeleted();
555 mDeletedElementIndices.push_back(p_element->GetIndex());
556 element_indices_iterator++;
557 }
558}
559
560template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
562{
563 assert(!mAddedNodes);
564 map.Resize(this->GetNumAllNodes());
565
566 std::vector<Element<ELEMENT_DIM, SPACE_DIM> *> live_elements;
567
568 for (unsigned i=0; i<this->mElements.size(); i++)
569 {
570 assert(i==this->mElements[i]->GetIndex()); // We need this to be true to be able to reindex the Jacobian cache
571 if (this->mElements[i]->IsDeleted())
572 {
573 delete this->mElements[i];
574 }
575 else
576 {
577 live_elements.push_back(this->mElements[i]);
578
579 unsigned this_element_index = this->mElements[i]->GetIndex();
580 if (SPACE_DIM == ELEMENT_DIM)
581 {
582 this->mElementJacobians[live_elements.size()-1] = this->mElementJacobians[this_element_index];
583 this->mElementInverseJacobians[live_elements.size()-1] = this->mElementInverseJacobians[this_element_index];
584 }
585 else
586 {
587 this->mElementWeightedDirections[live_elements.size()-1] = this->mElementWeightedDirections[this_element_index];
588 }
589 this->mElementJacobianDeterminants[live_elements.size()-1] = this->mElementJacobianDeterminants[this_element_index];
590 }
591 }
592
593 assert(mDeletedElementIndices.size() == this->mElements.size()-live_elements.size());
594 mDeletedElementIndices.clear();
595 this->mElements = live_elements;
596 unsigned num_elements = this->mElements.size();
597
598 if (SPACE_DIM == ELEMENT_DIM)
599 {
600 this->mElementJacobians.resize(num_elements);
601 this->mElementInverseJacobians.resize(num_elements);
602 }
603 else
604 {
605 this->mElementWeightedDirections.resize(num_elements);
606 }
607 this->mElementJacobianDeterminants.resize(num_elements);
608
609 std::vector<Node<SPACE_DIM> *> live_nodes;
610 for (unsigned i=0; i<this->mNodes.size(); i++)
611 {
612 if (this->mNodes[i]->IsDeleted())
613 {
614 delete this->mNodes[i];
615 map.SetDeleted(i);
616 }
617 else
618 {
619 live_nodes.push_back(this->mNodes[i]);
620 // the nodes will have their index set to be the index into the live_nodes
621 // vector further down
622 map.SetNewIndex(i, (unsigned)(live_nodes.size()-1));
623 }
624 }
625
626 assert(mDeletedNodeIndices.size() == this->mNodes.size()-live_nodes.size());
627 this->mNodes = live_nodes;
628 mDeletedNodeIndices.clear();
629
630 std::vector<BoundaryElement<ELEMENT_DIM-1, SPACE_DIM> *> live_boundary_elements;
631 for (unsigned i=0; i<this->mBoundaryElements.size(); i++)
632 {
633 if (this->mBoundaryElements[i]->IsDeleted())
634 {
635 delete this->mBoundaryElements[i];
636 }
637 else
638 {
639 live_boundary_elements.push_back(this->mBoundaryElements[i]);
640
641 this->mBoundaryElementWeightedDirections[live_boundary_elements.size()-1] = this->mBoundaryElementWeightedDirections[this->mBoundaryElements[i]->GetIndex()];
642 this->mBoundaryElementJacobianDeterminants[live_boundary_elements.size()-1] = this->mBoundaryElementJacobianDeterminants[this->mBoundaryElements[i]->GetIndex()];
643 }
644 }
645
646 assert(mDeletedBoundaryElementIndices.size() == this->mBoundaryElements.size()-live_boundary_elements.size());
647 this->mBoundaryElements = live_boundary_elements;
648 mDeletedBoundaryElementIndices.clear();
649
650 unsigned num_boundary_elements = this->mBoundaryElements.size();
651
652 this->mBoundaryElementWeightedDirections.resize(num_boundary_elements);
653 this->mBoundaryElementJacobianDeterminants.resize(num_boundary_elements);
654
655 for (unsigned i=0; i<this->mNodes.size(); i++)
656 {
657 this->mNodes[i]->SetIndex(i);
658 }
659
660 for (unsigned i=0; i<this->mElements.size(); i++)
661 {
662 this->mElements[i]->ResetIndex(i);
663 }
664
665 for (unsigned i=0; i<this->mBoundaryElements.size(); i++)
666 {
667 this->mBoundaryElements[i]->ResetIndex(i);
668 }
669}
670
671template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
673{
674 // Make sure that we are in the correct dimension - this code will be eliminated at compile time
675 assert( ELEMENT_DIM == SPACE_DIM ); // LCOV_EXCL_LINE
676
677 // Avoid some triangle/tetgen errors: need at least four
678 // nodes for tetgen, and at least three for triangle
679 if (GetNumNodes() <= SPACE_DIM)
680 {
681 EXCEPTION("The number of nodes must exceed the spatial dimension.");
682 }
683
684 // Make sure the map is big enough
685 map.Resize(this->GetNumAllNodes());
686 if (mAddedNodes || !mDeletedNodeIndices.empty())
687 {
688 // Size of mesh is about to change
689 if (this->mpDistributedVectorFactory)
690 {
691 delete this->mpDistributedVectorFactory;
692 this->mpDistributedVectorFactory = new DistributedVectorFactory(this->GetNumNodes());
693 }
694 }
695 if (SPACE_DIM == 1)
696 {
697 // Store the node locations
698 std::vector<c_vector<double, SPACE_DIM> > old_node_locations;
699 unsigned new_index = 0;
700 for (unsigned i=0; i<this->GetNumAllNodes(); i++)
701 {
702 if (this->mNodes[i]->IsDeleted())
703 {
704 map.SetDeleted(i);
705 }
706 else
707 {
708 map.SetNewIndex(i, new_index);
709 old_node_locations.push_back(this->mNodes[i]->rGetLocation());
710 new_index++;
711 }
712 }
713
714 // Remove current data
715 Clear();
716
717 // Construct the nodes and boundary nodes
718 for (unsigned node_index=0; node_index<old_node_locations.size(); node_index++)
719 {
720 // As we're in 1D, the boundary nodes are simply at either end of the mesh
721 bool is_boundary_node = (node_index==0 || node_index==old_node_locations.size()-1);
722
723 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index, old_node_locations[node_index], is_boundary_node);
724 this->mNodes.push_back(p_node);
725
726 if (is_boundary_node)
727 {
728 this->mBoundaryNodes.push_back(p_node);
729 }
730 }
731
732 // Create a map between node indices and node locations
733 std::map<double, unsigned> location_index_map;
734 for (unsigned i=0; i<this->mNodes.size(); i++)
735 {
736 location_index_map[this->mNodes[i]->rGetLocation()[0]] = this->mNodes[i]->GetIndex();
737 }
738
739 // Use this map to generate a vector of node indices that are ordered spatially
740 std::vector<unsigned> node_indices_ordered_spatially;
741 for (std::map<double, unsigned>::iterator iter = location_index_map.begin();
742 iter != location_index_map.end();
743 ++iter)
744 {
745 node_indices_ordered_spatially.push_back(iter->second);
746 }
747
748 // Construct the elements
749 this->mElements.reserve(old_node_locations.size()-1);
750 for (unsigned element_index=0; element_index<old_node_locations.size()-1; element_index++)
751 {
752 std::vector<Node<SPACE_DIM>*> nodes;
753 for (unsigned j=0; j<2; j++)
754 {
755 unsigned global_node_index = node_indices_ordered_spatially[element_index + j];
756 assert(global_node_index < this->mNodes.size());
757 nodes.push_back(this->mNodes[global_node_index]);
758 }
759 this->mElements.push_back(new Element<ELEMENT_DIM, SPACE_DIM>(element_index, nodes));
760 }
761
762 // Construct the two boundary elements - as we're in 1D, these are simply at either end of the mesh
763 std::vector<Node<SPACE_DIM>*> nodes;
764 nodes.push_back(this->mNodes[0]);
765 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(0, nodes));
766
767 nodes.clear();
768 nodes.push_back(this->mNodes[old_node_locations.size()-1]);
769 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(1, nodes));
770
771 this->RefreshJacobianCachedData();
772 }
773 else if (SPACE_DIM==2) // In 2D, remesh using triangle via library calls
774 {
775 struct triangulateio mesher_input, mesher_output;
776 this->InitialiseTriangulateIo(mesher_input);
777 this->InitialiseTriangulateIo(mesher_output);
778
779 this->ExportToMesher(map, mesher_input);
780
781 // Library call
782 triangulate((char*)"Qze", &mesher_input, &mesher_output, nullptr);
783
784 this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
785
786 //Tidy up triangle
787 this->FreeTriangulateIo(mesher_input);
788 this->FreeTriangulateIo(mesher_output);
789 }
790 else // in 3D, remesh using tetgen
791 {
792
793 class tetgen::tetgenio mesher_input, mesher_output;
794
795 this->ExportToMesher(map, mesher_input);
796
797 // Library call
798 tetgen::tetrahedralize((char*)"Qz", &mesher_input, &mesher_output);
799
800 this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, nullptr);
801 }
802}
803
804template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
806{
807 NodeMap map(GetNumNodes());
808 ReMesh(map);
809}
810
811template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
812std::vector<c_vector<unsigned, 5> > MutableMesh<ELEMENT_DIM, SPACE_DIM>::SplitLongEdges(double cutoffLength)
813{
814 assert(ELEMENT_DIM == 2); // LCOV_EXCL_LINE
815 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
816
817 std::vector<c_vector<unsigned, 5> > history;
818
819 bool long_edge_exists = true;
820
821 while (long_edge_exists)
822 {
823 std::set<std::pair<unsigned, unsigned> > long_edges;
824
825 // Loop over elements to check for Long edges
826 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator elem_iter = this->GetElementIteratorBegin();
827 elem_iter != this->GetElementIteratorEnd();
828 ++elem_iter)
829 {
830 unsigned num_nodes = ELEMENT_DIM+1;
831
832 // Loop over element vertices
833 for (unsigned local_index=0; local_index<num_nodes; local_index++)
834 {
835 // Find locations of current node (node a) and anticlockwise node (node b)
836 Node<SPACE_DIM>* p_node_a = elem_iter->GetNode(local_index);
837 unsigned local_index_plus_one = (local_index+1)%num_nodes;
838 Node<SPACE_DIM>* p_node_b = elem_iter->GetNode(local_index_plus_one);
839
840 // Find distance between nodes
841 double distance_between_nodes = this->GetDistanceBetweenNodes(p_node_a->GetIndex(), p_node_b->GetIndex());
842
843 if (distance_between_nodes > cutoffLength)
844 {
845 if (p_node_a->GetIndex() < p_node_b->GetIndex())
846 {
847 std::pair<unsigned, unsigned> long_edge(p_node_a->GetIndex(),p_node_b->GetIndex());
848 long_edges.insert(long_edge);
849 }
850 else
851 {
852 std::pair<unsigned, unsigned> long_edge(p_node_b->GetIndex(),p_node_a->GetIndex());
853 long_edges.insert(long_edge);
854 }
855 }
856 }
857 }
858
859 if (long_edges.size() > 0) //Split the edges in decreasing order.
860 {
861 while (long_edges.size() > 0)
862 {
863 double longest_edge = 0.0;
864 std::set<std::pair<unsigned, unsigned> >::iterator longest_edge_iter;
865
866 //Find the longest edge in the set and split it
867 for (std::set<std::pair<unsigned, unsigned> >::iterator edge_iter = long_edges.begin();
868 edge_iter != long_edges.end();
869 ++edge_iter)
870 {
871 unsigned node_a_global_index = edge_iter->first;
872 unsigned node_b_global_index = edge_iter->second;
873
874 double distance_between_nodes = this->GetDistanceBetweenNodes(node_a_global_index, node_b_global_index);
875
876 if (distance_between_nodes > longest_edge)
877 {
878 longest_edge = distance_between_nodes;
879 longest_edge_iter = edge_iter;
880 }
881 }
882 assert(longest_edge >0);
883
884 c_vector<unsigned, 3> new_node_index = SplitEdge(this->GetNode(longest_edge_iter->first), this->GetNode(longest_edge_iter->second));
885
886 c_vector<unsigned, 5> node_set;
887 node_set(0) = new_node_index[0];
888 node_set(1) = longest_edge_iter->first;
889 node_set(2) = longest_edge_iter->second;
890 node_set(3) = new_node_index[1];
891 node_set(4) = new_node_index[2];
892 history.push_back(node_set);
893
894 // Delete pair from set
895 long_edges.erase(*longest_edge_iter);
896 }
897 }
898 else
899 {
900 long_edge_exists = false;
901 }
902 }
903
904 return history;
905}
906
907template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
909{
910 c_vector<unsigned, 3> new_node_index_vector;
911
912 std::set<unsigned> elements_of_node_a = pNodeA->rGetContainingElementIndices();
913 std::set<unsigned> elements_of_node_b = pNodeB->rGetContainingElementIndices();
914
915 std::set<unsigned> intersection_elements;
916 std::set_intersection(elements_of_node_a.begin(), elements_of_node_a.end(),
917 elements_of_node_b.begin(), elements_of_node_b.end(),
918 std::inserter(intersection_elements, intersection_elements.begin()));
919
920 // Create the new node
921 c_vector<double, SPACE_DIM> new_node_location = pNodeA->rGetLocation() + 0.5*this->GetVectorFromAtoB(pNodeA->rGetLocation(), pNodeB->rGetLocation());
922
923 bool is_boundary_node = intersection_elements.size() == 1; // If only in one element then its on the boundary
924
925 Node<SPACE_DIM>* p_new_node = new Node<SPACE_DIM>(0u,new_node_location,is_boundary_node); // Index is rewritten once added to mesh
926
927 unsigned new_node_index = this->AddNode(p_new_node);
928
929 new_node_index_vector[0] = new_node_index;
930
931 unsigned counter = 1;
932
933 for (std::set<unsigned>::const_iterator it = intersection_elements.begin(); it != intersection_elements.end(); ++it)
934 {
935 unsigned elementIndex = *it;
936
937 Element<ELEMENT_DIM,SPACE_DIM>* p_original_element = this->GetElement(elementIndex);
938
939 // First, make a copy of the current element and assign an unused index
940 auto* p_new_element = new Element<ELEMENT_DIM,SPACE_DIM>(*p_original_element, UINT_MAX);
941
942 // Second, add the new element to the set of existing elements. This method will assign a proper index to the element.
943 AddElement(p_new_element);
944
945 // Third, update node a in the element with the new one
946 p_new_element->ReplaceNode(pNodeA, this->mNodes[new_node_index]);
947
948 // Last, update node b in the original element with the new one
949 p_original_element->ReplaceNode(pNodeB, this->mNodes[new_node_index]);
950
951 //Add node in both of these elements to new_node_index_vector (this enables us to add a new spring in the MeshBasedCellPopulation
952 unsigned other_node_index = UNSIGNED_UNSET;
953
954 if ((p_original_element->GetNodeGlobalIndex(0) != new_node_index) &&
955 (p_original_element->GetNodeGlobalIndex(0) != pNodeA->GetIndex()))
956 {
957 other_node_index = p_original_element->GetNodeGlobalIndex(0);
958 }
959 else if ((p_original_element->GetNodeGlobalIndex(1) != new_node_index) &&
960 (p_original_element->GetNodeGlobalIndex(1) != pNodeA->GetIndex()))
961 {
962 other_node_index = p_original_element->GetNodeGlobalIndex(1);
963 }
964 else if ((p_original_element->GetNodeGlobalIndex(2) != new_node_index) &&
965 (p_original_element->GetNodeGlobalIndex(2) != pNodeA->GetIndex()))
966 {
967 other_node_index = p_original_element->GetNodeGlobalIndex(2);
968 }
969 else
970 {
972 }
973 new_node_index_vector[counter] = other_node_index;
974 counter++;
975 }
976
977 assert(counter < 4);
978 assert(counter > 1);// need to be in at least one element
979
980 if (counter == 2) // only one new element
981 {
982 new_node_index_vector[2] = UNSIGNED_UNSET;
983 }
984
985 return new_node_index_vector;
986}
987
988template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
990{
991 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
992 unsigned num_nodes = pElement->GetNumNodes();
993 std::set<unsigned> neighbouring_elements_indices;
994 std::set< Element<ELEMENT_DIM,SPACE_DIM> *> neighbouring_elements;
995 std::set<unsigned> neighbouring_nodes_indices;
996
997 // Form a set of neighbouring elements via the nodes
998 for (unsigned i=0; i<num_nodes; i++)
999 {
1000 Node<SPACE_DIM>* p_node = pElement->GetNode(i);
1001 neighbouring_elements_indices = p_node->rGetContainingElementIndices();
1002 for (std::set<unsigned>::const_iterator it = neighbouring_elements_indices.begin();
1003 it != neighbouring_elements_indices.end();
1004 ++it)
1005 {
1006 neighbouring_elements.insert(this->GetElement(*it));
1007 }
1008 }
1009 neighbouring_elements.erase(pElement);
1010
1011 // For each neighbouring element find the supporting nodes
1012 typedef typename std::set<Element<ELEMENT_DIM,SPACE_DIM> *>::const_iterator ElementIterator;
1013
1014 for (ElementIterator it = neighbouring_elements.begin();
1015 it != neighbouring_elements.end();
1016 ++it)
1017 {
1018 for (unsigned i=0; i<num_nodes; i++)
1019 {
1020 neighbouring_nodes_indices.insert((*it)->GetNodeGlobalIndex(i));
1021 }
1022 }
1023
1024 // Remove the nodes that support this element
1025 for (unsigned i = 0; i < num_nodes; i++)
1026 {
1027 neighbouring_nodes_indices.erase(pElement->GetNodeGlobalIndex(i));
1028 }
1029
1030 // Get the circumsphere information
1031 c_vector<double, SPACE_DIM+1> this_circum_centre = zero_vector<double>(SPACE_DIM+1);
1032
1033 this_circum_centre = pElement->CalculateCircumsphere(this->mElementJacobians[pElement->GetIndex()], this->mElementInverseJacobians[pElement->GetIndex()]);
1034
1035 // Copy the actually circumcentre into a smaller vector
1036 c_vector<double, ELEMENT_DIM> circum_centre = zero_vector<double>(ELEMENT_DIM);
1037 for (unsigned i=0; i<ELEMENT_DIM; i++)
1038 {
1039 circum_centre[i] = this_circum_centre[i];
1040 }
1041
1042 for (std::set<unsigned>::const_iterator it = neighbouring_nodes_indices.begin();
1043 it != neighbouring_nodes_indices.end();
1044 ++it)
1045 {
1046 c_vector<double, ELEMENT_DIM> node_location = this->GetNode(*it)->rGetLocation();
1047
1048 // Calculate vector from circumcenter to node
1049 node_location -= circum_centre;
1050
1051 // This is to calculate the squared distance between them
1052 double squared_distance = inner_prod(node_location, node_location);
1053
1054 // If the squared distance is less than the elements circum-radius(squared),
1055 // then the Voronoi property is violated.
1056 if (squared_distance < this_circum_centre[ELEMENT_DIM])
1057 {
1058 // We know the node is inside the circumsphere, but we don't know how far
1059 double radius = sqrt(this_circum_centre[ELEMENT_DIM]);
1060 double distance = radius - sqrt(squared_distance);
1061
1062 // If the node penetration is greater than supplied maximum penetration factor
1063 if (distance/radius > maxPenetration)
1064 {
1065 return false;
1066 }
1067 }
1068 }
1069 return true;
1070}
1071
1072template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1074{
1075 // Looping through all the elements in the mesh
1077 for (unsigned i=0; i<this->mElements.size(); i++)
1078 {
1079 // Check if the element is not deleted
1080 if (!this->mElements[i]->IsDeleted())
1081 {
1082 // Checking the Voronoi of the Element
1083 if (CheckIsVoronoi(this->mElements[i], maxPenetration) == false)
1084 {
1085 return false;
1086 }
1087 }
1088 }
1089 return true;
1090}
1091
1092// Explicit instantiation
1093template class MutableMesh<1,1>;
1094template class MutableMesh<1,2>;
1095template class MutableMesh<1,3>;
1096template class MutableMesh<2,2>;
1097template class MutableMesh<2,3>;
1098template class MutableMesh<3,3>;
1099
1100// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define NEVER_REACHED
const unsigned UNSIGNED_UNSET
Definition Exception.hpp:53
#define EXPORT_TEMPLATE_CLASS_ALL_DIMS(CLASS)
void ReplaceNode(Node< SPACE_DIM > *pOldNode, Node< SPACE_DIM > *pNewNode)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
unsigned GetNumNodes() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
unsigned GetIndex() const
bool mMeshChangesDuringSimulation
c_vector< double, DIM > & rGetLocation()
bool IncludesPoint(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false)
Definition Element.cpp:304
void UpdateNode(const unsigned &rIndex, Node< SPACE_DIM > *pNode)
Definition Element.cpp:85
void ResetIndex(unsigned index)
Definition Element.cpp:100
void MarkAsDeleted()
Definition Element.cpp:74
c_vector< double, SPACE_DIM+1 > CalculateCircumsphere(c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, c_matrix< double, ELEMENT_DIM, SPACE_DIM > &rInverseJacobian)
Definition Element.cpp:114
unsigned GetNumElements() const
bool CheckIsVoronoi(Element< ELEMENT_DIM, SPACE_DIM > *pElement, double maxPenetration)
c_vector< unsigned, 3 > SplitEdge(Node< SPACE_DIM > *pNodeA, Node< SPACE_DIM > *pNodeB)
void ReIndex(NodeMap &map)
unsigned AddElement(Element< ELEMENT_DIM, SPACE_DIM > *pNewElement)
unsigned GetNumNodes() const
void DeleteNodePriorToReMesh(unsigned index)
virtual void SetNode(unsigned index, ChastePoint< SPACE_DIM > point, bool concreteMove=true)
virtual void DeleteNode(unsigned index)
void RescaleMeshFromBoundaryNode(ChastePoint< 1 > updatedPoint, unsigned boundaryNodeIndex)
void DeleteBoundaryNodeAt(unsigned index)
std::vector< c_vector< unsigned, 5 > > SplitLongEdges(double cutoffLength)
virtual ~MutableMesh()
unsigned RefineElement(Element< ELEMENT_DIM, SPACE_DIM > *pElement, ChastePoint< SPACE_DIM > point)
unsigned GetNumBoundaryElements() const
virtual unsigned AddNode(Node< SPACE_DIM > *pNewNode)
void MoveMergeNode(unsigned index, unsigned targetIndex, bool concreteMove=true)
virtual void DeleteElement(unsigned index)
void SetDeleted(unsigned index)
Definition NodeMap.cpp:76
void SetNewIndex(unsigned oldIndex, unsigned newIndex)
Definition NodeMap.cpp:66
void Resize(unsigned size)
Definition NodeMap.cpp:52
Definition Node.hpp:59
std::set< unsigned > & rGetContainingElementIndices()
Definition Node.cpp:300
void SetIndex(unsigned index)
Definition Node.cpp:121
bool IsDeleted() const
Definition Node.cpp:412
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
unsigned GetIndex() const
Definition Node.cpp:158
void SetAsBoundaryNode(bool value=true)
Definition Node.cpp:127
virtual void Clear()