Chaste  Release::2017.1
QuadraticMesh.cpp
1 /*
2 
3 Copyright (c) 2005-2017, 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); // LCOV_EXCL_LINE
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 template<unsigned DIM>
103 void QuadraticMesh<DIM>::ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool stagger)
104 {
105  assert(DIM==2); // LCOV_EXCL_LINE
106  assert(numElemX > 0);
107  assert(numElemY > 0);
108 
110 
111  this->mMeshIsLinear=false;
112  //Make the internal nodes in y-order. This is important for the distributed case, since we want the top and bottom
113  //layers to have predictable numbers
114  std::map<std::pair<unsigned, unsigned>, unsigned> edge_to_internal_map;
115 
116  unsigned node_index = this->GetNumNodes();
117  c_vector<double, DIM> top;
118  top[0]=numElemX;
119  top[1]=numElemY;
120  c_vector<double, DIM> node_pos;
121 
122  for (unsigned j=0; j<numElemY+1; j++)
123  {
124  node_pos[1]=j;
125  //Add mid-way nodes to horizontal edges in this slice
126  for (unsigned i=0; i<numElemX; i++)
127  {
128  unsigned left_index = j*(numElemX+1) + i;
129  std::pair<unsigned,unsigned> edge(left_index, left_index+1 );
130  edge_to_internal_map[edge] = node_index;
131  node_pos[0]=i+0.5;
132  MakeNewInternalNode(node_index, node_pos, top);
133  }
134 
135  //Add the vertical and diagonal nodes to the mid-way above the last set of horizontal edges
136  node_pos[1] = j+0.5;
137  for (unsigned i=0; i<numElemX+1; i++)
138  {
139  node_pos[0] = i;
140  unsigned left_index = j*(numElemX+1) + i;
141  std::pair<unsigned,unsigned> edge(left_index, left_index+(numElemX+1) );
142  edge_to_internal_map[edge] = node_index;
143  MakeNewInternalNode(node_index, node_pos, top);
144  unsigned parity=(i+(numElemY-j))%2;
145  if (stagger==false || parity==1) //Default when no stagger
146  {
147  //backslash
148  std::pair<unsigned,unsigned> back_edge(left_index+1, left_index+(numElemX+1) );
149  edge_to_internal_map[back_edge] = node_index;
150  }
151  else
152  {
153  //foward slash
154  std::pair<unsigned,unsigned> forward_edge(left_index, left_index+(numElemX+1)+1 );
155  edge_to_internal_map[forward_edge] = node_index;
156  }
157  node_pos[0] = i+0.5;
158  MakeNewInternalNode(node_index, node_pos, top);
159  }
160  }
161  CountVertices();
162 
163 // assert(edge_to_internal_map.size() == this->GetNumNodes()-this->GetNumVertices());
164  for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->GetElementIteratorBegin();
165  iter != this->GetElementIteratorEnd();
166  ++iter)
167  {
168  unsigned local_index1=0;
169  for (unsigned index=0; index<=DIM; index++)
170  {
171  local_index1 = (local_index1+1)%(DIM+1);
172  unsigned local_index2 = (local_index1+1)%(DIM+1);
173  unsigned global_index1 = iter->GetNodeGlobalIndex(local_index1);
174  unsigned global_index2 = iter->GetNodeGlobalIndex(local_index2);
175  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
176  iter->AddNode(this->mNodes[new_node_index]);
177  this->mNodes[new_node_index]->AddElement(iter->GetIndex());
178  }
179  }
180 
181  for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter = this->GetBoundaryElementIteratorBegin();
182  iter != this->GetBoundaryElementIteratorEnd();
183  ++iter)
184  {
185  unsigned global_index1 = (*iter)->GetNodeGlobalIndex(0);
186  unsigned global_index2 = (*iter)->GetNodeGlobalIndex(1);
187  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
188  (*iter)->AddNode(this->mNodes[new_node_index]);
189  this->mNodes[new_node_index]->AddBoundaryElement((*iter)->GetIndex());
190  }
191 
192  this->RefreshMesh();
193 }
194 
195 template<unsigned DIM>
196 Node<DIM>* QuadraticMesh<DIM>::MakeNewInternalNode(unsigned& rIndex, c_vector<double, DIM>& rLocation, c_vector<double, DIM>& rTop)
197 {
198  bool boundary = false;
199  for (unsigned dim=0; dim<DIM; dim++)
200  {
201  if (rLocation[dim] > rTop[dim])
202  {
203  //Outside the box so don't do anything
204  return nullptr;
205  }
206  if ((rLocation[dim] == 0.0) || (rLocation[dim] == rTop[dim]))
207  {
208  boundary = true;
209  }
210  }
211  //The caller needs to know that rIndex is in sync with what's in the mesh
212  assert(rIndex == this->mNodes.size());
213  Node<DIM>* p_node = new Node<DIM>(rIndex++, rLocation, boundary);
214  p_node->MarkAsInternal();
215  //Put in mesh
216  this->mNodes.push_back(p_node);
217  if (boundary)
218  {
219  this->mBoundaryNodes.push_back(p_node);
220  }
221  return p_node;
222 }
223 
224 template<unsigned DIM>
225 unsigned QuadraticMesh<DIM>::LookupInternalNode(unsigned globalIndex1, unsigned globalIndex2, std::map<std::pair<unsigned, unsigned>, unsigned>& rEdgeMap)
226 {
227  unsigned node_index = 0u;
228  assert(globalIndex1 != globalIndex2);
229  if (globalIndex1 < globalIndex2)
230  {
231  node_index = rEdgeMap[std::pair<unsigned,unsigned>(globalIndex1, globalIndex2)];
232  }
233  else
234  {
235  node_index = rEdgeMap[std::pair<unsigned,unsigned>(globalIndex2, globalIndex1)];
236  }
237  //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.
238  assert(node_index != 0u);
239  return node_index;
240 }
241 
242 template<unsigned DIM>
243 void QuadraticMesh<DIM>::ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
244 {
245  assert(DIM==3); // LCOV_EXCL_LINE
246 
247  assert(numElemX > 0);
248  assert(numElemY > 0);
249  assert(numElemZ > 0);
250 
251  AbstractTetrahedralMesh<DIM,DIM>::ConstructCuboid(numElemX, numElemY, numElemZ);
252  c_vector<double, DIM> top;
253  top[0]=numElemX;
254  top[1]=numElemY;
255  top[2]=numElemZ;
256  c_vector<double, DIM> node_pos;
257  this->mMeshIsLinear=false;
258  //Make the internal nodes in z-order. This is important for the distributed case, since we want the top and bottom
259  //layers to have predictable numbers
260  std::map<std::pair<unsigned, unsigned>, unsigned> edge_to_internal_map;
261  unsigned node_index = this->GetNumNodes();
262  for (unsigned k=0; k<numElemZ+1; k++)
263  {
264  //Add a slice of the mid-points to the edges and faces at this z=level
265  node_pos[2] = k;
266  for (unsigned j=0; j<numElemY+1; j++)
267  {
268  unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
269  unsigned lo_z_hi_y = (numElemX+1)*((numElemY+1)*k + j + 1);
270 
271  node_pos[1] = j;
272 
273  //The midpoints along the horizontal (y fixed) edges
274  for (unsigned i=0; i<numElemX+1; i++)
275  {
276  // i+.5, j, k
277  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_lo_y+i+1);
278  edge_to_internal_map[edge] = node_index;
279  node_pos[0] = i+0.5;
280  MakeNewInternalNode(node_index, node_pos, top);
281  }
282  //The midpoints and face centres between two horizontal (y-fixed) strips
283  node_pos[1] = j+0.5;
284  for (unsigned i=0; i<numElemX+1; i++)
285  {
286  // i, j+0.5, k
287  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_hi_y+i);
288  edge_to_internal_map[edge] = node_index;
289  node_pos[0] = i;
290  MakeNewInternalNode(node_index, node_pos, top);
291  //Centre of face node
292  // i+0.5, j+0.5, k
293  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, lo_z_hi_y+i+1);
294  edge_to_internal_map[edge2] = node_index;
295  node_pos[0] = i+0.5;
296  MakeNewInternalNode(node_index, node_pos, top);
297  }
298  }
299  //Add a slice of the mid-points to the edges and faces mid-way up the cube z=level
300  node_pos[2] = k+0.5;
301  for (unsigned j=0; j<numElemY+1; j++)
302  {
303  node_pos[1] = j;
304  unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
305  unsigned hi_z_lo_y = (numElemX+1)*((numElemY+1)*(k+1) + j);
306  unsigned hi_z_hi_y = (numElemX+1)*((numElemY+1)*(k+1) + j + 1);
307 
308  //The midpoints along the horizontal (y fixed) edges
309  for (unsigned i=0; i<numElemX+1; i++)
310  {
311  // i, j, k+0.5
312  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_lo_y+i);
313  edge_to_internal_map[edge] = node_index;
314  node_pos[0] = i;
315  MakeNewInternalNode(node_index, node_pos, top);
316 
317  // i+0.5, j, k+0.5
318  node_pos[0] = i+0.5;
319  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_lo_y+i+1);
320  edge_to_internal_map[edge2] = node_index;
321  MakeNewInternalNode(node_index, node_pos, top);
322  }
323  //The midpoints and face centres between two horizontal (y-fixed) strips
324  node_pos[1] = j+0.5;
325  for (unsigned i=0; i<numElemX+1; i++)
326  {
327  // i, j+0.5, k+0.5
328  std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_hi_y+i);
329  edge_to_internal_map[edge] = node_index;
330  node_pos[0] = i;
331  MakeNewInternalNode(node_index, node_pos, top);
332  //Centre of face node on the main diagonal
333  // i+0.5, j+0.5, k+0.5
334  std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_hi_y+i+1);
335  edge_to_internal_map[edge2] = node_index;
336  node_pos[0] = i+0.5;
337  MakeNewInternalNode(node_index, node_pos, top);
338  }
339  }
340  }
341  CountVertices();
342  for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->GetElementIteratorBegin();
343  iter != this->GetElementIteratorEnd();
344  ++iter)
345  {
346  /* The standard tetgen ordering of the internal nodes 4,5,6..9 (using the
347  * zero-based numbering scheme) is
348  * 4 (0,1), 5 (1,2), 6 (0,2) 7 (0,3), 8 (1,3), 9 (2,3)
349  * i.e. internal node with local index 4 is half-way between vertex nodes
350  * with local indices 0 and 1.
351  */
352  unsigned v0 = iter->GetNodeGlobalIndex(0);
353  unsigned v1 = iter->GetNodeGlobalIndex(1);
354  unsigned v2 = iter->GetNodeGlobalIndex(2);
355  unsigned v3 = iter->GetNodeGlobalIndex(3);
356  unsigned internal_index;
357 
358  //4
359  internal_index=LookupInternalNode(v0, v1, edge_to_internal_map);
360  iter->AddNode(this->mNodes[internal_index]);
361  this->mNodes[internal_index]->AddElement(iter->GetIndex());
362  //5
363  internal_index=LookupInternalNode(v1, v2, edge_to_internal_map);
364  iter->AddNode(this->mNodes[internal_index]);
365  this->mNodes[internal_index]->AddElement(iter->GetIndex());
366  //6
367  internal_index=LookupInternalNode(v0, v2, edge_to_internal_map);
368  iter->AddNode(this->mNodes[internal_index]);
369  this->mNodes[internal_index]->AddElement(iter->GetIndex());
370  //7
371  internal_index=LookupInternalNode(v0, v3, edge_to_internal_map);
372  iter->AddNode(this->mNodes[internal_index]);
373  this->mNodes[internal_index]->AddElement(iter->GetIndex());
374  //8
375  internal_index=LookupInternalNode(v1, v3, edge_to_internal_map);
376  iter->AddNode(this->mNodes[internal_index]);
377  this->mNodes[internal_index]->AddElement(iter->GetIndex());
378  //9
379  internal_index=LookupInternalNode(v2, v3, edge_to_internal_map);
380  iter->AddNode(this->mNodes[internal_index]);
381  this->mNodes[internal_index]->AddElement(iter->GetIndex());
382 
383  }
384  for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter = this->GetBoundaryElementIteratorBegin();
385  iter != this->GetBoundaryElementIteratorEnd();
386  ++iter)
387  {
388  unsigned local_index1=0;
389  for (unsigned index=0; index<DIM; index++)
390  {
391  local_index1 = (local_index1+1)%(DIM);
392  unsigned local_index2 = (local_index1+1)%(DIM);
393  unsigned global_index1 = (*iter)->GetNodeGlobalIndex(local_index1);
394  unsigned global_index2 = (*iter)->GetNodeGlobalIndex(local_index2);
395  unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
396  (*iter)->AddNode(this->mNodes[new_node_index]);
397  this->mNodes[new_node_index]->AddBoundaryElement((*iter)->GetIndex());
398  }
399  }
400  this->RefreshMesh();
401 }
402 
403 template<unsigned DIM>
405 {
406  return mNumVertices;
407 }
408 
409 template<unsigned DIM>
411 {
412  assert(DIM != 1); // LCOV_EXCL_LINE
413 
414  //Make a linear mesh
416 
417  NodeMap unused_map(this->GetNumNodes());
418 
419  if (DIM==2) // In 2D, remesh using triangle via library calls
420  {
421  struct triangulateio mesher_input, mesher_output;
422  this->InitialiseTriangulateIo(mesher_input);
423  this->InitialiseTriangulateIo(mesher_output);
424 
425  mesher_input.numberoftriangles = this->GetNumElements();
426  mesher_input.trianglelist = (int *) malloc(this->GetNumElements() * (DIM+1) * sizeof(int));
427  this->ExportToMesher(unused_map, mesher_input, mesher_input.trianglelist);
428 
429  // Library call
430  triangulate((char*)"Qzero2", &mesher_input, &mesher_output, nullptr);
431 
432  this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
433  CountVertices();
435 
436  //Tidy up triangle
437  this->FreeTriangulateIo(mesher_input);
438  this->FreeTriangulateIo(mesher_output);
439  }
440  else // in 3D, remesh using tetgen
441  {
442 
443  class tetgen::tetgenio mesher_input, mesher_output;
444 
445  mesher_input.numberoftetrahedra = this->GetNumElements();
446  mesher_input.tetrahedronlist = new int[this->GetNumElements() * (DIM+1)];
447  this->ExportToMesher(unused_map, mesher_input, mesher_input.tetrahedronlist);
448 
449  // Library call
450  tetgen::tetrahedralize((char*)"Qzro2", &mesher_input, &mesher_output);
451 
452  this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, nullptr);
453  CountVertices();
455  }
456 }
457 
458 template<unsigned DIM>
460 {
461  //Some mesh readers will let you read with non-linear elements
462  unsigned order_of_elements = rAbsMeshReader.GetOrderOfElements();
463 
464  // If it is a linear mesh reader
465  if (order_of_elements == 1)
466  {
467  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");
468  ConstructFromLinearMeshReader(rAbsMeshReader);
469  return;
470  }
471 
473  assert(this->GetNumBoundaryElements() > 0);
474 
476  CountVertices();
479 }
480 
482 
483 
484 template class QuadraticMesh<1>;
485 template class QuadraticMesh<2>;
486 template class QuadraticMesh<3>;
487 
488 
489 // 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:418
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:268
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