Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
QuadraticMeshHelper.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 "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
154 iter != pMesh->GetBoundaryElementIteratorEnd();
155 ++iter)
156 {
157
158 // collect the nodes of this boundary element in a set
159 std::set<unsigned> boundary_element_node_indices;
160 for (unsigned i=0; i<DIM; i++)
161 {
162 boundary_element_node_indices.insert( (*iter)->GetNodeGlobalIndex(i) );
163 }
164
165 bool found_this_boundary_element = false;
166
167 // Loop over elements surrounding this face, then loop over each face of that element, and see if it matches
168 // this boundary element.
169 // Note, if we know what elem it should be in (boundary_element_file_has_containing_element_info==true)
170 // we will reset elem_index immediately (below)
171 Node<DIM>* p_representative_node = (*iter)->GetNode(0);
172 for (typename Node<DIM>::ContainingElementIterator element_iter = p_representative_node->ContainingElementsBegin();
173 element_iter != p_representative_node->ContainingElementsEnd();
174 ++element_iter)
175 {
176 unsigned elem_index = *element_iter;
177
178 // We know what elem it should be in (but we'll still check the node indices match in case)
179 if (boundary_element_file_has_containing_element_info)
180 {
181 unsigned face_index = (*iter)->GetIndex();
183 do
184 {
185 elem_index = pMeshReader->GetNextFaceData().ContainingElement;
186 next_face_on_file++;
187 }
188 while (face_index >= next_face_on_file);
189 }
190
191 Element<DIM,DIM>* p_element = pMesh->GetElement(elem_index);
192
193 // For each element, loop over faces (the opposites to a node)
194 for (unsigned face=0; face<DIM+1; face++)
195 {
196 // Collect the node indices for this face
197 std::set<unsigned> node_indices;
198 for (unsigned local_node_index=0; local_node_index<DIM+1; local_node_index++)
199 {
200 if (local_node_index != face)
201 {
202 node_indices.insert( p_element->GetNodeGlobalIndex(local_node_index) );
203 }
204 }
205
206 assert(node_indices.size()==DIM);
207
208 // See if this face matches the boundary element, and add internal nodes if so
209 if (node_indices == boundary_element_node_indices)
210 {
211 QuadraticMeshHelper<DIM>::AddExtraBoundaryNodes(pMesh, *iter, p_element, face);
212
213 found_this_boundary_element = true;
214 break;
215 }
216 }
217
218 // If the containing element info was given, we must have found the face first time
219 if (boundary_element_file_has_containing_element_info && !found_this_boundary_element)
220 {
221 // LCOV_EXCL_START
222 //std::cout << (*iter)->GetIndex() << " " << pMeshReader->GetNextFaceData().ContainingElement << "\n";
223 EXCEPTION("Boundary element " << (*iter)->GetIndex()
224 << "wasn't found in the containing element given for it "
225 << elem_index);
226 // LCOV_EXCL_STOP
227 }
228
229 if (found_this_boundary_element)
230 {
231 break;
232 }
233 }
234
235 if (!found_this_boundary_element)
236 {
237 // LCOV_EXCL_START
238 EXCEPTION("Unable to find a face of an element which matches one of the boundary elements");
239 // LCOV_EXCL_STOP
240 }
241 }
242 }
243}
244
245template<unsigned DIM>
247{
248#ifndef NDEBUG
249 unsigned expected_num_nodes = DIM*(DIM+1)/2;
252 iter != pMesh->GetBoundaryElementIteratorEnd();
253 ++iter)
254 {
255 assert((*iter)->GetNumNodes() == expected_num_nodes);
256 }
257#endif
258}
259
260template<unsigned DIM>
262 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
263 Node<DIM>* pNode)
264{
265 assert(DIM > 1); // LCOV_EXCL_LINE
266
267 // Add node to the boundary node list
268 if (!pNode->IsBoundaryNode())
269 {
270 pNode->SetAsBoundaryNode();
271 pMesh->mBoundaryNodes.push_back(pNode);
272 }
273 // Add it to the boundary element
274 pBoundaryElement->AddNode(pNode);
275}
276
277template<unsigned DIM>
279 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
280 Element<DIM,DIM>* pElement,
281 unsigned internalNode)
282{
283 assert(DIM > 1); // LCOV_EXCL_LINE
284 assert(internalNode >= DIM+1);
285 assert(internalNode < (DIM+1)*(DIM+2)/2);
286 Node<DIM>* p_internal_node = pElement->GetNode(internalNode);
287 AddNodeToBoundaryElement(pMesh, pBoundaryElement, p_internal_node);
288}
289
290template<unsigned DIM>
292 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
293 Element<DIM,DIM>* pElement,
294 unsigned nodeIndexOppositeToFace)
295{
296 assert(DIM!=1); // LCOV_EXCL_LINE
297 if (DIM==2)
298 {
299 assert(nodeIndexOppositeToFace<3);
300 // the single internal node of the element's face will be numbered 'face+3'
301 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, nodeIndexOppositeToFace+3);
302 }
303 else
304 {
305 assert(DIM==3);
306
307 unsigned b_elem_n0 = pBoundaryElement->GetNodeGlobalIndex(0);
308 unsigned b_elem_n1 = pBoundaryElement->GetNodeGlobalIndex(1);
309
310 unsigned offset;
311 bool reverse;
312
313 if (nodeIndexOppositeToFace==0)
314 {
315 // face opposite to node 0 = {1,2,3}, with corresponding internals {9,8,5}
316 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 1, 2, 3, offset, reverse);
317 HelperMethod2(pMesh, pBoundaryElement, pElement, 9, 8, 5, offset, reverse);
318 }
319 else if (nodeIndexOppositeToFace==1)
320 {
321 // face opposite to node 1 = {2,0,3}, with corresponding internals {7,9,6}
322 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 2, 0, 3, offset, reverse);
323 HelperMethod2(pMesh, pBoundaryElement, pElement, 7, 9, 6, offset, reverse);
324 }
325 else if (nodeIndexOppositeToFace==2)
326 {
327 // face opposite to node 2 = {0,1,3}, with corresponding internals {8,7,4}
328 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 3, offset, reverse);
329 HelperMethod2(pMesh, pBoundaryElement, pElement, 8, 7, 4, offset, reverse);
330 }
331 else
332 {
333 assert(nodeIndexOppositeToFace==3);
334 // face opposite to node 3 = {0,1,2}, with corresponding internals {5,6,4}
335 HelperMethod1(b_elem_n0, b_elem_n1, pElement, 0, 1, 2, offset, reverse);
336 HelperMethod2(pMesh, pBoundaryElement, pElement, 5, 6, 4, offset, reverse);
337 }
338 }
339}
340
342// two unpleasant helper methods for AddExtraBoundaryNodes()
344
345// LCOV_EXCL_START /// \todo These helper methods aren't properly covered
346template<unsigned DIM>
347void QuadraticMeshHelper<DIM>::HelperMethod1(unsigned boundaryElemNode0, unsigned boundaryElemNode1,
348 Element<DIM,DIM>* pElement,
349 unsigned node0, unsigned node1, unsigned node2,
350 unsigned& rOffset,
351 bool& rReverse)
352{
353 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode0)
354 {
355 rOffset = 0;
356 if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1)
357 {
358 rReverse = false;
359 }
360 else
361 {
362 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1);
363 rReverse = true;
364 }
365 }
366 else if (pElement->GetNodeGlobalIndex(node1)==boundaryElemNode0)
367 {
368 rOffset = 1;
369 if (pElement->GetNodeGlobalIndex(node2)==boundaryElemNode1)
370 {
371 rReverse = false;
372 }
373 else
374 {
375 assert(pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1);
376 rReverse = true;
377 }
378 }
379 else
380 {
381 assert(pElement->GetNodeGlobalIndex(node2)==boundaryElemNode0);
382 rOffset = 2;
383 if (pElement->GetNodeGlobalIndex(node0)==boundaryElemNode1)
384 {
385 rReverse = false;
386 }
387 else
388 {
389 assert(pElement->GetNodeGlobalIndex(node1)==boundaryElemNode1);
390 rReverse = true;
391 }
392 }
393}
394// LCOV_EXCL_STOP /// \todo These helper methods aren't properly covered
395
396
397// LCOV_EXCL_START /// \todo These helper methods aren't properly covered
398template<unsigned DIM>
400 BoundaryElement<DIM-1,DIM>* pBoundaryElement,
401 Element<DIM,DIM>* pElement,
402 unsigned internalNode0, unsigned internalNode1, unsigned internalNode2,
403 unsigned offset,
404 bool reverse)
405{
406 if (offset==1)
407 {
408 unsigned temp = internalNode0;
409 internalNode0 = internalNode1;
410 internalNode1 = internalNode2;
411 internalNode2 = temp;
412 }
413 else if (offset == 2)
414 {
415 unsigned temp = internalNode0;
416 internalNode0 = internalNode2;
417 internalNode2 = internalNode1;
418 internalNode1 = temp;
419 }
420
421 if (reverse)
422 {
423 unsigned temp = internalNode1;
424 internalNode1 = internalNode2;
425 internalNode2 = temp;
426 }
427
428 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode0);
429 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode1);
430 AddNodeToBoundaryElement(pMesh, pBoundaryElement, pElement, internalNode2);
431}
432// LCOV_EXCL_STOP /// \todo These helper methods aren't properly covered
433
434// Explicit instantiation
435template class QuadraticMeshHelper<1>;
436template class QuadraticMeshHelper<2>;
437template 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)
std::vector< BoundaryElement< ELEMENT_DIM-1, SPACE_DIM > * >::const_iterator BoundaryElementIterator
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