Chaste Commit::9dfedaa0870fc7cfa8911a7ba21eba441bdba6b4
Element.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 "Element.hpp"
37#include <algorithm>
38#include <cfloat>
39#include <cassert>
40
42// Implementation
44
45template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
46Element<ELEMENT_DIM, SPACE_DIM>::Element(unsigned index, const std::vector<Node<SPACE_DIM>*>& rNodes, bool registerWithNodes)
47 : AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>(index, rNodes)
48{
49 if (registerWithNodes)
50 {
52 }
53}
54
55template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
56Element<ELEMENT_DIM, SPACE_DIM>::Element(const Element& rElement, const unsigned index)
57{
58 *this = rElement;
59 this->mIndex = index;
60
61 RegisterWithNodes();
62}
63
64template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
66{
67 for (unsigned i=0; i<this->mNodes.size(); i++)
68 {
69 this->mNodes[i]->AddElement(this->mIndex);
70 }
71}
73template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
75{
76 this->mIsDeleted = true;
77 // Update nodes in this element so they know they are not contained by us
78 for (unsigned i=0; i<this->GetNumNodes(); i++)
79 {
80 this->mNodes[i]->RemoveElement(this->mIndex);
81 }
82}
84template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
86{
87 assert(rIndex < this->mNodes.size());
88
89 // Remove it from the node at this location
90 this->mNodes[rIndex]->RemoveElement(this->mIndex);
92 // Update the node at this location
93 this->mNodes[rIndex] = pNode;
94
95 // Add element to this node
96 this->mNodes[rIndex]->AddElement(this->mIndex);
97}
99template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
101{
102 //std::cout << "ResetIndex - removing nodes.\n" << std::flush;
103 for (unsigned i=0; i<this->GetNumNodes(); i++)
104 {
105 //std::cout << "Node " << this->mNodes[i]->GetIndex() << " element "<< this->mIndex << std::flush;
106 this->mNodes[i]->RemoveElement(this->mIndex);
107 }
108 //std::cout << "\nResetIndex - done.\n" << std::flush;
109 this->mIndex=index;
110 RegisterWithNodes();
112
113template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
114c_vector<double,SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateCircumsphere(c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian)
115{
116 /*Assuming that x0,y0.. is at the origin then we need to solve
117 *
118 * ( 2x1 2y1 2z1 ) (x) (x1^2+y1^2+z1^2)
119 * ( 2x2 2y2 2z2 ) (y) (x2^2+y2^2+z2^2)
120 * ( 2x3 2y3 2z3 ) (z) (x3^2+y3^2+z3^2)
121 * where (x,y,z) is the circumcentre
122 *
123 */
125 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
126 c_vector<double, ELEMENT_DIM> rhs;
127
128 for (unsigned j=0; j<ELEMENT_DIM; j++)
130 double squared_location=0.0;
131 for (unsigned i=0; i<SPACE_DIM; i++)
132 {
133 //mJacobian(i,j) is the i-th component of j-th vertex (relative to vertex 0)
134 squared_location += rJacobian(i,j)*rJacobian(i,j);
135 }
136 rhs[j]=squared_location/2.0;
137 }
139 c_vector<double, SPACE_DIM> centre = zero_vector<double>(SPACE_DIM);
140 centre = prod(rhs, rInverseJacobian);
141 c_vector<double, SPACE_DIM+1> circum = zero_vector<double>(SPACE_DIM+1);
142 double squared_radius = 0.0;
143 for (unsigned i=0; i<SPACE_DIM; i++)
144 {
145 circum[i] = centre[i] + this->GetNodeLocation(0,i);
146 squared_radius += centre[i]*centre[i];
148 circum[SPACE_DIM] = squared_radius;
149
150 return circum;
151}
152
158template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
160{
161 assert(SPACE_DIM == ELEMENT_DIM); // LCOV_EXCL_LINE
162 if (SPACE_DIM == 1)
163 {
164 return 1.0;
165 }
167 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
168 c_matrix<double, ELEMENT_DIM, SPACE_DIM> jacobian_inverse;
169 double jacobian_determinant;
170
171 this->CalculateInverseJacobian(jacobian, jacobian_determinant, jacobian_inverse);
172
173 c_vector<double, SPACE_DIM+1> circum=CalculateCircumsphere(jacobian, jacobian_inverse);
174 if (SPACE_DIM == 2)
175 {
176 /* Want Q=(Area_Tri / Area_Cir) / (Area_Equilateral_Tri / Area_Equilateral_Cir)
177 * Area_Tri = |Jacobian| /2
178 * Area_Cir = Pi * r^2
179 * Area_Eq_Tri = (3*sqrt(3.0)/4)*R^2
180 * Area_Eq_Tri = Pi * R^2
181 * Q= (2*|Jacobian|)/3*sqrt(3.0)*r^2)
182 */
183 return 2.0*jacobian_determinant/(3.0*sqrt(3.0)*circum[SPACE_DIM]);
184 }
185 assert(SPACE_DIM == 3);
186 /* Want Q=(Vol_Tet / Vol_CirS) / (Vol_Plat_Tet / Vol_Plat_CirS)
187 * Vol_Tet = |Jacobian| /6
188 * Vol_CirS = 4*Pi*r^3/3
189 * Vol_Plat_Tet = 8*sqrt(3.0)*R^3/27
190 * Vol_Plat_CirS = 4*Pi*R^3/3
191 * Q= 3*sqrt(3.0)*|Jacobian|/ (16*r^3)
192 */
193
194 return (3.0*sqrt(3.0)*jacobian_determinant)
195 /(16.0*circum[SPACE_DIM]*sqrt(circum[SPACE_DIM]));
196}
197
198template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
200{
201 c_vector <double, 2> min_max;
202 min_max[0] = DBL_MAX; //Min initialised to very large
203 min_max[1] = 0.0; //Max initialised to zero
204 for (unsigned i=0; i<=ELEMENT_DIM; i++)
205 {
206 c_vector<double, SPACE_DIM> loc_i = this->GetNodeLocation(i);
207 for (unsigned j=i+1; j<=ELEMENT_DIM; j++)
208 {
209 double length = norm_2(this->GetNodeLocation(j) - loc_i);
210 min_max[0] = std::min(min_max[0], length);
211 min_max[1] = std::max(min_max[1], length);
212 }
213 }
214 return min_max;
215}
216
217template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
219{
220 // Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
221 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
222
223 c_vector<double, SPACE_DIM+1> weights;
224
225 c_vector<double, SPACE_DIM> xi = CalculateXi(rTestPoint);
226
227 // Copy 3 weights and compute the fourth weight
228 weights[0] = 1.0;
229 for (unsigned i=1; i<=SPACE_DIM; i++)
230 {
231 weights[0] -= xi[i-1];
232 weights[i] = xi[i-1];
233 }
234 return weights;
235}
236
237template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
239{
240 //Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
241 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
242
243 c_vector<double, SPACE_DIM+1> weights = CalculateInterpolationWeights(rTestPoint);
244
245 // Check for negative weights and set them to zero.
246 bool negative_weight = false;
247
248 for (unsigned i=0; i<=SPACE_DIM; i++)
249 {
250 if (weights[i] < 0.0)
251 {
252 weights[i] = 0.0;
253
254 negative_weight = true;
255 }
256 }
257
258 if (negative_weight == false)
259 {
260 // If there are no negative weights, there is nothing to do.
261 return weights;
262 }
263
264 // Renormalise so that all weights add to 1.0.
265
266 // Note that all elements of weights are now non-negative and so the l1-norm (sum of magnitudes) is equivalent to the sum of the elements of the vector
267 double sum = norm_1 (weights);
268
269 //l1-norm ought to be above 1 (because we scrubbed negative weights)
270 //However, if we scrubbed weights that were the size of the machine precision then we might be close to one (even less than 1).
271 assert( sum + DBL_EPSILON >= 1.0);
272
273 //We might skip this division when sum ~= 1
274 weights = weights/sum;
275
276 return weights;
277}
278
279template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
280c_vector<double, SPACE_DIM> Element<ELEMENT_DIM, SPACE_DIM>::CalculateXi(const ChastePoint<SPACE_DIM>& rTestPoint)
281{
282 //Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
283 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
284
285 // Find the location with respect to node 0
288 c_vector<double, SPACE_DIM> test_location = zero_vector<double>(SPACE_DIM);
289 test_location = rTestPoint.rGetLocation()-this->GetNodeLocation(0);
290
291 //Multiply by inverse Jacobian
292 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian = zero_matrix<double>(SPACE_DIM, ELEMENT_DIM);
293 c_matrix<double, ELEMENT_DIM, SPACE_DIM> inverse_jacobian = zero_matrix<double>(ELEMENT_DIM, SPACE_DIM);
294 double jacobian_determinant;
295
297 this->CalculateInverseJacobian(jacobian, jacobian_determinant, inverse_jacobian);
298
299 return prod(inverse_jacobian, test_location);
300}
301
302
303template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
305{
306 // Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
307 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
308
309 c_vector<double, SPACE_DIM+1> weights=CalculateInterpolationWeights(rTestPoint);
310
311 // If the point is in the simplex then all the weights should be positive.
312
313 for (unsigned i=0; i<=SPACE_DIM; i++)
314 {
315 if (strict)
316 {
317 // Points can't be close to a face
318 if (weights[i] <= 2*DBL_EPSILON)
319 {
320 return false;
321 }
322 }
323 else
324 {
325 // Allow point to be close to a face
326 if (weights[i] < -2*DBL_EPSILON)
327 {
328 return false;
329 }
330 }
331 }
332 return true;
333}
334
335// Explicit instantiation
336template class Element<1,1>;
337template class Element<1,2>;
338template class Element<1,3>;
339template class Element<2,2>;
340template class Element<2,3>;
341template class Element<3,3>;
c_vector< double, DIM > & rGetLocation()
c_vector< double, 2 > CalculateMinMaxEdgeLengths()
Definition Element.cpp:199
c_vector< double, SPACE_DIM+1 > CalculateInterpolationWeights(const ChastePoint< SPACE_DIM > &rTestPoint)
Definition Element.cpp:218
bool IncludesPoint(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false)
Definition Element.cpp:304
void RegisterWithNodes()
Definition Element.cpp:65
void UpdateNode(const unsigned &rIndex, Node< SPACE_DIM > *pNode)
Definition Element.cpp:85
c_vector< double, SPACE_DIM > CalculateXi(const ChastePoint< SPACE_DIM > &rTestPoint)
Definition Element.cpp:280
void ResetIndex(unsigned index)
Definition Element.cpp:100
void MarkAsDeleted()
Definition Element.cpp:74
double CalculateQuality()
Definition Element.cpp:159
c_vector< double, SPACE_DIM+1 > CalculateCircumsphere(c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, c_matrix< double, ELEMENT_DIM, SPACE_DIM > &rInverseJacobian)
Definition Element.cpp:114
Element(unsigned index, const std::vector< Node< SPACE_DIM > * > &rNodes, bool registerWithNodes=true)
Definition Element.cpp:46
c_vector< double, SPACE_DIM+1 > CalculateInterpolationWeightsWithProjection(const ChastePoint< SPACE_DIM > &rTestPoint)
Definition Element.cpp:238
Definition Node.hpp:59
void AddElement(unsigned index)
Definition Node.cpp:268