Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
Element.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 "Element.hpp"
37
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 if (length < min_max[0])
211 {
212 min_max[0] = length;
213 }
214 if (length > min_max[1])
215 {
216 min_max[1] = length;
217 }
218 }
219 }
220 return min_max;
221}
222
223template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
225{
226 // Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
227 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
228
229 c_vector<double, SPACE_DIM+1> weights;
230
231 c_vector<double, SPACE_DIM> xi = CalculateXi(rTestPoint);
232
233 // Copy 3 weights and compute the fourth weight
234 weights[0] = 1.0;
235 for (unsigned i=1; i<=SPACE_DIM; i++)
236 {
237 weights[0] -= xi[i-1];
238 weights[i] = xi[i-1];
239 }
240 return weights;
241}
242
243template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
245{
246 //Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
247 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
248
249 c_vector<double, SPACE_DIM+1> weights = CalculateInterpolationWeights(rTestPoint);
250
251 // Check for negative weights and set them to zero.
252 bool negative_weight = false;
253
254 for (unsigned i=0; i<=SPACE_DIM; i++)
255 {
256 if (weights[i] < 0.0)
257 {
258 weights[i] = 0.0;
259
260 negative_weight = true;
261 }
262 }
263
264 if (negative_weight == false)
265 {
266 // If there are no negative weights, there is nothing to do.
267 return weights;
268 }
269
270 // Renormalise so that all weights add to 1.0.
271
272 // 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
273 double sum = norm_1 (weights);
274
275 //l1-norm ought to be above 1 (because we scrubbed negative weights)
276 //However, if we scrubbed weights that were the size of the machine precision then we might be close to one (even less than 1).
277 assert( sum + DBL_EPSILON >= 1.0);
278
279 //We might skip this division when sum ~= 1
280 weights = weights/sum;
281
282 return weights;
283}
284
285template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
286c_vector<double, SPACE_DIM> Element<ELEMENT_DIM, SPACE_DIM>::CalculateXi(const ChastePoint<SPACE_DIM>& rTestPoint)
287{
288 //Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
289 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
290
291 // Find the location with respect to node 0
294 c_vector<double, SPACE_DIM> test_location = zero_vector<double>(SPACE_DIM);
295 test_location = rTestPoint.rGetLocation()-this->GetNodeLocation(0);
296
297 //Multiply by inverse Jacobian
298 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian = zero_matrix<double>(SPACE_DIM, ELEMENT_DIM);
299 c_matrix<double, ELEMENT_DIM, SPACE_DIM> inverse_jacobian = zero_matrix<double>(ELEMENT_DIM, SPACE_DIM);
300 double jacobian_determinant;
301
303 this->CalculateInverseJacobian(jacobian, jacobian_determinant, inverse_jacobian);
304
305 return prod(inverse_jacobian, test_location);
306}
307
308
309template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
311{
312 // Can only test if it's a tetrahedral mesh in 3d, triangles in 2d...
313 assert(ELEMENT_DIM == SPACE_DIM); // LCOV_EXCL_LINE
314
315 c_vector<double, SPACE_DIM+1> weights=CalculateInterpolationWeights(rTestPoint);
316
317 // If the point is in the simplex then all the weights should be positive.
318
319 for (unsigned i=0; i<=SPACE_DIM; i++)
320 {
321 if (strict)
322 {
323 // Points can't be close to a face
324 if (weights[i] <= 2*DBL_EPSILON)
325 {
326 return false;
327 }
328 }
329 else
330 {
331 // Allow point to be close to a face
332 if (weights[i] < -2*DBL_EPSILON)
333 {
334 return false;
335 }
336 }
337 }
338 return true;
339}
340
341// Explicit instantiation
342template class Element<1,1>;
343template class Element<1,2>;
344template class Element<1,3>;
345template class Element<2,2>;
346template class Element<2,3>;
347template 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:224
bool IncludesPoint(const ChastePoint< SPACE_DIM > &rTestPoint, bool strict=false)
Definition Element.cpp:310
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:286
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:244
Definition Node.hpp:59
void AddElement(unsigned index)
Definition Node.cpp:268