Chaste Commit::6e4f5fe395bca70eb7641cf6e0e87f450383ca5a
QuadraticMeshHelper.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include "QuadraticMeshHelper.hpp"
37
38#define SEEK_TO_CONTENT(methNameDirect, methNameIncrement, index) \
39 if (index > 0u) { \
40 if (rMeshReader.IsFileFormatBinary()) { \
41 rMeshReader.methNameDirect(index - 1u); \
42 } else { \
43 for (unsigned i=0; i<index-1u; ++i) { \
44 rMeshReader.methNameIncrement(); \
45 } } }
46
47template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
48void SeekToBoundaryElement(AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>& rMeshReader,
49 unsigned boundaryElementIndex)
50{
51 SEEK_TO_CONTENT(GetFaceData, GetNextFaceData, boundaryElementIndex);
52}
53
54template<unsigned DIM>
56 AbstractMeshReader<DIM,DIM>* pMeshReader)
57{
58 assert(pMesh);
59 assert(pMeshReader);
60
61 if (pMesh->GetNumLocalElements() > 0u)
62 {
63 pMeshReader->Reset();
64
65 // Create a set of element indices we own
66 std::set<unsigned> owned_element_indices;
68 iter != pMesh->GetElementIteratorEnd();
69 ++iter)
70 {
71 owned_element_indices.insert(iter->GetIndex());
72 }
73
74 const std::vector<unsigned>& r_node_perm = pMesh->rGetNodePermutation();
75
76 // Add the extra nodes (1 extra node in 1D, 3 in 2D, 6 in 3D) to the element data
77 for (typename AbstractMeshReader<DIM,DIM>::ElementIterator iter = pMeshReader->GetElementIteratorBegin(owned_element_indices);
78 iter != pMeshReader->GetElementIteratorEnd();
79 ++iter)
80 {
81 std::vector<unsigned> nodes = iter->NodeIndices;
82 assert(nodes.size()==(DIM+1)*(DIM+2)/2);
83 Element<DIM,DIM>* p_element = pMesh->GetElement(iter.GetIndex());
84 assert(p_element->GetNumNodes()==DIM+1); // Element is initially linear
85
86 // Add extra nodes to make it a quad element
87 for (unsigned j=DIM+1; j<(DIM+1)*(DIM+2)/2; j++)
88 {
89 unsigned node_index = nodes[j];
90 if (!r_node_perm.empty())
91 {
92 node_index = r_node_perm[node_index];
93 }
94 Node<DIM>* p_node = pMesh->GetNodeOrHaloNode(node_index);
95 p_element->AddNode(p_node);
96 p_node->AddElement(p_element->GetIndex());
97 p_node->MarkAsInternal();
98 }
99 }
100 }
101}
102
103template<unsigned DIM>
105 AbstractMeshReader<DIM,DIM>* pMeshReader)
106{
107 assert(pMesh);
108 assert(pMeshReader);
109 // We only have boundary elements in 2d or 3d
110 if (DIM > 1 && pMesh->GetNumLocalBoundaryElements() > 0u)
111 {
112 // If the data is on disk our job is easy
113 if (pMeshReader->GetOrderOfBoundaryElements() == 2u)
114 {
115 // The work should have been done in the linear constructor, but let's check
116 // that the first face has more than DIM nodes.
117 assert((*pMesh->GetBoundaryElementIteratorBegin())->GetNumNodes()==DIM*(DIM+1)/2);
118 return;
119 }
120 else
121 {
122 AddNodesToBoundaryElements(pMesh, pMeshReader);
123 }
124 }
125}
126
127template<unsigned DIM>
129 AbstractMeshReader<DIM,DIM>* pMeshReader)
130 {
131 // Loop over all boundary elements, find the equivalent face from all
132 // the elements, and add the extra nodes to the boundary element
133 bool boundary_element_file_has_containing_element_info = false;
134
135 if (pMeshReader)
136 {
137 boundary_element_file_has_containing_element_info = pMeshReader->GetReadContainingElementOfBoundaryElement();
138 }
139
140 if (DIM > 1)
141 {
142 if (boundary_element_file_has_containing_element_info)
143 {
144 pMeshReader->Reset();
145 }
146
148 // we may need to skip through the boundary element file searching for containing elements hints.
149 // This counter keeps track of our position in the file.
150 unsigned next_face_on_file = 0u;
151
152 for (auto iter = pMesh->GetBoundaryElementIteratorBegin();
153 iter != pMesh->GetBoundaryElementIteratorEnd();
154 ++iter)
155 {
156
157 // collect the nodes of this boundary element in a set
158 std::set<unsigned> boundary_element_node_indices;
159 for (unsigned i=0; i<DIM; i++)
160 {
161 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) );
162 }
163
164 bool found_this_boundary_element = false;
165
166 // Loop over elements surrounding this face, then loop over each face of that element, and see if it matches
167 // this boundary element.
168 // Note, if we know what elem it should be in (boundary_element_file_has_containing_element_info==true)
169 // we will reset elem_index immediately (below)
170 Node<DIM>* p_representative_node = (*iter)->GetNode(0);
171 for (typename Node<DIM>::ContainingElementIterator element_iter = p_representative_node->ContainingElementsBegin();
172 element_iter != p_representative_node->ContainingElementsEnd();
173 ++element_iter)
174 {
175 unsigned elem_index = *element_iter;
176
177 // We know what elem it should be in (but we'll still check the node indices match in case)
178 if (boundary_element_file_has_containing_element_info)
179 {
180 unsigned face_index = (*iter)->GetIndex();
182 do
183 {
184 elem_index = pMeshReader->GetNextFaceData().ContainingElement;
185 next_face_on_file++;
186 }
187 while (face_index >= next_face_on_file);
188 }
189
190 Element<DIM,DIM>* p_element = pMesh->GetElement(elem_index);
191
192 // For each element, loop over faces (the opposites to a node)
193 for (unsigned face=0; face<DIM+1; face++)
194 {
195 // Collect the node indices for this face
196 std::set<unsigned> node_indices;
197 for (unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++)
198 {
199 if (local_node_index != face)
200 {
201 node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) );
202 }
203 }
204
205 assert(node_indices.size()==DIM);
206
207 // See if this face matches the boundary element, and add internal nodes if so
208 if (node_indices == boundary_element_node_indices)
209 {
210 QuadraticMeshHelper<DIM>::AddExtraBoundaryNodes(pMesh, *iter, p_element, face);
211
212 found_this_boundary_element = true;
213 break;
214 }
215 }
216
217 // If the containing element info was given, we must have found the face first time
218 if (boundary_element_file_has_containing_element_info && !found_this_boundary_element)
219 {
220 // LCOV_EXCL_START
221 //std::cout << (*iter)->GetIndex() << " " << pMeshReader->GetNextFaceData().ContainingElement << "\n";
222 EXCEPTION("Boundary element " << (*iter)->GetIndex()
223 << "wasn't found in the containing element given for it "
224 << elem_index);
225 // LCOV_EXCL_STOP
226 }
227
228 if (found_this_boundary_element)
229 {
230 break;
231 }
232 }
233
234 if (!found_this_boundary_element)
235 {
236 // LCOV_EXCL_START
237 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements");
238 // LCOV_EXCL_STOP
239 }
240 }
241 }
242}
243
244template<unsigned DIM>
246{
247#ifndef NDEBUG
248 unsigned expected_num_nodes = DIM*(DIM+1)/2;
249 for (auto iter = pMesh->GetBoundaryElementIteratorBegin();
250 iter != pMesh->GetBoundaryElementIteratorEnd();
251 ++iter)
252 {
253 assert((*iter)->GetNumNodes() == expected_num_nodes);
254 }
255#endif
256}
257
258template<unsigned DIM>
260 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
261 Node<DIM>* pNode)
262{
263 assert(DIM > 1); // LCOV_EXCL_LINE
264
265 // Add node to the boundary node list
266 if (!pNode->IsBoundaryNode())
267 {
268 pNode->SetAsBoundaryNode();
269 pMesh->mBoundaryNodes.push_back(pNode);
270 }
271 // Add it to the boundary element
272 pBoundaryElement->AddNode(pNode);
273}
274
275template<unsigned DIM>
277 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
278 Element<DIM,DIM>* pElement,
279 unsigned internalNode)
280{
281 assert(DIM > 1); // LCOV_EXCL_LINE
282 assert(internalNode >= DIM+1);
283 assert(internalNode < (DIM+1)*(DIM+2)/2);
284 Node<DIM>* p_internal_node = pElement->GetNode(internalNode);
285 AddNodeToBoundaryElement(pMesh, pBoundaryElement, p_internal_node);
286}
287
288template<unsigned DIM>
290 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
291 Element<DIM,DIM>* pElement,
292 unsigned nodeIndexOppositeToFace)
293{
294 assert(DIM!=1); // LCOV_EXCL_LINE
295 if (DIM==2)
296 {
297 assert(nodeIndexOppositeToFace<3);
298 // the single internal node of the element's face will be numbered 'face+3'
299 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, nodeIndexOppositeToFace+3);
300 }
301 else
302 {
303 assert(DIM==3);
304
305 unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0);
306 unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1);
307
308 unsigned offset;
309 bool reverse;
310
311 if (nodeIndexOppositeToFace==0)
312 {
313 // face opposite to node 0 = {1,2,3}, with corresponding internals {9,8,5}
314 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse);
315 HelperMethod2(pMesh, pBoundaryElement, pElement, 9, 8, 5, offset, reverse);
316 }
317 else if (nodeIndexOppositeToFace==1)
318 {
319 // face opposite to node 1 = {2,0,3}, with corresponding internals {7,9,6}
320 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse);
321 HelperMethod2(pMesh, pBoundaryElement, pElement, 7, 9, 6, offset, reverse);
322 }
323 else if (nodeIndexOppositeToFace==2)
324 {
325 // face opposite to node 2 = {0,1,3}, with corresponding internals {8,7,4}
326 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse);
327 HelperMethod2(pMesh, pBoundaryElement, pElement, 8, 7, 4, offset, reverse);
328 }
329 else
330 {
331 assert(nodeIndexOppositeToFace==3);
332 // face opposite to node 3 = {0,1,2}, with corresponding internals {5,6,4}
333 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse);
334 HelperMethod2(pMesh, pBoundaryElement, pElement, 5, 6, 4, offset, reverse);
335 }
336 }
337}
338
340// two unpleasant helper methods for AddExtraBoundaryNodes()
342
343// LCOV_EXCL_START /// \todo These helper methods aren't properly covered
344template<unsigned DIM>
345void QuadraticMeshHelper<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1,
346 Element<DIM,DIM>* pElement,
347 unsigned node0, unsigned node1, unsigned node2,
348 unsigned& rOffset,
349 bool& rReverse)
350{
351 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0)
352 {
353 rOffset = 0;
354 if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1)
355 {
356 rReverse = false;
357 }
358 else
359 {
360 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1);
361 rReverse = true;
362 }
363 }
364 else if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0)
365 {
366 rOffset = 1;
367 if (pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1)
368 {
369 rReverse = false;
370 }
371 else
372 {
373 assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1);
374 rReverse = true;
375 }
376 }
377 else
378 {
379 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0);
380 rOffset = 2;
381 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1)
382 {
383 rReverse = false;
384 }
385 else
386 {
387 assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1);
388 rReverse = true;
389 }
390 }
391}
392// LCOV_EXCL_STOP /// \todo These helper methods aren't properly covered
393
394
395// LCOV_EXCL_START /// \todo These helper methods aren't properly covered
396template<unsigned DIM>
398 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
399 Element<DIM,DIM>* pElement,
400 unsigned internalNode0, unsigned internalNode1, unsigned internalNode2,
401 unsigned offset,
402 bool reverse)
403{
404 if (offset==1)
405 {
406 unsigned temp = internalNode0;
407 internalNode0 = internalNode1;
408 internalNode1 = internalNode2;
409 internalNode2 = temp;
410 }
411 else if (offset == 2)
412 {
413 unsigned temp = internalNode0;
414 internalNode0 = internalNode2;
415 internalNode2 = internalNode1;
416 internalNode1 = temp;
417 }
418
419 if (reverse)
420 {
421 unsigned temp = internalNode1;
422 internalNode1 = internalNode2;
423 internalNode2 = temp;
424 }
425
426 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode0);
427 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode1);
428 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode2);
429}
430// LCOV_EXCL_STOP /// \todo These helper methods aren't properly covered
431
432// Explicit instantiation
433template class QuadraticMeshHelper<1>;
434template class QuadraticMeshHelper<2>;
435template class QuadraticMeshHelper<3>;
#define EXCEPTION(message)
Node< SPACE_DIM > * GetNode(unsigned localIndex) const
unsigned GetNumNodes() const
unsigned GetNodeGlobalIndex(unsigned localIndex) const
void AddNode(Node< SPACE_DIM > *pNode)
unsigned GetIndex() const
ElementIterator GetElementIteratorBegin()
virtual void Reset()=0
virtual bool GetReadContainingElementOfBoundaryElement()
virtual unsigned GetOrderOfBoundaryElements()
virtual ElementData GetNextFaceData()=0
ElementIterator GetElementIteratorEnd()
virtual Node< SPACE_DIM > * GetNodeOrHaloNode(unsigned index) const
std::vector< Node< SPACE_DIM > * > mBoundaryNodes
const std::vector< unsigned > & rGetNodePermutation() const
virtual unsigned GetNumLocalBoundaryElements() const
ElementIterator GetElementIteratorBegin(bool skipDeletedElements=true)
virtual unsigned GetNumLocalElements() const
Element< ELEMENT_DIM, SPACE_DIM > * GetElement(unsigned index) const
BoundaryElementIterator GetBoundaryElementIteratorBegin() const
BoundaryElementIterator GetBoundaryElementIteratorEnd() const
Definition Node.hpp:59
ContainingElementIterator ContainingElementsEnd() const
Definition Node.hpp:493
void AddElement(unsigned index)
Definition Node.cpp:268
ContainingElementIterator ContainingElementsBegin() const
Definition Node.hpp:485
bool IsBoundaryNode() const
Definition Node.cpp:164
unsigned GetIndex() const
Definition Node.cpp:158
void SetAsBoundaryNode(bool value=true)
Definition Node.cpp:127
void MarkAsInternal()
Definition Node.cpp:418
static void AddExtraBoundaryNodes(AbstractTetrahedralMesh< DIM, DIM > *pMesh, BoundaryElement< DIM-1, DIM > *pBoundaryElement, Element< DIM, DIM > *pElement, unsigned nodeIndexOppositeToFace)
static void AddNodeToBoundaryElement(AbstractTetrahedralMesh< DIM, DIM > *pMesh, BoundaryElement< DIM-1, DIM > *pBoundaryElement, Element< DIM, DIM > *pElement, unsigned internalNode)
static void HelperMethod2(AbstractTetrahedralMesh< DIM, DIM > *pMesh, BoundaryElement< DIM-1, DIM > *pBoundaryElement, Element< DIM, DIM > *pElement, unsigned internalNode0, unsigned internalNode1, unsigned internalNode2, unsigned offset, bool reverse)
static void CheckBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh)
static void AddNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
static void HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1, Element< DIM, DIM > *pElement, unsigned node0, unsigned node1, unsigned node2, unsigned &rOffset, bool &rReverse)
static void AddInternalNodesToBoundaryElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
static void AddInternalNodesToElements(AbstractTetrahedralMesh< DIM, DIM > *pMesh, AbstractMeshReader< DIM, DIM > *pMeshReader)
unsigned ContainingElement