Chaste  Release::3.4
QuadraticMesh.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 "QuadraticMesh.hpp"
37 #include "OutputFileHandler.hpp"
38 #include "TrianglesMeshReader.hpp"
39 #include "Warnings.hpp"
40 #include "QuadraticMeshHelper.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 template<unsigned DIM>
52 {
53  mNumVertices = 0;
54  for (unsigned i=0; i<this->GetNumNodes(); i++)
55  {
56  bool is_internal = this->GetNode(i)->IsInternal();
57  if (is_internal==false)
58  {
59  mNumVertices++;
60  }
61  }
62 }
63 
64 template<unsigned DIM>
65 QuadraticMesh<DIM>::QuadraticMesh(double spaceStep, double width, double height, double depth)
66 {
67  this->ConstructRegularSlabMesh(spaceStep, width, height, depth);
68 }
69 
71 // Badly-named (name inherited from parent class),
72 // 'linear' here refers to the fact it creates a 1d mesh
73 // on a line
75 template<unsigned DIM>
77 {
78  assert(DIM==1);
79 
81  assert (this->mNodes.size() == numElemX+1);
82  mNumVertices = numElemX+1;
83  c_vector<double, DIM> top;
84  top[0] = numElemX;
85 
86  unsigned mid_node_index=mNumVertices;
87  for (unsigned element_index=0; element_index<numElemX; element_index++)
88  {
89  c_vector<double, DIM> x_value_mid_node;
90  x_value_mid_node[0] = element_index+0.5;
91 
92  Node<DIM>* p_mid_node = MakeNewInternalNode(mid_node_index, x_value_mid_node, top);
93 
94  //Put in element and cross-reference
95  this->mElements[element_index]->AddNode(p_mid_node);
96  p_mid_node->AddElement(element_index);
97  }
98 
99  this->RefreshMesh();
100 }
101 
102 
103 
104 
105 
106 template<unsigned DIM>
107 void QuadraticMesh<DIM>::ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool stagger)
108 {
109  assert(DIM==2);
110  assert(numElemX > 0);
111  assert(numElemY > 0);
112 
114 
115  this->mMeshIsLinear=false;
116  //Make the internal nodes in y-order. This is important for the distributed case, since we want the top and bottom
117  //layers to have predictable numbers
118  std::map<std::pair<unsigned, unsigned>, unsigned> edge_to_internal_map;
119 
120  unsigned node_index = this->GetNumNodes();
121  c_vector<double, DIM> top;
122  top[0]=numElemX;
123  top[1]=numElemY;
124  c_vector<double, DIM> node_pos;
125 
126  for (unsigned j=0; j<numElemY+1; j++)
127  {
128  node_pos[1]=j;
129  //Add mid-way nodes to horizontal edges in this slice
130  for (unsigned i=0; i<numElemX; i++)
131  {
132  unsigned left_index = j*(numElemX+1) + i;
133  std::pair<unsigned,unsigned> edge(left_index, left_index+1 ) ;
134  edge_to_internal_map[edge] = node_index;
135  node_pos[0]=i+0.5;
136  MakeNewInternalNode(node_index, node_pos, top);
137  }
138 
139  //Add the vertical and diagonal nodes to the mid-way above the last set of horizontal edges
140  node_pos[1] = j+0.5;
141  for (unsigned i=0; i<numElemX+1; i++)
142  {
143  node_pos[0] = i;
144  unsigned left_index = j*(numElemX+1) + i;
145  std::pair<unsigned,unsigned> edge(left_index, left_index+(numElemX+1) ) ;
146  edge_to_internal_map[edge] = node_index;
147  MakeNewInternalNode(node_index, node_pos, top);
148  unsigned parity=(i+(numElemY-j))%2;
149  if (stagger==false || parity==1) //Default when no stagger
150  {
151  //backslash
152  std::pair<unsigned,unsigned> back_edge(left_index+1, left_index+(numElemX+1) ) ;
153  edge_to_internal_map[back_edge] = node_index;
154  }
155  else
156  {
157  //foward slash
158  std::pair<unsigned,unsigned> forward_edge(left_index, left_index+(numElemX+1)+1 ) ;
159  edge_to_internal_map[forward_edge] = node_index;
160  }
161  node_pos[0] = i+0.5;
162  MakeNewInternalNode(node_index, node_pos, top);
163  }
164  }
165  CountVertices();
166 
167 // assert(edge_to_internal_map.size() == this->GetNumNodes()-this->GetNumVertices());
168  for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->GetElementIteratorBegin();
169  iter != this->GetElementIteratorEnd();
170  ++iter)
171  {
172  unsigned local_index1=0;
173  for (unsigned index=0; index<=DIM; index++)
174  {
175  local_index1 = (local_index1+1)%(DIM+1);
176  unsigned local_index2 = (local_index1+1)%(DIM+1);
177  unsigned global_index1 = iter->GetNodeGlobalIndex(local_index1);
178  unsigned global_index2 = iter->GetNodeGlobalIndex(local_index2);
179  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
180  iter->AddNode(this->mNodes[new_node_index]);
181  this->mNodes[new_node_index]->AddElement(iter->GetIndex());
182  }
183  }
184 
185  for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter = this->GetBoundaryElementIteratorBegin();
186  iter != this->GetBoundaryElementIteratorEnd();
187  ++iter)
188  {
189  unsigned global_index1 = (*iter)->GetNodeGlobalIndex(0);
190  unsigned global_index2 = (*iter)->GetNodeGlobalIndex(1);
191  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
192  (*iter)->AddNode(this->mNodes[new_node_index]);
193  this->mNodes[new_node_index]->AddBoundaryElement((*iter)->GetIndex());
194  }
195 
196  this->RefreshMesh();
197 }
198 
199 template<unsigned DIM>
200 Node<DIM>* QuadraticMesh<DIM>::MakeNewInternalNode(unsigned& rIndex, c_vector<double, DIM>& rLocation, c_vector<double, DIM>& rTop)
201 {
202  bool boundary = false;
203  for (unsigned dim=0; dim<DIM; dim++)
204  {
205  if (rLocation[dim] > rTop[dim])
206  {
207  //Outside the box so don't do anything
208  return NULL;
209  }
210  if ( (rLocation[dim] == 0.0) || (rLocation[dim] == rTop[dim]) )
211  {
212  boundary = true;
213  }
214  }
215  //The caller needs to know that rIndex is in sync with what's in the mesh
216  assert(rIndex == this->mNodes.size());
217  Node<DIM>* p_node = new Node<DIM>(rIndex++, rLocation, boundary);
218  p_node->MarkAsInternal();
219  //Put in mesh
220  this->mNodes.push_back(p_node);
221  if (boundary)
222  {
223  this->mBoundaryNodes.push_back(p_node);
224  }
225  return p_node;
226 }
227 
228 template<unsigned DIM>
229 unsigned QuadraticMesh<DIM>::LookupInternalNode(unsigned globalIndex1, unsigned globalIndex2, std::map<std::pair<unsigned, unsigned>, unsigned>& rEdgeMap)
230 {
231  unsigned node_index = 0u;
232  assert(globalIndex1 != globalIndex2);
233  if (globalIndex1 < globalIndex2)
234  {
235  node_index = rEdgeMap[std::pair<unsigned,unsigned>(globalIndex1, globalIndex2)];
236  }
237  else
238  {
239  node_index = rEdgeMap[std::pair<unsigned,unsigned>(globalIndex2, globalIndex1)];
240  }
241  //A failure to find the key would result in a new zero entry in the map. Note that no *internal* node will have global index zero.
242  assert(node_index != 0u);
243  return node_index;
244 }
245 
246 template<unsigned DIM>
247 void QuadraticMesh<DIM>::ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
248 {
249  assert(DIM==3);
250 
251  assert(numElemX > 0);
252  assert(numElemY > 0);
253  assert(numElemZ > 0);
254 
255  AbstractTetrahedralMesh<DIM,DIM>::ConstructCuboid(numElemX, numElemY, numElemZ);
256  c_vector<double, DIM> top;
257  top[0]=numElemX;
258  top[1]=numElemY;
259  top[2]=numElemZ;
260  c_vector<double, DIM> node_pos;
261  this->mMeshIsLinear=false;
262  //Make the internal nodes in z-order. This is important for the distributed case, since we want the top and bottom
263  //layers to have predictable numbers
264  std::map<std::pair<unsigned, unsigned>, unsigned> edge_to_internal_map;
265  unsigned node_index = this->GetNumNodes();
266  for (unsigned k=0; k<numElemZ+1; k++)
267  {
268  //Add a slice of the mid-points to the edges and faces at this z=level
269  node_pos[2] = k;
270  for (unsigned j=0; j<numElemY+1; j++)
271  {
272  unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
273  unsigned lo_z_hi_y = (numElemX+1)*((numElemY+1)*k + j + 1);
274 
275  node_pos[1] = j;
276 
277  //The midpoints along the horizontal (y fixed) edges
278  for (unsigned i=0; i<numElemX+1; i++)
279  {
280  // i+.5, j, k
281  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_lo_y+i+1);
282  edge_to_internal_map[edge] = node_index;
283  node_pos[0] = i+0.5;
284  MakeNewInternalNode(node_index, node_pos, top);
285  }
286  //The midpoints and face centres between two horizontal (y-fixed) strips
287  node_pos[1] = j+0.5;
288  for (unsigned i=0; i<numElemX+1; i++)
289  {
290  // i, j+0.5, k
291  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_hi_y+i);
292  edge_to_internal_map[edge] = node_index;
293  node_pos[0] = i;
294  MakeNewInternalNode(node_index, node_pos, top);
295  //Centre of face node
296  // i+0.5, j+0.5, k
297  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, lo_z_hi_y+i+1);
298  edge_to_internal_map[edge2] = node_index;
299  node_pos[0] = i+0.5;
300  MakeNewInternalNode(node_index, node_pos, top);
301  }
302  }
303  //Add a slice of the mid-points to the edges and faces mid-way up the cube z=level
304  node_pos[2] = k+0.5;
305  for (unsigned j=0; j<numElemY+1; j++)
306  {
307  node_pos[1] = j;
308  unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
309  unsigned hi_z_lo_y = (numElemX+1)*((numElemY+1)*(k+1) + j);
310  unsigned hi_z_hi_y = (numElemX+1)*((numElemY+1)*(k+1) + j + 1);
311 
312  //The midpoints along the horizontal (y fixed) edges
313  for (unsigned i=0; i<numElemX+1; i++)
314  {
315  // i, j, k+0.5
316  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_lo_y+i);
317  edge_to_internal_map[edge] = node_index;
318  node_pos[0] = i;
319  MakeNewInternalNode(node_index, node_pos, top);
320 
321  // i+0.5, j, k+0.5
322  node_pos[0] = i+0.5;
323  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_lo_y+i+1);
324  edge_to_internal_map[edge2] = node_index;
325  MakeNewInternalNode(node_index, node_pos, top);
326  }
327  //The midpoints and face centres between two horizontal (y-fixed) strips
328  node_pos[1] = j+0.5;
329  for (unsigned i=0; i<numElemX+1; i++)
330  {
331  // i, j+0.5, k+0.5
332  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_hi_y+i);
333  edge_to_internal_map[edge] = node_index;
334  node_pos[0] = i;
335  MakeNewInternalNode(node_index, node_pos, top);
336  //Centre of face node on the main diagonal
337  // i+0.5, j+0.5, k+0.5
338  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_hi_y+i+1);
339  edge_to_internal_map[edge2] = node_index;
340  node_pos[0] = i+0.5;
341  MakeNewInternalNode(node_index, node_pos, top);
342  }
343  }
344 
345  }
346  CountVertices();
347  for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->GetElementIteratorBegin();
348  iter != this->GetElementIteratorEnd();
349  ++iter)
350  {
351  /* The standard tetgen ordering of the internal nodes 4,5,6..9 (using the
352  * zero-based numbering scheme) is
353  * 4 (0,1), 5 (1,2), 6 (0,2) 7 (0,3), 8 (1,3), 9 (2,3)
354  * i.e. internal node with local index 4 is half-way between vertex nodes
355  * with local indices 0 and 1.
356  */
357  unsigned v0=iter->GetNodeGlobalIndex(0);
358  unsigned v1=iter->GetNodeGlobalIndex(1);
359  unsigned v2=iter->GetNodeGlobalIndex(2);
360  unsigned v3=iter->GetNodeGlobalIndex(3);
361  unsigned internal_index;
362 
363  //4
364  internal_index=LookupInternalNode(v0, v1, edge_to_internal_map);
365  iter->AddNode(this->mNodes[internal_index]);
366  this->mNodes[internal_index]->AddElement(iter->GetIndex());
367  //5
368  internal_index=LookupInternalNode(v1, v2, edge_to_internal_map);
369  iter->AddNode(this->mNodes[internal_index]);
370  this->mNodes[internal_index]->AddElement(iter->GetIndex());
371  //6
372  internal_index=LookupInternalNode(v0, v2, edge_to_internal_map);
373  iter->AddNode(this->mNodes[internal_index]);
374  this->mNodes[internal_index]->AddElement(iter->GetIndex());
375  //7
376  internal_index=LookupInternalNode(v0, v3, edge_to_internal_map);
377  iter->AddNode(this->mNodes[internal_index]);
378  this->mNodes[internal_index]->AddElement(iter->GetIndex());
379  //8
380  internal_index=LookupInternalNode(v1, v3, edge_to_internal_map);
381  iter->AddNode(this->mNodes[internal_index]);
382  this->mNodes[internal_index]->AddElement(iter->GetIndex());
383  //9
384  internal_index=LookupInternalNode(v2, v3, edge_to_internal_map);
385  iter->AddNode(this->mNodes[internal_index]);
386  this->mNodes[internal_index]->AddElement(iter->GetIndex());
387 
388  }
389  for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter = this->GetBoundaryElementIteratorBegin();
390  iter != this->GetBoundaryElementIteratorEnd();
391  ++iter)
392  {
393  unsigned local_index1=0;
394  for (unsigned index=0; index<DIM; index++)
395  {
396  local_index1 = (local_index1+1)%(DIM);
397  unsigned local_index2 = (local_index1+1)%(DIM);
398  unsigned global_index1 = (*iter)->GetNodeGlobalIndex(local_index1);
399  unsigned global_index2 = (*iter)->GetNodeGlobalIndex(local_index2);
400  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
401  (*iter)->AddNode(this->mNodes[new_node_index]);
402  this->mNodes[new_node_index]->AddBoundaryElement((*iter)->GetIndex());
403  }
404 
405  }
406  this->RefreshMesh();
407 }
408 
409 
410 
411 template<unsigned DIM>
413 {
414  return mNumVertices;
415 }
416 
417 
418 template<unsigned DIM>
420 {
421  assert(DIM != 1);
422 
423  //Make a linear mesh
425 
426  NodeMap unused_map(this->GetNumNodes());
427 
428  if (DIM==2) // In 2D, remesh using triangle via library calls
429  {
430  struct triangulateio mesher_input, mesher_output;
431  this->InitialiseTriangulateIo(mesher_input);
432  this->InitialiseTriangulateIo(mesher_output);
433 
434  mesher_input.numberoftriangles = this->GetNumElements();
435  mesher_input.trianglelist = (int *) malloc(this->GetNumElements() * (DIM+1) * sizeof(int));
436  this->ExportToMesher(unused_map, mesher_input, mesher_input.trianglelist);
437 
438  // Library call
439  triangulate((char*)"Qzero2", &mesher_input, &mesher_output, NULL);
440 
441  this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
442  CountVertices();
444 
445  //Tidy up triangle
446  this->FreeTriangulateIo(mesher_input);
447  this->FreeTriangulateIo(mesher_output);
448  }
449  else // in 3D, remesh using tetgen
450  {
451 
452  class tetgen::tetgenio mesher_input, mesher_output;
453 
454  mesher_input.numberoftetrahedra = this->GetNumElements();
455  mesher_input.tetrahedronlist = new int[this->GetNumElements() * (DIM+1)];
456  this->ExportToMesher(unused_map, mesher_input, mesher_input.tetrahedronlist);
457 
458  // Library call
459  tetgen::tetrahedralize((char*)"Qzro2", &mesher_input, &mesher_output);
460 
461  this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, NULL);
462  CountVertices();
464  }
465 }
466 
467 
468 template<unsigned DIM>
470 {
471  //Some mesh readers will let you read with non-linear elements
472  unsigned order_of_elements = rAbsMeshReader.GetOrderOfElements();
473 
474  // If it is a linear mesh reader
475  if (order_of_elements == 1)
476  {
477  WARNING("Reading a (linear) tetrahedral mesh and converting it to a QuadraticMesh. This involves making an external library call to Triangle/Tetgen in order to compute internal nodes");
478  ConstructFromLinearMeshReader(rAbsMeshReader);
479  return;
480  }
481 
483  assert(this->GetNumBoundaryElements() > 0);
484 
486  CountVertices();
489 }
490 
492 // Explicit instantiation
494 
495 
496 template class QuadraticMesh<1>;
497 template class QuadraticMesh<2>;
498 template class QuadraticMesh<3>;
499 
500 
501 // Serialization for Boost >= 1.36
static void AddNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
void CountVertices()
static void AddInternalNodesToElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
Definition: Node.hpp:58
void MarkAsInternal()
Definition: Node.cpp:359
static void AddInternalNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
virtual unsigned GetOrderOfElements()
void ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
Node< DIM > * MakeNewInternalNode(unsigned &rIndex, c_vector< double, DIM > &rLocation, c_vector< double, DIM > &rTop)
void ConstructFromMeshReader(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader)
void ConstructLinearMesh(unsigned numElemX)
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
unsigned LookupInternalNode(unsigned globalIndex1, unsigned globalIndex2, std::map< std::pair< unsigned, unsigned >, unsigned > &rEdgeMap)
virtual void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
virtual void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
void ConstructFromLinearMeshReader(AbstractMeshReader< DIM, DIM > &rMeshReader)
void AddElement(unsigned index)
Definition: Node.cpp:269
void ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool stagger=true)
void ConstructFromMeshReader(AbstractMeshReader< DIM, DIM > &rMeshReader)
virtual void ConstructLinearMesh(unsigned width)
static void CheckBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh)
unsigned GetNumVertices() const