Chaste  Release::3.4
AbstractTetrahedralMesh.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, 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 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include <limits>
37 #include "AbstractTetrahedralMesh.hpp"
38 
40 // Implementation
42 
43 
44 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
46 {
47  unsigned lo=this->GetDistributedVectorFactory()->GetLow();
48  unsigned hi=this->GetDistributedVectorFactory()->GetHigh();
49  for (unsigned element_index=0; element_index<mElements.size(); element_index++)
50  {
51  Element<ELEMENT_DIM, SPACE_DIM>* p_element = mElements[element_index];
52  p_element->SetOwnership(false);
53  for (unsigned local_node_index=0; local_node_index< p_element->GetNumNodes(); local_node_index++)
54  {
55  unsigned global_node_index = p_element->GetNodeGlobalIndex(local_node_index);
56  if (lo<=global_node_index && global_node_index<hi)
57  {
58  p_element->SetOwnership(true);
59  break;
60  }
61  }
62  }
63 }
64 
65 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
67  : mMeshIsLinear(true)
68 {
69 }
70 
71 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
73 {
74  // Iterate over elements and free the memory
75  for (unsigned i=0; i<mElements.size(); i++)
76  {
77  delete mElements[i];
78  }
79  // Iterate over boundary elements and free the memory
80  for (unsigned i=0; i<mBoundaryElements.size(); i++)
81  {
82  delete mBoundaryElements[i];
83  }
84 }
85 
86 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
88 {
89  return mElements.size();
90 }
91 
92 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
94 {
95  return GetNumElements();
96 }
97 
98 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
100 {
101  return mElements.size();
102 }
103 
104 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
106 {
107  return mBoundaryElements.size();
108 }
109 
110 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
112 {
113  return GetNumBoundaryElements();
114 }
115 
116 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
118 {
119  return mBoundaryElements.size();
120 }
121 
122 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
124 {
125  return 0u;
126 }
127 
128 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
130 {
131  return this->GetNumNodes();
132 }
133 
134 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
136 {
137  return this->GetNumAllNodes();
138 }
139 
140 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
142 {
143  unsigned local_index = SolveElementMapping(index);
144  return mElements[local_index];
145 }
146 
147 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
149 {
150  unsigned local_index = SolveBoundaryElementMapping(index);
151  return mBoundaryElements[local_index];
152 }
153 
154 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
156 {
157  return mBoundaryElements.begin();
158 }
159 
160 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
162 {
163  return mBoundaryElements.end();
164 }
165 
166 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
168  unsigned elementIndex,
169  c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian,
170  double& rJacobianDeterminant,
171  c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian) const
172 {
173  mElements[SolveElementMapping(elementIndex)]->CalculateInverseJacobian(rJacobian, rJacobianDeterminant, rInverseJacobian);
174 }
175 
176 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
178  unsigned elementIndex,
179  c_vector<double, SPACE_DIM>& rWeightedDirection,
180  double& rJacobianDeterminant) const
181 {
182  mBoundaryElements[SolveBoundaryElementMapping(elementIndex)]->CalculateWeightedDirection(rWeightedDirection, rJacobianDeterminant );
183 }
184 
185 
186 template<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 = NULL;
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 != NULL);
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 
245 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
247 {
248  assert(ELEMENT_DIM == 1);
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 
277 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278 void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructRectangularMesh(unsigned width, unsigned height, bool stagger)
279 {
280  assert(SPACE_DIM == 2);
281  assert(ELEMENT_DIM == 2);
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++)
288  {
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));
342  }
343 
344  //Construct the elements
345  unsigned elem_index = 0;
346  for (unsigned j=0; j<height; j++)
347  {
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)
357  {
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)
370  {
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));
378  }
379  }
380 
381  this->RefreshMesh();
382 }
383 
384 
385 
386 
387 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
389  unsigned height,
390  unsigned depth)
391 {
392  assert(SPACE_DIM == 3);
393  assert(ELEMENT_DIM == 3);
394  //Construct the nodes
395 
396  unsigned node_index = 0;
397  for (unsigned k=0; k<depth+1; k++)
398  {
399  for (unsigned j=0; j<height+1; j++)
400  {
401  for (unsigned i=0; i<width+1; i++)
402  {
403  bool is_boundary = false;
404  if (i==0 || j==0 || k==0 || i==width || j==height || k==depth)
405  {
406  is_boundary = true;
407  }
408  assert(node_index == (k*(height+1)+j)*(width+1)+i);
409  Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index++, is_boundary, i, j, k);
410 
411  this->mNodes.push_back(p_node);
412  if (is_boundary)
413  {
414  this->mBoundaryNodes.push_back(p_node);
415  }
416  }
417  }
418  }
419 
420  // Construct the elements
421 
422  unsigned elem_index = 0;
423  unsigned belem_index = 0;
424  unsigned element_nodes[6][4] = {{0, 1, 5, 7}, {0, 1, 3, 7},
425  {0, 2, 3, 7}, {0, 2, 6, 7},
426  {0, 4, 6, 7}, {0, 4, 5, 7}};
427 /* Alternative tessellation - (gerardus)
428  * Note that our method (above) has a bias that all tetrahedra share a
429  * common edge (the diagonal 0 - 7). In the following method the cube is
430  * split along the "face diagonal" 1-2-5-6 into two prisms. This also has a bias.
431  *
432  unsigned element_nodes[6][4] = {{ 0, 6, 5, 4},
433  { 0, 2, 6, 1},
434  { 0, 1, 6, 5},
435  { 1, 2, 3, 7},
436  { 1, 2, 6, 7},
437  { 1, 6, 7, 5 }};
438 */
439  std::vector<Node<SPACE_DIM>*> tetrahedra_nodes;
440 
441  for (unsigned k=0; k<depth; k++)
442  {
443  if (k!=0)
444  {
445  // height*width squares on upper face, k layers of 2*height+2*width square aroun
446  assert(belem_index == 2*(height*width+k*2*(height+width)) );
447  }
448  for (unsigned j=0; j<height; j++)
449  {
450  for (unsigned i=0; i<width; i++)
451  {
452  // Compute the nodes' index
453  unsigned global_node_indices[8];
454  unsigned local_node_index = 0;
455 
456  for (unsigned z = 0; z < 2; z++)
457  {
458  for (unsigned y = 0; y < 2; y++)
459  {
460  for (unsigned x = 0; x < 2; x++)
461  {
462  global_node_indices[local_node_index] = i+x+(width+1)*(j+y+(height+1)*(k+z));
463 
464  local_node_index++;
465  }
466  }
467  }
468 
469  for (unsigned m = 0; m < 6; m++)
470  {
471  // Tetrahedra #m
472 
473  tetrahedra_nodes.clear();
474 
475  for (unsigned n = 0; n < 4; n++)
476  {
477  tetrahedra_nodes.push_back(this->mNodes[global_node_indices[element_nodes[m][n]]]);
478  }
479 
480  assert(elem_index == 6 * ((k*height+j)*width+i)+m );
481  this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index++, tetrahedra_nodes));
482  }
483 
484  // Are we at a boundary?
485  std::vector<Node<SPACE_DIM>*> triangle_nodes;
486 
487  if (i == 0) //low face at x==0
488  {
489  triangle_nodes.clear();
490  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
491  triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
492  triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
493  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
494  triangle_nodes.clear();
495  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
496  triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
497  triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
498  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
499  }
500  if (i == width-1) //high face at x=width
501  {
502  triangle_nodes.clear();
503  triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
504  triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
505  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
506  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
507  triangle_nodes.clear();
508  triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
509  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
510  triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
511  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
512  }
513  if (j == 0) //low face at y==0
514  {
515  triangle_nodes.clear();
516  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
517  triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
518  triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
519  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
520  triangle_nodes.clear();
521  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
522  triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
523  triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
524  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
525  }
526  if (j == height-1) //high face at y=height
527  {
528  triangle_nodes.clear();
529  triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
530  triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
531  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
532  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
533  triangle_nodes.clear();
534  triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
535  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
536  triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
537  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
538  }
539  if (k == 0) //low face at z==0
540  {
541  triangle_nodes.clear();
542  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
543  triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
544  triangle_nodes.push_back(this->mNodes[global_node_indices[2]]);
545  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
546  triangle_nodes.clear();
547  triangle_nodes.push_back(this->mNodes[global_node_indices[0]]);
548  triangle_nodes.push_back(this->mNodes[global_node_indices[1]]);
549  triangle_nodes.push_back(this->mNodes[global_node_indices[3]]);
550  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
551  }
552  if (k == depth-1) //high face at z=depth
553  {
554  triangle_nodes.clear();
555  triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
556  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
557  triangle_nodes.push_back(this->mNodes[global_node_indices[5]]);
558  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
559  triangle_nodes.clear();
560  triangle_nodes.push_back(this->mNodes[global_node_indices[4]]);
561  triangle_nodes.push_back(this->mNodes[global_node_indices[6]]);
562  triangle_nodes.push_back(this->mNodes[global_node_indices[7]]);
563  this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
564  }
565  }//i
566  }//j
567  }//k
568 
569  this->RefreshMesh();
570 }
571 
572 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
573 void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ConstructRegularSlabMesh(double spaceStep, double width, double height, double depth)
574 {
575  assert(spaceStep>0.0);
576  assert(width>0.0);
577  if (ELEMENT_DIM > 1)
578  {
579  assert(height>0.0);
580  }
581  if (ELEMENT_DIM > 2)
582  {
583  assert(depth>0.0);
584  }
585  unsigned num_elem_x=(unsigned)((width+0.5*spaceStep)/spaceStep); //0.5*spaceStep is to ensure that rounding down snaps to correct number
586  unsigned num_elem_y=(unsigned)((height+0.5*spaceStep)/spaceStep);
587  unsigned num_elem_z=(unsigned)((depth+0.5*spaceStep)/spaceStep);
588 
589  //Make it obvious that actual_width_x etc. are temporaries used in spotting for exception
590  {
591  double actual_width_x=num_elem_x*spaceStep;
592  double actual_width_y=num_elem_y*spaceStep;
593  double actual_width_z=num_elem_z*spaceStep;
594 
595  //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
596  // Doing relative comparisons with zero is okay - if we avoid division by zero.
597  // However, it's best not to test whether " fabs( 0.0 - 0.0) > DBL_EPSILON*0.0 "
598  if ( fabs (actual_width_x - width) > DBL_EPSILON*width
599  ||( height!= 0.0 && fabs (actual_width_y - height) > DBL_EPSILON*height)
600  ||( depth != 0.0 && fabs (actual_width_z - depth) > DBL_EPSILON*depth ) )
601  {
602  EXCEPTION("Space step does not divide the size of the mesh");
603  }
604  }
605  switch (ELEMENT_DIM)
606  {
607  case 1:
608  this->ConstructLinearMesh(num_elem_x);
609  break;
610  case 2:
611  this->ConstructRectangularMesh(num_elem_x, num_elem_y); // Stagger=default value
612  break;
613  default:
614  case 3:
615  this->ConstructCuboid(num_elem_x, num_elem_y, num_elem_z);
616  }
617  this->Scale(spaceStep, spaceStep, spaceStep);
618 }
619 
620 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
622  unsigned dimension, double spaceStep,
623  double width, double height, double depth)
624 {
625  assert(ELEMENT_DIM == SPACE_DIM);
626  if (dimension >= SPACE_DIM)
627  {
628  EXCEPTION("Cannot split on non-existent dimension");
629  }
630  // Rotate the width -> height -> depth around (if dimension is non-default)
631  if (SPACE_DIM == 2 && dimension == 0)
632  {
633  double temp = height ;
634  height = width;
635  width = temp;
636  }
637  else if (SPACE_DIM == 3)
638  {
639  unsigned rotate_perm = SPACE_DIM - 1u - dimension; // How many shuffles to get the user's axis to the top
640  for (unsigned i=0; i<rotate_perm; i++)
641  {
642  double temp = depth;
643  depth = height;
644  height = width;
645  width = temp;
646  }
647  }
648  this->ConstructRegularSlabMesh(spaceStep, width, height, depth);
649  if (SPACE_DIM == 2 && dimension == 0)
650  {
651  // Rotate the positions back again x -> y -> x
652  // this->Rotate(M_PI_2);
653  c_matrix<double, 2, 2> axis_rotation = zero_matrix<double>(2, 2);
654  axis_rotation(0,1)=1.0;
655  axis_rotation(1,0)=-1.0;
656  this->Rotate(axis_rotation);
657  this->Translate(0.0, width); // Formerly known as height, but we rotated it
658  }
659  else if (SPACE_DIM == 3 && dimension == 0)
660  {
661  // this->RotateZ(M_PI_2);
662  // this->RotateY(M_PI_2);
663  // RotY * RotZ = [0 0 1; 1 0 0; 0 1 0] x->y->z->x
664  //this->Translate(depth /*old width*/, width /*old height*/, 0.0);
665  c_matrix<double, 3, 3> axis_permutation = zero_matrix<double>(3, 3);
666  axis_permutation(0, 2)=1.0;
667  axis_permutation(1, 0)=1.0;
668  axis_permutation(2, 1)=1.0;
669  this->Rotate(axis_permutation);
670  }
671  else if (SPACE_DIM == 3 && dimension == 1)
672  {
673  // this->RotateY(-M_PI_2);
674  // this->RotateZ(-M_PI_2);
675  // // RotZ' after RotY' = RotZ' * RotY' = [0 1 0; 0 0 1; 1 0 0] x->z->y->x
676  // this->Translate(height /*old width*/, 0.0, width /*old depth*/);
677  c_matrix<double, 3, 3> axis_permutation = zero_matrix<double>(3, 3);
678  axis_permutation(0, 1)=1.0;
679  axis_permutation(1, 2)=1.0;
680  axis_permutation(2, 0)=1.0;
681  this->Rotate(axis_permutation);
682  }
683 
684 }
685 
686 
687 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
689 {
690  // This may throw in the distributed parallel case
691  unsigned tie_break_index = this->GetBoundaryElement(faceIndex)->GetNodeGlobalIndex(0);
692 
693  // if it is in my range
694  if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(tie_break_index))
695  {
696  return true;
697  }
698  else
699  {
700  return false;
701  }
702 }
703 
704 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
706 {
707  // This may throw in the distributed parallel case
708  unsigned tie_break_index = this->GetElement(elementIndex)->GetNodeGlobalIndex(0);
709 
710  // if it is in my range
711  if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(tie_break_index))
712  {
713  return true;
714  }
715  else
716  {
717  return false;
718  }
719 }
720 
721 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
723 {
724 
725  if (this->mNodes.size() == 0u)
726  {
727 #define COVERAGE_IGNORE
728  /*
729  * Coverage of this block requires a mesh regular slab mesh with the number of
730  * elements in the primary dimension less than (num_procs - 1), e.g. a 1D mesh
731  * one element wide with num_procs >=3.
732  * Note that if a process owns no nodes, then it will still need to enter the
733  * collective call to MatMPIAIJSetPreallocation. In PetscTools::SetupMat, the
734  * rowPreallocation parameter uses the special value zero to indicate no preallocation.
735  */
736 
737  // This process owns no nodes and thus owns none of the mesh
738  return (1u);
739 #undef COVERAGE_IGNORE
740  }
741 
742  unsigned nodes_per_element = this->mElements[0]->GetNumNodes(); //Usually ELEMENT_DIM+1, except in Quadratic case
743  if (ELEMENT_DIM <= 2u)
744  {
745  /*
746  * Note that we start assuming that each internal node is connected to 1 element.
747  * This is to avoid the trivial situation in which there are no internal nodes (a
748  * single line or a single triangle. We need the minimum connectivity to be 2 (in 1D) or
749  * 3 (in 2D) even if there is only one element.
750  */
751  unsigned max_num = 1u; // See note above.
752  unsigned boundary_max_num = 0u;
753  for (unsigned local_node_index=0; local_node_index<this->mNodes.size(); local_node_index++)
754  {
755  unsigned num = this->mNodes[local_node_index]->GetNumContainingElements();
756  if (this->mNodes[local_node_index]->IsBoundaryNode()==false && num>max_num)
757  {
758  max_num = num;
759  }
760  if (this->mNodes[local_node_index]->IsBoundaryNode() && num>boundary_max_num)
761  {
762  boundary_max_num = num;
763  }
764  }
765  bool linear = (nodes_per_element == ELEMENT_DIM + 1);
766  /*
767  * In 1d each containing element is connected to one node (or 2 if quadratic), add to this the node itself
768  * and the connectivity is GetNumContainingElements() + 1 or 2*GetNumContainingElements() + 1
769  */
770  if (ELEMENT_DIM == 1)
771  {
772  if (linear)
773  {
774  return max_num+1;
775  }
776  else
777  {
778  return 2*max_num+1;
779  }
780  }
781  // Not returned ...else if (ELEMENT_DIM == 2)
782  /*
783  * In 2d each containing element adds one connected node (since one node will be shared by a previous element)
784  * this leads to a connectivity of GetNumContainingElements() + 1 (or 3*GetNumContainingElements() + 1) in the quadratic case
785  *
786  * If the node is on a boundary then the one elements will have an unpaired node and the connectivity is
787  * GetNumContainingElements() + 1 (or 3*(GetNumContainingElements() + 3 for quadratic)
788  */
789  if (linear)
790  {
791  return std::max(max_num+1, boundary_max_num+2);
792  }
793  else
794  {
795  return std::max(3*max_num+1, 3*boundary_max_num+3);
796  }
797  }
798 
799  /*
800  * In 3d there are many more cases. In general a non-boundary node has fewer connecting nodes than it has elements.
801  * A node on the boundary may have even fewer, unless it is on a corner and has more faces than it has elements.
802  * We can, in the linear case estimate an upper bound as max(elements, faces)+2.
803  * However for the sake of accuracy we are going for a brute force solution.
804  *
805  * This may prove to be a bottle-neck...
806  */
807 
808  std::set<unsigned> forward_star_nodes; // Used to collect each node's neighbours
809  unsigned max_connectivity = 0u;
810  for (unsigned local_node_index=0; local_node_index<this->mNodes.size(); local_node_index++)
811  {
812  forward_star_nodes.clear();
813 
814  for (typename Node<SPACE_DIM>::ContainingElementIterator it = this->mNodes[local_node_index]->ContainingElementsBegin();
815  it != this->mNodes[local_node_index]->ContainingElementsEnd();
816  ++it)
817  {
818  Element<ELEMENT_DIM, SPACE_DIM>* p_elem = this->GetElement(*it);
819  for (unsigned i=0; i<nodes_per_element; i++)
820  {
821  forward_star_nodes.insert(p_elem->GetNodeGlobalIndex(i));
822  }
823  }
824  if (forward_star_nodes.size() > max_connectivity)
825  {
826  max_connectivity = forward_star_nodes.size();
827  }
828  }
829  return max_connectivity;
830 }
831 
832 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
833 void AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::GetHaloNodeIndices(std::vector<unsigned>& rHaloIndices) const
834 {
835  // Make sure the output vector is empty
836  rHaloIndices.clear();
837 }
838 
839 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
841 {
842  for (unsigned i=0; i<rOtherMesh.GetNumNodes(); i++)
843  {
844  Node<SPACE_DIM>* p_node = rOtherMesh.GetNode(i);
845  assert(!p_node->IsDeleted());
846  c_vector<double, SPACE_DIM> location=p_node->rGetLocation();
847  bool is_boundary=p_node->IsBoundaryNode();
848 
849  Node<SPACE_DIM>* p_node_copy = new Node<SPACE_DIM>(i, location, is_boundary);
850  this->mNodes.push_back( p_node_copy );
851  if (is_boundary)
852  {
853  this->mBoundaryNodes.push_back( p_node_copy );
854  }
855  }
856 
857  for (unsigned i=0; i<rOtherMesh.GetNumElements(); i++)
858  {
859  Element<ELEMENT_DIM, SPACE_DIM>* p_elem = rOtherMesh.GetElement(i);
860  assert(!p_elem->IsDeleted());
861  std::vector<Node<SPACE_DIM>*> nodes_for_element;
862  for (unsigned j=0; j<p_elem->GetNumNodes(); j++)
863  {
864  nodes_for_element.push_back(this->mNodes[ p_elem->GetNodeGlobalIndex(j) ]);
865  }
866  Element<ELEMENT_DIM, SPACE_DIM>* p_elem_copy = new Element<ELEMENT_DIM, SPACE_DIM>(i, nodes_for_element);
867  p_elem_copy->RegisterWithNodes();
868  this->mElements.push_back(p_elem_copy);
869  }
870 
871  for (unsigned i=0; i<rOtherMesh.GetNumBoundaryElements(); i++)
872  {
873  BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_elem = rOtherMesh.GetBoundaryElement(i);
874  assert(!p_b_elem->IsDeleted());
875  std::vector<Node<SPACE_DIM>*> nodes_for_element;
876  for (unsigned j=0; j<p_b_elem->GetNumNodes(); j++)
877  {
878  nodes_for_element.push_back(this->mNodes[ p_b_elem->GetNodeGlobalIndex(j) ]);
879  }
880  BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>* p_b_elem_copy = new BoundaryElement<ELEMENT_DIM-1, SPACE_DIM>(i, nodes_for_element);
881  p_b_elem_copy->RegisterWithNodes();
882  this->mBoundaryElements.push_back(p_b_elem_copy);
883  }
884  this->RefreshMesh();
885 
886 }
887 
888 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
890  std::vector<std::vector<unsigned> >& rNodesToSendPerProcess,
891  std::vector<std::vector<unsigned> >& rNodesToReceivePerProcess)
892 {
893  assert( rNodesToSendPerProcess.empty() );
894  assert( rNodesToReceivePerProcess.empty() );
895 
896  //Initialise vectors of sets for the exchange data
897  std::vector<std::set<unsigned> > node_sets_to_send_per_process;
898  std::vector<std::set<unsigned> > node_sets_to_receive_per_process;
899 
900  node_sets_to_send_per_process.resize(PetscTools::GetNumProcs());
901  node_sets_to_receive_per_process.resize(PetscTools::GetNumProcs());
902  std::vector<unsigned> global_lows = this->GetDistributedVectorFactory()->rGetGlobalLows();
903 
904  for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator iter = this->GetElementIteratorBegin();
905  iter != this->GetElementIteratorEnd();
906  ++iter)
907  {
908  std::vector <unsigned> nodes_on_this_process;
909  std::vector <unsigned> nodes_not_on_this_process;
910  //Calculate local and non-local node indices
911  for (unsigned i=0; i<ELEMENT_DIM+1; i++)
912  {
913  unsigned node_index=iter->GetNodeGlobalIndex(i);
914  if (this->GetDistributedVectorFactory()->IsGlobalIndexLocal(node_index))
915  {
916  nodes_on_this_process.push_back(node_index);
917  }
918  else
919  {
920  nodes_not_on_this_process.push_back(node_index);
921  }
922  }
923  /*
924  * If this is a TetrahedralMesh (not distributed) then it's possible that we own none
925  * of the nodes in this element. In that case we must skip the element.
926  */
927  if (nodes_on_this_process.empty())
928  {
929  continue; //Move on to the next element.
931  }
932  // If there are any non-local nodes on this element then we need to add to the data exchange
933  if (!nodes_not_on_this_process.empty())
934  {
935  for (unsigned i=0; i<nodes_not_on_this_process.size(); i++)
936  {
937  // Calculate who owns this remote node
938  unsigned remote_process=global_lows.size()-1;
939  for (; global_lows[remote_process] > nodes_not_on_this_process[i]; remote_process--)
940  {
941  }
942 
943  // Add this node to the correct receive set
944  node_sets_to_receive_per_process[remote_process].insert(nodes_not_on_this_process[i]);
945 
946  // Add all local nodes to the send set
947  for (unsigned j=0; j<nodes_on_this_process.size(); j++)
948  {
949  node_sets_to_send_per_process[remote_process].insert(nodes_on_this_process[j]);
950  }
951  }
952  }
953  }
954 
955  for (unsigned process_number = 0; process_number < PetscTools::GetNumProcs(); process_number++)
956  {
957  std::vector<unsigned> process_send_vector( node_sets_to_send_per_process[process_number].begin(),
958  node_sets_to_send_per_process[process_number].end() );
959  std::sort(process_send_vector.begin(), process_send_vector.end());
960 
961  rNodesToSendPerProcess.push_back(process_send_vector);
962 
963  std::vector<unsigned> process_receive_vector( node_sets_to_receive_per_process[process_number].begin(),
964  node_sets_to_receive_per_process[process_number].end() );
965  std::sort(process_receive_vector.begin(), process_receive_vector.end());
966 
967  rNodesToReceivePerProcess.push_back(process_receive_vector);
968  }
969 
970 }
971 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
973 {
974  c_vector<double, 2> min_max;
975  min_max[0] = DBL_MAX;
976  min_max[1] = 0.0;
977  for (typename AbstractTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::ElementIterator ele_iter = GetElementIteratorBegin();
978  ele_iter != GetElementIteratorEnd();
979  ++ele_iter)
980  {
981  c_vector<double, 2> ele_min_max = ele_iter->CalculateMinMaxEdgeLengths();
982  if (ele_min_max[0] < min_max[0])
983  {
984  min_max[0] = ele_min_max[0];
985  }
986  if (ele_min_max[1] > min_max[1])
987  {
988  min_max[1] = ele_min_max[1];
989  }
990  }
991  return min_max;
992 }
993 
994 
995 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
997  bool strict,
998  std::set<unsigned> testElements,
999  bool onlyTryWithTestElements)
1000 {
1001  for (std::set<unsigned>::iterator iter=testElements.begin(); iter!=testElements.end(); iter++)
1002  {
1003  assert(*iter<this->GetNumElements());
1004  if (this->mElements[*iter]->IncludesPoint(rTestPoint, strict))
1005  {
1006  assert(!this->mElements[*iter]->IsDeleted());
1007  return *iter;
1008  }
1009  }
1010 
1011  if (!onlyTryWithTestElements)
1012  {
1013  for (unsigned i=0; i<this->mElements.size(); i++)
1014  {
1015  if (this->mElements[i]->IncludesPoint(rTestPoint, strict))
1016  {
1017  assert(!this->mElements[i]->IsDeleted());
1018  return i;
1019  }
1020  }
1021  }
1022 
1023  // If it's in none of the elements, then throw
1024  std::stringstream ss;
1025  ss << "Point [";
1026  for (unsigned j=0; (int)j<(int)SPACE_DIM-1; j++)
1027  {
1028  ss << rTestPoint[j] << ",";
1029  }
1030  ss << rTestPoint[SPACE_DIM-1] << "] is not in ";
1031  if (!onlyTryWithTestElements)
1032  {
1033  ss << "mesh - all elements tested";
1034  }
1035  else
1036  {
1037  ss << "set of elements given";
1038  }
1039  EXCEPTION(ss.str());
1040 }
1041 
1042 
1043 
1044 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1046  std::set<unsigned> testElements)
1047 {
1048  assert(testElements.size() > 0);
1049 
1050  double max_min_weight = -std::numeric_limits<double>::infinity();
1051  unsigned closest_index = 0;
1052  for (std::set<unsigned>::iterator iter = testElements.begin();
1053  iter != testElements.end();
1054  iter++)
1055  {
1056  c_vector<double, ELEMENT_DIM+1> weight = this->mElements[*iter]->CalculateInterpolationWeights(rTestPoint);
1057  double neg_weight_sum = 0.0;
1058  for (unsigned j=0; j<=ELEMENT_DIM; j++)
1059  {
1060  if (weight[j] < 0.0)
1061  {
1062  neg_weight_sum += weight[j];
1063  }
1064  }
1065  if (neg_weight_sum > max_min_weight)
1066  {
1067  max_min_weight = neg_weight_sum;
1068  closest_index = *iter;
1069  }
1070  }
1071  assert(!this->mElements[closest_index]->IsDeleted());
1072  return closest_index;
1073 }
1074 
1076 // Explicit instantiation
1078 
1079 template class AbstractTetrahedralMesh<1,1>;
1080 template class AbstractTetrahedralMesh<1,2>;
1081 template class AbstractTetrahedralMesh<1,3>;
1082 template class AbstractTetrahedralMesh<2,2>;
1083 template class AbstractTetrahedralMesh<2,3>;
1084 template class AbstractTetrahedralMesh<3,3>;
virtual unsigned GetNumBoundaryElements() const
virtual unsigned GetNumLocalBoundaryElements() const
Definition: Node.hpp:58
BoundaryElementIterator GetBoundaryElementIteratorBegin() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
virtual unsigned GetMaximumNodeIndex()
virtual bool CalculateDesignatedOwnershipOfBoundaryElement(unsigned faceIndex)
void ConstructRegularSlabMesh(double spaceStep, double width, double height=0, double depth=0)
bool IsBoundaryNode() const
Definition: Node.cpp:165
Node< SPACE_DIM > * GetNode(unsigned index) const
virtual unsigned GetNumElements() const
#define EXCEPTION(message)
Definition: Exception.hpp:143
virtual unsigned GetNumCableElements() const
unsigned CalculateMaximumNodeConnectivityPerProcess() const
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
void SetOwnership(bool ownership)
virtual unsigned GetNumNodes() const
virtual void GetHaloNodeIndices(std::vector< unsigned > &rHaloIndices) 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 unsigned GetNumLocalElements() const
void RegisterWithNodes()
Definition: Element.cpp:65
bool IsDeleted() const
ContainingElementIterator ContainingElementsBegin() const
Definition: Node.hpp:444
virtual void GetWeightedDirectionForBoundaryElement(unsigned elementIndex, c_vector< double, SPACE_DIM > &rWeightedDirection, double &rJacobianDeterminant) const
unsigned GetNumNodes() const
virtual unsigned GetNumVertices() const
unsigned GetContainingElementIndex(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false, std::set< unsigned > testElements=std::set< unsigned >(), bool onlyTryWithTestElements=false)
virtual void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * GetBoundaryElement(unsigned index) const
virtual void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
const c_vector< double, SPACE_DIM > & rGetLocation() const
Definition: Node.cpp:140
void CalculateNodeExchange(std::vector< std::vector< unsigned > > &rNodesToSendPerProcess, std::vector< std::vector< unsigned > > &rNodesToReceivePerProcess)
unsigned GetNumAllBoundaryElements() const
unsigned GetNearestElementIndexFromTestElements(const ChastePoint< SPACE_DIM > &rTestPoint, std::set< unsigned > testElements)
virtual bool CalculateDesignatedOwnershipOfElement(unsigned elementIndex)
virtual void ConstructLinearMesh(unsigned width)
static unsigned GetNumProcs()
Definition: PetscTools.cpp:108
std::vector< BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * >::const_iterator BoundaryElementIterator
virtual c_vector< double, 2 > CalculateMinMaxEdgeLengths()
void ConstructFromMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rOtherMesh)
bool IsDeleted() const
Definition: Node.cpp:353
ContainingElementIterator ContainingElementsEnd() const
Definition: Node.hpp:452
void ConstructRegularSlabMeshWithDimensionSplit(unsigned dimension, double spaceStep, double width, double height=0, double depth=0)
BoundaryElementIterator GetBoundaryElementIteratorEnd() const