Chaste Release::3.1
|
00001 /* 00002 00003 Copyright (c) 2005-2012, University of Oxford. 00004 All rights reserved. 00005 00006 University of Oxford means the Chancellor, Masters and Scholars of the 00007 University of Oxford, having an administrative office at Wellington 00008 Square, Oxford OX1 2JD, UK. 00009 00010 This file is part of Chaste. 00011 00012 Redistribution and use in source and binary forms, with or without 00013 modification, are permitted provided that the following conditions are met: 00014 * Redistributions of source code must retain the above copyright notice, 00015 this list of conditions and the following disclaimer. 00016 * Redistributions in binary form must reproduce the above copyright notice, 00017 this list of conditions and the following disclaimer in the documentation 00018 and/or other materials provided with the distribution. 00019 * Neither the name of the University of Oxford nor the names of its 00020 contributors may be used to endorse or promote products derived from this 00021 software without specific prior written permission. 00022 00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 00034 */ 00035 00036 #include "Element.hpp" 00037 00038 #include <cfloat> 00039 #include <cassert> 00040 00042 // Implementation 00044 00045 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00046 Element<ELEMENT_DIM, SPACE_DIM>::Element(unsigned index, const std::vector<Node<SPACE_DIM>*>& rNodes, bool registerWithNodes) 00047 : AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>(index, rNodes) 00048 { 00049 if (registerWithNodes) 00050 { 00051 RegisterWithNodes(); 00052 } 00053 } 00054 00055 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00056 Element<ELEMENT_DIM, SPACE_DIM>::Element(const Element& rElement, const unsigned index) 00057 { 00058 *this = rElement; 00059 this->mIndex = index; 00060 00061 RegisterWithNodes(); 00062 } 00063 00064 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00065 void Element<ELEMENT_DIM, SPACE_DIM>::RegisterWithNodes() 00066 { 00067 for (unsigned i=0; i<this->mNodes.size(); i++) 00068 { 00069 this->mNodes[i]->AddElement(this->mIndex); 00070 } 00071 } 00072 00073 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00074 void Element<ELEMENT_DIM, SPACE_DIM>::MarkAsDeleted() 00075 { 00076 this->mIsDeleted = true; 00077 // Update nodes in this element so they know they are not contained by us 00078 for (unsigned i=0; i<this->GetNumNodes(); i++) 00079 { 00080 this->mNodes[i]->RemoveElement(this->mIndex); 00081 } 00082 } 00083 00088 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00089 void Element<ELEMENT_DIM, SPACE_DIM>::UpdateNode(const unsigned& rIndex, Node<SPACE_DIM>* pNode) 00090 { 00091 assert(rIndex < this->mNodes.size()); 00092 00093 // Remove it from the node at this location 00094 this->mNodes[rIndex]->RemoveElement(this->mIndex); 00095 00096 // Update the node at this location 00097 this->mNodes[rIndex] = pNode; 00098 00099 // Add element to this node 00100 this->mNodes[rIndex]->AddElement(this->mIndex); 00101 } 00102 00103 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00104 void Element<ELEMENT_DIM, SPACE_DIM>::ResetIndex(unsigned index) 00105 { 00106 //std::cout << "ResetIndex - removing nodes.\n" << std::flush; 00107 for (unsigned i=0; i<this->GetNumNodes(); i++) 00108 { 00109 //std::cout << "Node " << this->mNodes[i]->GetIndex() << " element "<< this->mIndex << std::flush; 00110 this->mNodes[i]->RemoveElement(this->mIndex); 00111 } 00112 //std::cout << "\nResetIndex - done.\n" << std::flush; 00113 this->mIndex=index; 00114 RegisterWithNodes(); 00115 } 00116 00117 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00118 c_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) 00119 { 00120 /*Assuming that x0,y0.. is at the origin then we need to solve 00121 * 00122 * ( 2x1 2y1 2z1 ) (x) (x1^2+y1^2+z1^2) 00123 * ( 2x2 2y2 2z2 ) (y) (x2^2+y2^2+z2^2) 00124 * ( 2x3 2y3 2z3 ) (z) (x3^2+y3^2+z3^2) 00125 * where (x,y,z) is the circumcentre 00126 * 00127 */ 00128 00129 assert(ELEMENT_DIM == SPACE_DIM); 00130 c_vector<double, ELEMENT_DIM> rhs; 00131 00132 for (unsigned j=0; j<ELEMENT_DIM; j++) 00133 { 00134 double squared_location=0.0; 00135 for (unsigned i=0; i<SPACE_DIM; i++) 00136 { 00137 //mJacobian(i,j) is the i-th component of j-th vertex (relative to vertex 0) 00138 squared_location += rJacobian(i,j)*rJacobian(i,j); 00139 } 00140 rhs[j]=squared_location/2.0; 00141 } 00142 00143 c_vector<double, SPACE_DIM> centre; 00144 centre = prod(rhs, rInverseJacobian); 00145 c_vector<double, SPACE_DIM+1> circum; 00146 double squared_radius = 0.0; 00147 for (unsigned i=0; i<SPACE_DIM; i++) 00148 { 00149 circum[i] = centre[i] + this->GetNodeLocation(0,i); 00150 squared_radius += centre[i]*centre[i]; 00151 } 00152 circum[SPACE_DIM] = squared_radius; 00153 00154 return circum; 00155 } 00156 00162 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00163 double Element<ELEMENT_DIM, SPACE_DIM>::CalculateQuality() 00164 { 00165 assert(SPACE_DIM == ELEMENT_DIM); 00166 if (SPACE_DIM == 1) 00167 { 00168 return 1.0; 00169 } 00170 00171 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian; 00172 c_matrix<double, ELEMENT_DIM, SPACE_DIM> jacobian_inverse; 00173 double jacobian_determinant; 00174 00175 this->CalculateInverseJacobian(jacobian, jacobian_determinant, jacobian_inverse); 00176 00177 c_vector<double, SPACE_DIM+1> circum=CalculateCircumsphere(jacobian, jacobian_inverse); 00178 if (SPACE_DIM == 2) 00179 { 00180 /* Want Q=(Area_Tri / Area_Cir) / (Area_Equilateral_Tri / Area_Equilateral_Cir) 00181 * Area_Tri = |Jacobian| /2 00182 * Area_Cir = Pi * r^2 00183 * Area_Eq_Tri = (3*sqrt(3)/4)*R^2 00184 * Area_Eq_Tri = Pi * R^2 00185 * Q= (2*|Jacobian|)/3*sqrt(3)*r^2) 00186 */ 00187 return 2.0*jacobian_determinant/(3.0*sqrt(3.0)*circum[SPACE_DIM]); 00188 } 00189 assert(SPACE_DIM == 3); 00190 /* Want Q=(Vol_Tet / Vol_CirS) / (Vol_Plat_Tet / Vol_Plat_CirS) 00191 * Vol_Tet = |Jacobian| /6 00192 * Vol_CirS = 4*Pi*r^3/3 00193 * Vol_Plat_Tet = 8*sqrt(3)*R^3/27 00194 * Vol_Plat_CirS = 4*Pi*R^3/3 00195 * Q= 3*sqrt(3)*|Jacobian|/ (16*r^3) 00196 */ 00197 00198 return (3.0*sqrt(3.0)*jacobian_determinant) 00199 /(16.0*circum[SPACE_DIM]*sqrt(circum[SPACE_DIM])); 00200 } 00201 00202 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00203 c_vector <double, 2> Element<ELEMENT_DIM, SPACE_DIM>::CalculateMinMaxEdgeLengths() 00204 { 00205 c_vector <double, 2> min_max; 00206 min_max[0] = DBL_MAX; //Min initialised to very large 00207 min_max[1] = 0.0; //Max initialised to zero 00208 for (unsigned i=0; i<=ELEMENT_DIM; i++) 00209 { 00210 c_vector<double, SPACE_DIM> loc_i = this->GetNodeLocation(i); 00211 for (unsigned j=i+1; j<=ELEMENT_DIM; j++) 00212 { 00213 double length = norm_2(this->GetNodeLocation(j) - loc_i); 00214 if (length < min_max[0]) 00215 { 00216 min_max[0] = length; 00217 } 00218 if (length > min_max[1]) 00219 { 00220 min_max[1] = length; 00221 } 00222 } 00223 } 00224 return min_max; 00225 } 00226 00227 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00228 c_vector<double, SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateInterpolationWeights(const ChastePoint<SPACE_DIM>& rTestPoint) 00229 { 00230 //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d... 00231 assert(ELEMENT_DIM == SPACE_DIM); 00232 00233 c_vector<double, SPACE_DIM+1> weights; 00234 00235 c_vector<double, SPACE_DIM> xi=CalculateXi(rTestPoint); 00236 00237 //Copy 3 weights and compute the fourth weight 00238 weights[0]=1.0; 00239 for (unsigned i=1; i<=SPACE_DIM; i++) 00240 { 00241 weights[0] -= xi[i-1]; 00242 weights[i] = xi[i-1]; 00243 } 00244 return weights; 00245 } 00246 00252 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00253 c_vector<double, SPACE_DIM+1> Element<ELEMENT_DIM, SPACE_DIM>::CalculateInterpolationWeightsWithProjection(const ChastePoint<SPACE_DIM>& rTestPoint) 00254 { 00255 //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d... 00256 assert(ELEMENT_DIM == SPACE_DIM); 00257 00258 c_vector<double, SPACE_DIM+1> weights = CalculateInterpolationWeights(rTestPoint); 00259 00260 // Check for negative weights and set them to zero. 00261 bool negative_weight = false; 00262 00263 for (unsigned i=0; i<=SPACE_DIM; i++) 00264 { 00265 if (weights[i] < 0.0) 00266 { 00267 weights[i] = 0.0; 00268 00269 negative_weight = true; 00270 } 00271 } 00272 00273 if (negative_weight == false) 00274 { 00275 // If there are no negative weights, there is nothing to do. 00276 return weights; 00277 } 00278 00279 // Renormalise so that all weights add to 1.0. 00280 00281 // 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 00282 double sum = norm_1 (weights); 00283 00284 //l1-norm ought to be above 1 (because we scrubbed negative weights) 00285 //However, if we scrubbed weights that were the size of the machine precision then we might be close to one (even less than 1). 00286 assert( sum + DBL_EPSILON >= 1.0); 00287 00288 //We might skip this division when sum ~= 1 00289 weights = weights/sum; 00290 00291 return weights; 00292 } 00293 00294 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00295 c_vector<double, SPACE_DIM> Element<ELEMENT_DIM, SPACE_DIM>::CalculateXi(const ChastePoint<SPACE_DIM>& rTestPoint) 00296 { 00297 //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d... 00298 assert(ELEMENT_DIM == SPACE_DIM); 00299 00300 // Find the location with respect to node 0 00303 ChastePoint<SPACE_DIM> copy = rTestPoint; // as rGetLocation in line below is not a const method 00304 c_vector<double, SPACE_DIM> test_location=copy.rGetLocation()-this->GetNodeLocation(0); 00305 00306 //Multiply by inverse Jacobian 00307 c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian; 00308 c_matrix<double, ELEMENT_DIM, SPACE_DIM> inverse_jacobian; 00309 double jacobian_determinant; 00310 00312 this->CalculateInverseJacobian(jacobian, jacobian_determinant, inverse_jacobian); 00313 00314 return prod(inverse_jacobian, test_location); 00315 } 00316 00317 00318 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM> 00319 bool Element<ELEMENT_DIM, SPACE_DIM>::IncludesPoint(const ChastePoint<SPACE_DIM>& rTestPoint, bool strict) 00320 { 00321 //Can only test if it's a tetrahedal mesh in 3d, triangles in 2d... 00322 assert(ELEMENT_DIM == SPACE_DIM); 00323 00324 c_vector<double, SPACE_DIM+1> weights=CalculateInterpolationWeights(rTestPoint); 00325 00326 //If the point is in the simplex then all the weights should be positive 00327 00328 for (unsigned i=0; i<=SPACE_DIM; i++) 00329 { 00330 if (strict) 00331 { 00332 //Points can't be close to a face 00333 if (weights[i] <= 2*DBL_EPSILON) 00334 { 00335 return false; 00336 } 00337 } 00338 else 00339 { 00340 //Allow point to be close to a face 00341 if (weights[i] < -2*DBL_EPSILON) 00342 { 00343 return false; 00344 } 00345 } 00346 } 00347 return true; 00348 } 00349 00351 // Explicit instantiation 00353 00354 template class Element<1,1>; 00355 template class Element<1,2>; 00356 template class Element<1,3>; 00357 template class Element<2,2>; 00358 template class Element<2,3>; 00359 template class Element<3,3>;