Chaste Commit::baa90ac2819b962188b7562f2326be23c47859a7
QuadraticMesh.cpp
1/*
2
3Copyright (c) 2005-2024, 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 "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
50template<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
64template<unsigned DIM>
65QuadraticMesh<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
75template<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
102template<unsigned DIM>
103void QuadraticMesh<DIM>::ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool stagger)
104{
105 if (DIM != 2)
106 {
107 EXCEPTION("This cuboid construction is only valid in 3D"); // LCOV_EXCL_LINE
108 }
109
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
199template<unsigned DIM>
200Node<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 nullptr;
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
228template<unsigned DIM>
229unsigned 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
246template<unsigned DIM>
247void QuadraticMesh<DIM>::ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
248{
249 if (DIM != 3)
250 {
251 EXCEPTION("This cuboid construction is only valid in 3D"); // LCOV_EXCL_LINE
252 }
253
254 assert(numElemX > 0);
255 assert(numElemY > 0);
256 assert(numElemZ > 0);
257
258 AbstractTetrahedralMesh<DIM,DIM>::ConstructCuboid(numElemX, numElemY, numElemZ);
259 c_vector<double, DIM> top;
260 top[0]=numElemX;
261 top[1]=numElemY;
262 top[2]=numElemZ;
263 c_vector<double, DIM> node_pos;
264 this->mMeshIsLinear=false;
265 //Make the internal nodes in z-order. This is important for the distributed case, since we want the top and bottom
266 //layers to have predictable numbers
267 std::map<std::pair<unsigned, unsigned>, unsigned> edge_to_internal_map;
268 unsigned node_index = this->GetNumNodes();
269 for (unsigned k=0; k<numElemZ+1; k++)
270 {
271 //Add a slice of the mid-points to the edges and faces at this z=level
272 node_pos[2] = k;
273 for (unsigned j=0; j<numElemY+1; j++)
274 {
275 unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
276 unsigned lo_z_hi_y = (numElemX+1)*((numElemY+1)*k + j + 1);
277
278 node_pos[1] = j;
279
280 //The midpoints along the horizontal (y fixed) edges
281 for (unsigned i=0; i<numElemX+1; i++)
282 {
283 // i+.5, j, k
284 std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_lo_y+i+1);
285 edge_to_internal_map[edge] = node_index;
286 node_pos[0] = i+0.5;
287 MakeNewInternalNode(node_index, node_pos, top);
288 }
289 //The midpoints and face centres between two horizontal (y-fixed) strips
290 node_pos[1] = j+0.5;
291 for (unsigned i=0; i<numElemX+1; i++)
292 {
293 // i, j+0.5, k
294 std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, lo_z_hi_y+i);
295 edge_to_internal_map[edge] = node_index;
296 node_pos[0] = i;
297 MakeNewInternalNode(node_index, node_pos, top);
298 //Centre of face node
299 // i+0.5, j+0.5, k
300 std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, lo_z_hi_y+i+1);
301 edge_to_internal_map[edge2] = node_index;
302 node_pos[0] = i+0.5;
303 MakeNewInternalNode(node_index, node_pos, top);
304 }
305 }
306 //Add a slice of the mid-points to the edges and faces mid-way up the cube z=level
307 node_pos[2] = k+0.5;
308 for (unsigned j=0; j<numElemY+1; j++)
309 {
310 node_pos[1] = j;
311 unsigned lo_z_lo_y = (numElemX+1)*((numElemY+1)*k + j);
312 unsigned hi_z_lo_y = (numElemX+1)*((numElemY+1)*(k+1) + j);
313 unsigned hi_z_hi_y = (numElemX+1)*((numElemY+1)*(k+1) + j + 1);
314
315 //The midpoints along the horizontal (y fixed) edges
316 for (unsigned i=0; i<numElemX+1; i++)
317 {
318 // i, j, k+0.5
319 std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_lo_y+i);
320 edge_to_internal_map[edge] = node_index;
321 node_pos[0] = i;
322 MakeNewInternalNode(node_index, node_pos, top);
323
324 // i+0.5, j, k+0.5
325 node_pos[0] = i+0.5;
326 std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_lo_y+i+1);
327 edge_to_internal_map[edge2] = node_index;
328 MakeNewInternalNode(node_index, node_pos, top);
329 }
330 //The midpoints and face centres between two horizontal (y-fixed) strips
331 node_pos[1] = j+0.5;
332 for (unsigned i=0; i<numElemX+1; i++)
333 {
334 // i, j+0.5, k+0.5
335 std::pair<unsigned,unsigned> edge(lo_z_lo_y+i, hi_z_hi_y+i);
336 edge_to_internal_map[edge] = node_index;
337 node_pos[0] = i;
338 MakeNewInternalNode(node_index, node_pos, top);
339 //Centre of face node on the main diagonal
340 // i+0.5, j+0.5, k+0.5
341 std::pair<unsigned,unsigned> edge2(lo_z_lo_y+i, hi_z_hi_y+i+1);
342 edge_to_internal_map[edge2] = node_index;
343 node_pos[0] = i+0.5;
344 MakeNewInternalNode(node_index, node_pos, top);
345 }
346 }
347 }
348 CountVertices();
349 for (typename AbstractTetrahedralMesh<DIM,DIM>::ElementIterator iter = this->GetElementIteratorBegin();
350 iter != this->GetElementIteratorEnd();
351 ++iter)
352 {
353 /* The standard tetgen ordering of the internal nodes 4,5,6..9 (using the
354 * zero-based numbering scheme) is
355 * 4 (0,1), 5 (1,2), 6 (0,2) 7 (0,3), 8 (1,3), 9 (2,3)
356 * i.e. internal node with local index 4 is half-way between vertex nodes
357 * with local indices 0 and 1.
358 */
359 unsigned v0 = iter->GetNodeGlobalIndex(0);
360 unsigned v1 = iter->GetNodeGlobalIndex(1);
361 unsigned v2 = iter->GetNodeGlobalIndex(2);
362 unsigned v3 = iter->GetNodeGlobalIndex(3);
363 unsigned internal_index;
364
365 //4
366 internal_index=LookupInternalNode(v0, v1, edge_to_internal_map);
367 iter->AddNode(this->mNodes[internal_index]);
368 this->mNodes[internal_index]->AddElement(iter->GetIndex());
369 //5
370 internal_index=LookupInternalNode(v1, v2, edge_to_internal_map);
371 iter->AddNode(this->mNodes[internal_index]);
372 this->mNodes[internal_index]->AddElement(iter->GetIndex());
373 //6
374 internal_index=LookupInternalNode(v0, v2, edge_to_internal_map);
375 iter->AddNode(this->mNodes[internal_index]);
376 this->mNodes[internal_index]->AddElement(iter->GetIndex());
377 //7
378 internal_index=LookupInternalNode(v0, v3, edge_to_internal_map);
379 iter->AddNode(this->mNodes[internal_index]);
380 this->mNodes[internal_index]->AddElement(iter->GetIndex());
381 //8
382 internal_index=LookupInternalNode(v1, v3, edge_to_internal_map);
383 iter->AddNode(this->mNodes[internal_index]);
384 this->mNodes[internal_index]->AddElement(iter->GetIndex());
385 //9
386 internal_index=LookupInternalNode(v2, v3, edge_to_internal_map);
387 iter->AddNode(this->mNodes[internal_index]);
388 this->mNodes[internal_index]->AddElement(iter->GetIndex());
389
390 }
391 for (typename AbstractTetrahedralMesh<DIM,DIM>::BoundaryElementIterator iter = this->GetBoundaryElementIteratorBegin();
392 iter != this->GetBoundaryElementIteratorEnd();
393 ++iter)
394 {
395 unsigned local_index1=0;
396 for (unsigned index=0; index<DIM; index++)
397 {
398 local_index1 = (local_index1+1)%(DIM);
399 unsigned local_index2 = (local_index1+1)%(DIM);
400 unsigned global_index1 = (*iter)->GetNodeGlobalIndex(local_index1);
401 unsigned global_index2 = (*iter)->GetNodeGlobalIndex(local_index2);
402 unsigned new_node_index = LookupInternalNode(global_index1, global_index2, edge_to_internal_map);
403 (*iter)->AddNode(this->mNodes[new_node_index]);
404 this->mNodes[new_node_index]->AddBoundaryElement((*iter)->GetIndex());
405 }
406 }
407 this->RefreshMesh();
408}
409
410template<unsigned DIM>
412{
413 return mNumVertices;
414}
415
416template<unsigned DIM>
418{
419 assert(DIM != 1); // LCOV_EXCL_LINE
420
421 //Make a linear mesh
423
424 NodeMap unused_map(this->GetNumNodes());
425
426 if (DIM==2) // In 2D, remesh using triangle via library calls
427 {
428 struct triangulateio mesher_input, mesher_output;
429 this->InitialiseTriangulateIo(mesher_input);
430 this->InitialiseTriangulateIo(mesher_output);
431
432 mesher_input.numberoftriangles = this->GetNumElements();
433 mesher_input.trianglelist = (int *) malloc(this->GetNumElements() * (DIM+1) * sizeof(int));
434 this->ExportToMesher(unused_map, mesher_input, mesher_input.trianglelist);
435
436 // Library call
437 triangulate((char*)"Qzero2", &mesher_input, &mesher_output, nullptr);
438
439 this->ImportFromMesher(mesher_output, mesher_output.numberoftriangles, mesher_output.trianglelist, mesher_output.numberofedges, mesher_output.edgelist, mesher_output.edgemarkerlist);
440 CountVertices();
442
443 //Tidy up triangle
444 this->FreeTriangulateIo(mesher_input);
445 this->FreeTriangulateIo(mesher_output);
446 }
447 else // in 3D, remesh using tetgen
448 {
449
450 class tetgen::tetgenio mesher_input, mesher_output;
451
452 mesher_input.numberoftetrahedra = this->GetNumElements();
453 mesher_input.tetrahedronlist = new int[this->GetNumElements() * (DIM+1)];
454 this->ExportToMesher(unused_map, mesher_input, mesher_input.tetrahedronlist);
455
456 // Library call
457 tetgen::tetrahedralize((char*)"Qzro2", &mesher_input, &mesher_output);
458
459 this->ImportFromMesher(mesher_output, mesher_output.numberoftetrahedra, mesher_output.tetrahedronlist, mesher_output.numberoftrifaces, mesher_output.trifacelist, nullptr);
460 CountVertices();
462 }
463}
464
465template<unsigned DIM>
467{
468 //Some mesh readers will let you read with non-linear elements
469 unsigned order_of_elements = rAbsMeshReader.GetOrderOfElements();
470
471 // If it is a linear mesh reader
472 if (order_of_elements == 1)
473 {
474 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");
475 ConstructFromLinearMeshReader(rAbsMeshReader);
476 return;
477 }
478
480 assert(this->GetNumBoundaryElements() > 0);
481
483 CountVertices();
486}
487
489
490
491template class QuadraticMesh<1>;
492template class QuadraticMesh<2>;
493template class QuadraticMesh<3>;
494
495
496// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define EXPORT_TEMPLATE_CLASS_SAME_DIMS(CLASS)
virtual unsigned GetOrderOfElements()
std::vector< BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * >::const_iterator BoundaryElementIterator
virtual void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
virtual void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
virtual void ConstructLinearMesh(unsigned width)
Definition Node.hpp:59
void AddElement(unsigned index)
Definition Node.cpp:268
void MarkAsInternal()
Definition Node.cpp:418
static void CheckBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh)
static void AddNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
static void AddInternalNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
static void AddInternalNodesToElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
void ConstructCuboid(unsigned numElemX, unsigned numElemY, unsigned numElemZ)
void ConstructFromMeshReader(AbstractMeshReader< DIM, DIM > &rMeshReader)
unsigned LookupInternalNode(unsigned globalIndex1, unsigned globalIndex2, std::map< std::pair< unsigned, unsigned >, unsigned > &rEdgeMap)
Node< DIM > * MakeNewInternalNode(unsigned &rIndex, c_vector< double, DIM > &rLocation, c_vector< double, DIM > &rTop)
unsigned GetNumVertices() const
void ConstructRectangularMesh(unsigned numElemX, unsigned numElemY, bool stagger=true)
void ConstructFromLinearMeshReader(AbstractMeshReader< DIM, DIM > &rMeshReader)
void ConstructLinearMesh(unsigned numElemX)
void ConstructFromMeshReader(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader)