Chaste Commit::9dfedaa0870fc7cfa8911a7ba21eba441bdba6b4
AbstractTetrahedralMesh.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 <algorithm>
37#include <limits>
38#include "AbstractTetrahedralMesh.hpp"
39
41// Implementation
43
44
45template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
47{
48 unsigned lo=this->GetDistributedVectorFactory()->GetLow();
49 unsigned hi=this->GetDistributedVectorFactory()->GetHigh();
50 for (unsigned element_index=0; element_index<mElements.size(); element_index++)
51 {
52 Element<ELEMENT_DIM, SPACE_DIM>* p_element = mElements[element_index];
53 p_element->SetOwnership(false);
54 for (unsigned local_node_index=0; local_node_index< p_element->GetNumNodes(); local_node_index++)
55 {
56 unsigned global_node_index = p_element->GetNodeGlobalIndex(local_node_index);
57 if (lo<=global_node_index && global_node_index<hi)
58 {
59 p_element->SetOwnership(true);
60 break;
61 }
62 }
63 }
64}
65
66template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
71
72template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
74{
75 // Iterate over elements and free the memory
76 for (unsigned i=0; i<mElements.size(); i++)
77 {
78 delete mElements[i];
79 }
80 // Iterate over boundary elements and free the memory
81 for (unsigned i=0; i<mBoundaryElements.size(); i++)
82 {
83 delete mBoundaryElements[i];
84 }
85}
86
87template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
89{
90 return mElements.size();
91}
92
93template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
95{
96 return GetNumElements();
97}
98
99template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
101{
102 return mElements.size();
103}
104
105template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
107{
108 return mBoundaryElements.size();
109}
110
111template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
113{
114 return GetNumBoundaryElements();
115}
116
117template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
119{
120 return mBoundaryElements.size();
121}
122
123template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
128
129template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
131{
132 return this->GetNumNodes();
133}
134
135template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
137{
138 return this->GetNumAllNodes();
139}
140
141template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
143{
144 unsigned local_index = SolveElementMapping(index);
145 return mElements[local_index];
146}
147
148template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
150{
151 unsigned local_index = SolveBoundaryElementMapping(index);
152 return mBoundaryElements[local_index];
153}
154
155template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
160
161template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
166
167template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
169 unsigned elementIndex,
170 c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian,
171 double& rJacobianDeterminant,
172 c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian) const
173{
174 mElements[SolveElementMapping(elementIndex)]->CalculateInverseJacobian(rJacobian, rJacobianDeterminant, rInverseJacobian);
175}
176
177template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
179 unsigned elementIndex,
180 c_vector<double, SPACE_DIM>& rWeightedDirection,
181 double& rJacobianDeterminant) const
182{
183 mBoundaryElements[SolveBoundaryElementMapping(elementIndex)]->CalculateWeightedDirection(rWeightedDirection, rJacobianDeterminant );
184}
185
186template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
188{
189 if (ELEMENT_DIM <= 1)
190 {
191 //If the ELEMENT_DIM of the mesh is 1 then the boundary will have ELEMENT_DIM = 0
192 EXCEPTION("1-D mesh has no boundary normals");
193 }
194 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::BoundaryElementIterator face_iter = this->GetBoundaryElementIteratorBegin();
195 face_iter != this->GetBoundaryElementIteratorEnd();
196 ++face_iter)
197 {
198 //Form a set for the boundary element node indices
199 std::set<unsigned> boundary_element_node_indices;
200 for (unsigned i=0; i<ELEMENT_DIM; i++)
201 {
202 boundary_element_node_indices.insert( (*face_iter)->GetNodeGlobalIndex(i) );
203 }
204
205 Node<SPACE_DIM>* p_opposite_node = nullptr;
206 Node<SPACE_DIM>* p_representative_node = (*face_iter)->GetNode(0);
207 for (typename Node<SPACE_DIM>::ContainingElementIterator element_iter = p_representative_node->ContainingElementsBegin();
208 element_iter != p_representative_node->ContainingElementsEnd();
209 ++element_iter)
210 {
211 Element<ELEMENT_DIM, SPACE_DIM>* p_element = this->GetElement(*element_iter);
212 //Form a set for the element node indices
213 std::set<unsigned> element_node_indices;
214 for (unsigned i=0; i<=ELEMENT_DIM; i++)
215 {
216 element_node_indices.insert( p_element->GetNodeGlobalIndex(i) );
217 }
218
219 std::vector<unsigned> difference(ELEMENT_DIM);
220
221 std::vector<unsigned>::iterator set_iter = std::set_difference(
222 element_node_indices.begin(),element_node_indices.end(),
223 boundary_element_node_indices.begin(), boundary_element_node_indices.end(),
224 difference.begin());
225 if (set_iter - difference.begin() == 1)
226 {
227 p_opposite_node = this -> GetNodeOrHaloNode(difference[0]);
228 break;
229 }
230 }
231 assert(p_opposite_node != nullptr);
232
233 // Vector from centroid of face to opposite node
234 c_vector<double, SPACE_DIM> into_mesh = p_opposite_node->rGetLocation() - (*face_iter)->CalculateCentroid();
235 c_vector<double, SPACE_DIM> normal = (*face_iter)->CalculateNormal();
236
237 if (inner_prod(into_mesh, normal) > 0.0)
238 {
239 EXCEPTION("Inward facing normal in boundary element index "<<(*face_iter)->GetIndex());
240 }
241 }
242}
243
244
245template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
247{
248 assert(ELEMENT_DIM == 1); // LCOV_EXCL_LINE
249
250 for (unsigned node_index=0; node_index<=width; node_index++)
251 {
252 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index, node_index==0 || node_index==width, node_index);
253 this->mNodes.push_back(p_node); // create node
254 if (node_index==0) // create left boundary node and boundary element
255 {
256 this->mBoundaryNodes.push_back(p_node);
257 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(0, p_node) );
258 }
259 if (node_index==width) // create right boundary node and boundary element
260 {
261 this->mBoundaryNodes.push_back(p_node);
262 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(1, p_node) );
263 }
264 if (node_index > 0) // create element
265 {
266 std::vector<Node<SPACE_DIM>*> nodes;
267 nodes.push_back(this->mNodes[node_index-1]);
268 nodes.push_back(this->mNodes[node_index]);
269 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(node_index-1, nodes) );
270 }
271 }
272
273 this->RefreshMesh();
274}
275
276
277template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructRectangularMesh(unsigned width, unsigned height, bool stagger)
279{
280 assert(SPACE_DIM == 2); // LCOV_EXCL_LINE
281 assert(ELEMENT_DIM == 2); // LCOV_EXCL_LINE
282
283 //Construct the nodes
284 unsigned node_index=0;
285 for (unsigned j=0; j<height+1; j++)
286 {
287 for (unsigned i=0; i<width+1; i++)
289 bool is_boundary=false;
290 if (i==0 || j==0 || i==width || j==height)
291 {
292 is_boundary=true;
293 }
294 //Check in place for parallel
295 assert(node_index==(width+1)*(j) + i);
296 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index++, is_boundary, i, j);
297 this->mNodes.push_back(p_node);
298 if (is_boundary)
299 {
300 this->mBoundaryNodes.push_back(p_node);
301 }
302 }
303 }
304
305 //Construct the boundary elements
306 unsigned belem_index=0;
307 //Top
308 for (unsigned i=0; i<width; i++)
309 {
310 std::vector<Node<SPACE_DIM>*> nodes;
311 nodes.push_back(this->mNodes[height*(width+1)+i+1]);
312 nodes.push_back(this->mNodes[height*(width+1)+i]);
313 assert(belem_index==i);
314 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,nodes));
315 }
316 //Right
317 for (unsigned j=1; j<=height; j++)
318 {
319 std::vector<Node<SPACE_DIM>*> nodes;
320 nodes.push_back(this->mNodes[(width+1)*j-1]);
321 nodes.push_back(this->mNodes[(width+1)*(j+1)-1]);
322 assert(belem_index==width+j-1);
323 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,nodes));
324 }
325 //Bottom
326 for (unsigned i=0; i<width; i++)
327 {
328 std::vector<Node<SPACE_DIM>*> nodes;
329 nodes.push_back(this->mNodes[i]);
330 nodes.push_back(this->mNodes[i+1]);
331 assert(belem_index==width+height+i);
332 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,nodes));
333 }
334 //Left
335 for (unsigned j=0; j<height; j++)
336 {
337 std::vector<Node<SPACE_DIM>*> nodes;
338 nodes.push_back(this->mNodes[(width+1)*(j+1)]);
339 nodes.push_back(this->mNodes[(width+1)*(j)]);
340 assert(belem_index==2*width+height+j);
341 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,nodes));
343
344 //Construct the elements
345 unsigned elem_index = 0;
346 for (unsigned j=0; j<height; j++)
348 for (unsigned i=0; i<width; i++)
349 {
350 unsigned parity=(i+(height-j))%2;//Note that parity is measured from the top-left (not bottom left) for historical reasons
351 unsigned nw=(j+1)*(width+1)+i; //ne=nw+1
352 unsigned sw=(j)*(width+1)+i; //se=sw+1
353 std::vector<Node<SPACE_DIM>*> upper_nodes;
354 upper_nodes.push_back(this->mNodes[nw]);
355 upper_nodes.push_back(this->mNodes[nw+1]);
356 if (stagger==false || parity == 1)
358 upper_nodes.push_back(this->mNodes[sw+1]);
359 }
360 else
361 {
362 upper_nodes.push_back(this->mNodes[sw]);
363 }
364 assert(elem_index==2*(j*width+i));
365 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index++,upper_nodes));
366 std::vector<Node<SPACE_DIM>*> lower_nodes;
367 lower_nodes.push_back(this->mNodes[sw+1]);
368 lower_nodes.push_back(this->mNodes[sw]);
369 if (stagger==false ||parity == 1)
371 lower_nodes.push_back(this->mNodes[nw]);
372 }
373 else
374 {
375 lower_nodes.push_back(this->mNodes[nw+1]);
376 }
377 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index++,lower_nodes));
379 }
380
381 this->RefreshMesh();
382}
383
384template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
386 unsigned height,
387 unsigned depth)
388{
389 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
390 assert(ELEMENT_DIM == 3); // LCOV_EXCL_LINE
391 //Construct the nodes
392
393 unsigned node_index = 0;
394 for (unsigned k=0; k<depth+1; k++)
395 {
396 for (unsigned j=0; j<height+1; j++)
397 {
398 for (unsigned i=0; i<width+1; i++)
399 {
400 bool is_boundary = false;
401 if (i==0 || j==0 || k==0 || i==width || j==height || k==depth)
402 {
403 is_boundary = true;
404 }
405 assert(node_index == (k*(height+1)+j)*(width+1)+i);
406 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index++, is_boundary, i, j, k);
407
408 this->mNodes.push_back(p_node);
409 if (is_boundary)
410 {
411 this->mBoundaryNodes.push_back(p_node);
412 }
414 }
415 }
416
417 // Construct the elements
418
419 unsigned elem_index = 0;
420 unsigned belem_index = 0;
421 unsigned element_nodes[6][4] = {{0, 1, 5, 7}, {0, 1, 3, 7},
422 {0, 2, 3, 7}, {0, 2, 6, 7},
423 {0, 4, 6, 7}, {0, 4, 5, 7}};
424/* Alternative tessellation - (gerardus)
425 * Note that our method (above) has a bias that all tetrahedra share a
426 * common edge (the diagonal 0 - 7). In the following method the cube is
427 * split along the "face diagonal" 1-2-5-6 into two prisms. This also has a bias.
428 *
429 unsigned element_nodes[6][4] = {{ 0, 6, 5, 4},
430 { 0, 2, 6, 1},
431 { 0, 1, 6, 5},
432 { 1, 2, 3, 7},
433 { 1, 2, 6, 7},
434 { 1, 6, 7, 5 }};
436 std::vector<Node<SPACE_DIM>*> tetrahedra_nodes;
437
438 for (unsigned k=0; k<depth; k++)
439 {
440 if (k!=0)
441 {
442 // height*width squares on upper face, k layers of 2*height+2*width square aroun
443 assert(belem_index == 2*(height*width+k*2*(height+width)) );
444 }
445 for (unsigned j=0; j<height; j++)
446 {
447 for (unsigned i=0; i<width; i++)
448 {
449 // Compute the nodes' index
450 unsigned global_node_indices[8];
451 unsigned local_node_index = 0;
452
453 for (unsigned z = 0; z < 2; z++)
454 {
455 for (unsigned y = 0; y < 2; y++)
456 {
457 for (unsigned x = 0; x < 2; x++)
459 global_node_indices[local_node_index] = i+x+(width+1)*(j+y+(height+1)*(k+z));
460
461 local_node_index++;
462 }
463 }
464 }
465
466 for (unsigned m = 0; m < 6; m++)
467 {
468 // Tetrahedra #m
469
470 tetrahedra_nodes.clear();
471
472 for (unsigned n = 0; n < 4; n++)
473 {
474 tetrahedra_nodes.push_back(this->mNodes[global_node_indices[element_nodes[m][n]]]);
475 }
476
477 assert(elem_index == 6 * ((k*height+j)*width+i)+m );
478 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index++, tetrahedra_nodes));
479 }
480
481 // Are we at a boundary?
482 std::vector<Node<SPACE_DIM>*> triangle_nodes;
483
484 if (i == 0) //low face at x==0
485 {
486 triangle_nodes.clear();
487 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
488 triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
489 triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
490 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
491 triangle_nodes.clear();
492 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
493 triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
494 triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
495 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
496 }
497 if (i == width-1) //high face at x=width
498 {
499 triangle_nodes.clear();
500 triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
501 triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
502 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
503 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
504 triangle_nodes.clear();
505 triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
506 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
507 triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
508 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
509 }
510 if (j == 0) //low face at y==0
511 {
512 triangle_nodes.clear();
513 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
514 triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
515 triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
516 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
517 triangle_nodes.clear();
518 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
519 triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
520 triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
521 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
522 }
523 if (j == height-1) //high face at y=height
524 {
525 triangle_nodes.clear();
526 triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
527 triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
528 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
529 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
530 triangle_nodes.clear();
531 triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
532 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
533 triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
534 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
535 }
536 if (k == 0) //low face at z==0
537 {
538 triangle_nodes.clear();
539 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
540 triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
541 triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
542 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
543 triangle_nodes.clear();
544 triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
545 triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
546 triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
547 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
548 }
549 if (k == depth-1) //high face at z=depth
550 {
551 triangle_nodes.clear();
552 triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
553 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
554 triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
555 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
556 triangle_nodes.clear();
557 triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
558 triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
559 triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
560 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
561 }
562 }//i
563 }//j
564 }//k
565
566 this->RefreshMesh();
567}
568
569template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
570void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructRegularSlabMesh(double spaceStep, double width, double height, double depth)
571{
572 assert(spaceStep>0.0);
573 assert(width>0.0);
574 if (ELEMENT_DIM > 1)
575 {
576 assert(height>0.0);
577 }
578 if (ELEMENT_DIM > 2)
579 {
580 assert(depth>0.0);
581 }
582 unsigned num_elem_x=(unsigned)((width+0.5*spaceStep)/spaceStep); //0.5*spaceStep is to ensure that rounding down snaps to correct number
583 unsigned num_elem_y=(unsigned)((height+0.5*spaceStep)/spaceStep);
584 unsigned num_elem_z=(unsigned)((depth+0.5*spaceStep)/spaceStep);
586 //Make it obvious that actual_width_x etc. are temporaries used in spotting for exception
587 {
588 double actual_width_x=num_elem_x*spaceStep;
589 double actual_width_y=num_elem_y*spaceStep;
590 double actual_width_z=num_elem_z*spaceStep;
591
592 //Note here that in ELEMENT_DIM > 1 cases there may be a zero height or depth - in which case we don't need to use relative comparisons
593 // Doing relative comparisons with zero is okay - if we avoid division by zero.
594 // However, it's best not to test whether " fabs( 0.0 - 0.0) > DBL_EPSILON*0.0 "
595 if (fabs (actual_width_x - width) > DBL_EPSILON*width
596 ||( height!= 0.0 && fabs (actual_width_y - height) > DBL_EPSILON*height)
597 ||( depth != 0.0 && fabs (actual_width_z - depth) > DBL_EPSILON*depth ))
598 {
599 EXCEPTION("Space step does not divide the size of the mesh");
600 }
601 }
602 switch (ELEMENT_DIM)
603 {
604 case 1:
605 this->ConstructLinearMesh(num_elem_x);
606 break;
607 case 2:
608 this->ConstructRectangularMesh(num_elem_x, num_elem_y); // Stagger=default value
609 break;
610 default:
611 case 3:
612 this->ConstructCuboid(num_elem_x, num_elem_y, num_elem_z);
613 }
614 this->Scale(spaceStep, spaceStep, spaceStep);
615}
616
617template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
619 unsigned dimension, double spaceStep,
620 double width, double height, double depth)
621{
622 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
623 if (dimension >= SPACE_DIM)
624 {
625 EXCEPTION("Cannot split on non-existent dimension");
626 }
627 // Rotate the width -> height -> depth around (if dimension is non-default)
628 if (SPACE_DIM == 2 && dimension == 0)
629 {
630 double temp = height ;
631 height = width;
632 width = temp;
633 }
634 else if (SPACE_DIM == 3)
635 {
636 unsigned rotate_perm = SPACE_DIM - 1u - dimension; // How many shuffles to get the user's axis to the top
637 for (unsigned i=0; i<rotate_perm; i++)
638 {
639 double temp = depth;
640 depth = height;
641 height = width;
642 width = temp;
643 }
644 }
645 this->ConstructRegularSlabMesh(spaceStep, width, height, depth);
646 if (SPACE_DIM == 2 && dimension == 0)
647 {
648 // Rotate the positions back again x -> y -> x
649 // this->Rotate(M_PI_2);
650 c_matrix<double, 2, 2> axis_rotation = zero_matrix<double>(2, 2);
651 axis_rotation(0,1)=1.0;
652 axis_rotation(1,0)=-1.0;
653 this->Rotate(axis_rotation);
654 this->Translate(0.0, width); // Formerly known as height, but we rotated it
655 }
656 else if (SPACE_DIM == 3 && dimension == 0)
657 {
658 // this->RotateZ(M_PI_2);
659 // this->RotateY(M_PI_2);
660 // RotY * RotZ = [0 0 1; 1 0 0; 0 1 0] x->y->z->x
661 //this->Translate(depth /*old width*/, width /*old height*/, 0.0);
662 c_matrix<double, 3, 3> axis_permutation = zero_matrix<double>(3, 3);
663 axis_permutation(0, 2)=1.0;
664 axis_permutation(1, 0)=1.0;
665 axis_permutation(2, 1)=1.0;
666 this->Rotate(axis_permutation);
667 }
668 else if (SPACE_DIM == 3 && dimension == 1)
669 {
670 // this->RotateY(-M_PI_2);
671 // this->RotateZ(-M_PI_2);
672 // // RotZ' after RotY' = RotZ' * RotY' = [0 1 0; 0 0 1; 1 0 0] x->z->y->x
673 // this->Translate(height /*old width*/, 0.0, width /*old depth*/);
674 c_matrix<double, 3, 3> axis_permutation = zero_matrix<double>(3, 3);
675 axis_permutation(0, 1)=1.0;
676 axis_permutation(1, 2)=1.0;
677 axis_permutation(2, 0)=1.0;
678 this->Rotate(axis_permutation);
679 }
680
681}
682
683
684template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
686{
687 // This may throw in the distributed parallel case
688 unsigned tie_break_index = this->GetBoundaryElement(faceIndex)->GetNodeGlobalIndex(0);
689
690 // if it is in my range
691 if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(tie_break_index))
692 {
693 return true;
694 }
695 else
696 {
697 return false;
698 }
699}
700
701template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
703{
704 // This may throw in the distributed parallel case
705 unsigned tie_break_index = this->GetElement(elementIndex)->GetNodeGlobalIndex(0);
706
707 // if it is in my range
708 if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(tie_break_index))
709 {
710 return true;
711 }
712 else
713 {
714 return false;
715 }
716}
717
718template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
720{
721
722 if (this->mNodes.size() == 0u)
723 {
724// LCOV_EXCL_START
725 /*
726 * Coverage of this block requires a mesh regular slab mesh with the number of
727 * elements in the primary dimension less than (num_procs - 1), e.g. a 1D mesh
728 * one element wide with num_procs >=3.
729 * Note that if a process owns no nodes, then it will still need to enter the
730 * collective call to MatMPIAIJSetPreallocation. In PetscTools::SetupMat, the
731 * rowPreallocation parameter uses the special value zero to indicate no preallocation.
732 */
733
734 // This process owns no nodes and thus owns none of the mesh
735 return (1u);
736// LCOV_EXCL_STOP
737 }
738
739 unsigned nodes_per_element = this->mElements[0]->GetNumNodes(); //Usually ELEMENT_DIM+1, except in Quadratic case
740 if (ELEMENT_DIM <= 2u)
741 {
742 /*
743 * Note that we start assuming that each internal node is connected to 1 element.
744 * This is to avoid the trivial situation in which there are no internal nodes (a
745 * single line or a single triangle. We need the minimum connectivity to be 2 (in 1D) or
746 * 3 (in 2D) even if there is only one element.
747 */
748 unsigned max_num = 1u; // See note above.
749 unsigned boundary_max_num = 0u;
750 for (unsigned local_node_index=0; local_node_index<this->mNodes.size(); local_node_index++)
751 {
752 unsigned num = this->mNodes[local_node_index]->GetNumContainingElements();
753 if (this->mNodes[local_node_index]->IsBoundaryNode()==false && num>max_num)
754 {
755 max_num = num;
756 }
757 if (this->mNodes[local_node_index]->IsBoundaryNode() && num>boundary_max_num)
758 {
759 boundary_max_num = num;
760 }
761 }
762 bool linear = (nodes_per_element == ELEMENT_DIM + 1);
763 /*
764 * In 1d each containing element is connected to one node (or 2 if quadratic), add to this the node itself
765 * and the connectivity is GetNumContainingElements() + 1 or 2*GetNumContainingElements() + 1
766 */
767 if (ELEMENT_DIM == 1)
768 {
769 if (linear)
770 {
771 return max_num+1;
772 }
773 else
774 {
775 return 2*max_num+1;
776 }
777 }
778 // Not returned ...else if (ELEMENT_DIM == 2)
779 /*
780 * In 2d each containing element adds one connected node (since one node will be shared by a previous element)
781 * this leads to a connectivity of GetNumContainingElements() + 1 (or 3*GetNumContainingElements() + 1) in the quadratic case
782 *
783 * If the node is on a boundary then the one elements will have an unpaired node and the connectivity is
784 * GetNumContainingElements() + 1 (or 3*(GetNumContainingElements() + 3 for quadratic)
785 */
786 if (linear)
787 {
788 return std::max(max_num+1, boundary_max_num+2);
789 }
790 else
791 {
792 return std::max(3*max_num+1, 3*boundary_max_num+3);
793 }
794 }
795
796 /*
797 * In 3d there are many more cases. In general a non-boundary node has fewer connecting nodes than it has elements.
798 * A node on the boundary may have even fewer, unless it is on a corner and has more faces than it has elements.
799 * We can, in the linear case estimate an upper bound as max(elements, faces)+2.
800 * However for the sake of accuracy we are going for a brute force solution.
801 *
802 * This may prove to be a bottle-neck...
803 */
804
805 std::set<unsigned> forward_star_nodes; // Used to collect each node's neighbours
806 unsigned max_connectivity = 0u;
807 for (unsigned local_node_index=0; local_node_index<this->mNodes.size(); local_node_index++)
808 {
809 forward_star_nodes.clear();
810
811 for (typename Node<SPACE_DIM>::ContainingElementIterator it = this->mNodes[local_node_index]->ContainingElementsBegin();
812 it != this->mNodes[local_node_index]->ContainingElementsEnd();
813 ++it)
814 {
815 Element<ELEMENT_DIM, SPACE_DIM>* p_elem = this->GetElement(*it);
816 for (unsigned i=0; i<nodes_per_element; i++)
817 {
818 forward_star_nodes.insert(p_elem->GetNodeGlobalIndex(i));
819 }
820 }
821 if (forward_star_nodes.size() > max_connectivity)
822 {
823 max_connectivity = forward_star_nodes.size();
824 }
825 }
826 return max_connectivity;
827}
828
829template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
830void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetHaloNodeIndices(std::vector<unsigned>& rHaloIndices) const
831{
832 // Make sure the output vector is empty
833 rHaloIndices.clear();
834}
835
836template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
838{
839 for (unsigned i=0; i<rOtherMesh.GetNumNodes(); i++)
840 {
841 Node<SPACE_DIM>* p_node = rOtherMesh.GetNode(i);
842 assert(!p_node->IsDeleted());
843 const c_vector<double, SPACE_DIM>& location=p_node->rGetLocation();
844 bool is_boundary=p_node->IsBoundaryNode();
845
846 Node<SPACE_DIM>* p_node_copy = new Node<SPACE_DIM>(i, location, is_boundary);
847 this->mNodes.push_back( p_node_copy );
848 if (is_boundary)
849 {
850 this->mBoundaryNodes.push_back( p_node_copy );
851 }
852 }
853
854 for (unsigned i=0; i<rOtherMesh.GetNumElements(); i++)
855 {
856 Element<ELEMENT_DIM, SPACE_DIM>* p_elem = rOtherMesh.GetElement(i);
857 assert(!p_elem->IsDeleted());
858 std::vector<Node<SPACE_DIM>*> nodes_for_element;
859 for (unsigned j=0; j<p_elem->GetNumNodes(); j++)
860 {
861 nodes_for_element.push_back(this->mNodes[ p_elem->GetNodeGlobalIndex(j) ]);
862 }
863 Element<ELEMENT_DIM, SPACE_DIM>* p_elem_copy = new Element<ELEMENT_DIM, SPACE_DIM>(i, nodes_for_element);
864 p_elem_copy->RegisterWithNodes();
865 this->mElements.push_back(p_elem_copy);
866 }
867
868 for (unsigned i=0; i<rOtherMesh.GetNumBoundaryElements(); i++)
869 {
870 BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_elem = rOtherMesh.GetBoundaryElement(i);
871 assert(!p_b_elem->IsDeleted());
872 std::vector<Node<SPACE_DIM>*> nodes_for_element;
873 for (unsigned j=0; j<p_b_elem->GetNumNodes(); j++)
874 {
875 nodes_for_element.push_back(this->mNodes[ p_b_elem->GetNodeGlobalIndex(j) ]);
876 }
877 BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_elem_copy = new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(i, nodes_for_element);
878 p_b_elem_copy->RegisterWithNodes();
879 this->mBoundaryElements.push_back(p_b_elem_copy);
880 }
881 this->RefreshMesh();
882
883}
884
885template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
887 std::vector<std::vector<unsigned> >& rNodesToSendPerProcess,
888 std::vector<std::vector<unsigned> >& rNodesToReceivePerProcess)
889{
890 assert( rNodesToSendPerProcess.empty() );
891 assert( rNodesToReceivePerProcess.empty() );
892
893 //Initialise vectors of sets for the exchange data
894 std::vector<std::set<unsigned> > node_sets_to_send_per_process;
895 std::vector<std::set<unsigned> > node_sets_to_receive_per_process;
896
897 node_sets_to_send_per_process.resize(PetscTools::GetNumProcs());
898 node_sets_to_receive_per_process.resize(PetscTools::GetNumProcs());
899 std::vector<unsigned> global_lows = this->GetDistributedVectorFactory()->rGetGlobalLows();
900
901 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
902 iter != this->GetElementIteratorEnd();
903 ++iter)
904 {
905 std::vector <unsigned> nodes_on_this_process;
906 std::vector <unsigned> nodes_not_on_this_process;
907 //Calculate local and non-local node indices
908 for (unsigned i=0; i<ELEMENT_DIM+1; i++)
909 {
910 unsigned node_index=iter->GetNodeGlobalIndex(i);
911 if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index))
912 {
913 nodes_on_this_process.push_back(node_index);
914 }
915 else
916 {
917 nodes_not_on_this_process.push_back(node_index);
918 }
919 }
920 /*
921 * If this is a TetrahedralMesh (not distributed) then it's possible that we own none
922 * of the nodes in this element. In that case we must skip the element.
923 */
924 if (nodes_on_this_process.empty())
925 {
926 continue; //Move on to the next element.
928 }
929 // If there are any non-local nodes on this element then we need to add to the data exchange
930 if (!nodes_not_on_this_process.empty())
931 {
932 for (unsigned i=0; i<nodes_not_on_this_process.size(); i++)
933 {
934 // Calculate who owns this remote node
935 unsigned remote_process=global_lows.size()-1;
936 for (; global_lows[remote_process] > nodes_not_on_this_process[i]; remote_process--)
937 {
938 }
939
940 // Add this node to the correct receive set
941 node_sets_to_receive_per_process[remote_process].insert(nodes_not_on_this_process[i]);
942
943 // Add all local nodes to the send set
944 for (unsigned j=0; j<nodes_on_this_process.size(); j++)
945 {
946 node_sets_to_send_per_process[remote_process].insert(nodes_on_this_process[j]);
947 }
948 }
949 }
950 }
951
952 for (unsigned process_number = 0; process_number < PetscTools::GetNumProcs(); process_number++)
953 {
954 std::vector<unsigned> process_send_vector( node_sets_to_send_per_process[process_number].begin(),
955 node_sets_to_send_per_process[process_number].end() );
956 std::sort(process_send_vector.begin(), process_send_vector.end());
957
958 rNodesToSendPerProcess.push_back(process_send_vector);
959
960 std::vector<unsigned> process_receive_vector( node_sets_to_receive_per_process[process_number].begin(),
961 node_sets_to_receive_per_process[process_number].end() );
962 std::sort(process_receive_vector.begin(), process_receive_vector.end());
963
964 rNodesToReceivePerProcess.push_back(process_receive_vector);
965 }
966
967}
968template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
970{
971 c_vector<double, 2> min_max;
972 min_max[0] = DBL_MAX;
973 min_max[1] = 0.0;
974 for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator ele_iter = GetElementIteratorBegin();
975 ele_iter != GetElementIteratorEnd();
976 ++ele_iter)
977 {
978 c_vector<double, 2> ele_min_max = ele_iter->CalculateMinMaxEdgeLengths();
979 min_max[0] = std::min(min_max[0], ele_min_max[0]);
980 min_max[1] = std::max(min_max[1], ele_min_max[1]);
981 }
982 return min_max;
983}
984
985
986template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
988 bool strict,
989 std::set<unsigned> testElements,
990 bool onlyTryWithTestElements)
991{
992 for (std::set<unsigned>::iterator iter=testElements.begin(); iter!=testElements.end(); iter++)
993 {
994 assert(*iter<this->GetNumElements());
995 if (this->mElements[*iter]->IncludesPoint(rTestPoint, strict))
996 {
997 assert(!this->mElements[*iter]->IsDeleted());
998 return *iter;
999 }
1000 }
1001
1002 if (!onlyTryWithTestElements)
1003 {
1004 for (unsigned i=0; i<this->mElements.size(); i++)
1005 {
1006 if (this->mElements[i]->IncludesPoint(rTestPoint, strict))
1007 {
1008 assert(!this->mElements[i]->IsDeleted());
1009 return i;
1010 }
1011 }
1012 }
1013
1014 // If it's in none of the elements, then throw
1015 std::stringstream ss;
1016 ss << "Point [";
1017 for (unsigned j=0; (int)j<(int)SPACE_DIM-1; j++)
1018 {
1019 ss << rTestPoint[j] << ",";
1020 }
1021 ss << rTestPoint[SPACE_DIM-1] << "] is not in ";
1022 if (!onlyTryWithTestElements)
1023 {
1024 ss << "mesh - all elements tested";
1025 }
1026 else
1027 {
1028 ss << "set of elements given";
1029 }
1030 EXCEPTION(ss.str());
1031}
1032
1033template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1035 std::set<unsigned> testElements)
1036{
1037 assert(testElements.size() > 0);
1038 EXCEPT_IF_NOT(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE // CalculateInterpolationWeights hits an assertion otherwise
1039
1040 double max_min_weight = -std::numeric_limits<double>::infinity();
1041 unsigned closest_index = 0;
1042 for (std::set<unsigned>::iterator iter = testElements.begin();
1043 iter != testElements.end();
1044 iter++)
1045 {
1046 c_vector<double, ELEMENT_DIM+1> weight = this->mElements[*iter]->CalculateInterpolationWeights(rTestPoint);
1047 double neg_weight_sum = 0.0;
1048 for (unsigned j=0; j<=ELEMENT_DIM; j++)
1049 {
1050 if (weight[j] < 0.0)
1051 {
1052 neg_weight_sum += weight[j];
1053 }
1054 }
1055 if (neg_weight_sum > max_min_weight)
1056 {
1057 max_min_weight = neg_weight_sum;
1058 closest_index = *iter;
1059 }
1060 }
1061 assert(!this->mElements[closest_index]->IsDeleted());
1062 return closest_index;
1063}
1064
1065// Explicit instantiation
1066template class AbstractTetrahedralMesh<1,1>;
1067template class AbstractTetrahedralMesh<1,2>;
1068template class AbstractTetrahedralMesh<1,3>;
1069template class AbstractTetrahedralMesh<2,2>;
1070template class AbstractTetrahedralMesh<2,3>;
1071template class AbstractTetrahedralMesh<3,3>;
#define EXCEPTION(message)
#define EXCEPT_IF_NOT(test)
bool IsDeleted() const
unsigned GetNumNodes() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
void SetOwnership(bool ownership)
virtual unsigned GetNumNodes() const
Node< SPACE_DIM > * GetNode(unsigned index) const
void ConstructRegularSlabMesh(double spaceStep, double width, double height=0, double depth=0)
virtual unsigned GetNumLocalBoundaryElements() const
virtual bool CalculateDesignatedOwnershipOfElement(unsigned elementIndex)
void CalculateNodeExchange(std::vector< std::vector< unsigned > > &rNodesToSendPerProcess, std::vector< std::vector< unsigned > > &rNodesToReceivePerProcess)
unsigned GetContainingElementIndex(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false, std::set< unsigned > testElements=std::set< unsigned >(), bool onlyTryWithTestElements=false)
virtual void GetWeightedDirectionForBoundaryElement(unsigned elementIndex, c_vector< double, SPACE_DIM > &rWeightedDirection, double &rJacobianDeterminant) const
virtual unsigned GetNumBoundaryElements() const
virtual void GetInverseJacobianForElement(unsigned elementIndex, c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, double &rJacobianDeterminant, c_matrix< double, ELEMENT_DIM, SPACE_DIM > &rInverseJacobian) const
virtual bool CalculateDesignatedOwnershipOfBoundaryElement(unsigned faceIndex)
void ConstructFromMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rOtherMesh)
virtual unsigned GetNumVertices() const
void ConstructRegularSlabMeshWithDimensionSplit(unsigned dimension, double spaceStep, double width, double height=0, double depth=0)
virtual void GetHaloNodeIndices(std::vector< unsigned > &rHaloIndices) const
std::vector< BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * >::const_iterator BoundaryElementIterator
virtual unsigned GetNumLocalElements() const
unsigned GetNearestElementIndexFromTestElements(const ChastePoint< SPACE_DIM > &rTestPoint, std::set< unsigned > testElements)
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
unsigned CalculateMaximumNodeConnectivityPerProcess() const
virtual c_vector< double, 2 > CalculateMinMaxEdgeLengths()
virtual void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
BoundaryElementIterator GetBoundaryElementIteratorBegin() const
BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * GetBoundaryElement(unsigned index) const
virtual unsigned GetNumCableElements() const
virtual void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
virtual unsigned GetNumElements() const
virtual void ConstructLinearMesh(unsigned width)
BoundaryElementIterator GetBoundaryElementIteratorEnd() const
void RegisterWithNodes()
Definition Element.cpp:65
Definition Node.hpp:59
ContainingElementIterator ContainingElementsEnd() const
Definition Node.hpp:493
ContainingElementIterator ContainingElementsBegin() const
Definition Node.hpp:485
bool IsDeleted() const
Definition Node.cpp:412
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition Node.cpp:139
bool IsBoundaryNode() const
Definition Node.cpp:164
static unsigned GetNumProcs()