Chaste  Release::3.4
AbstractTetrahedralElement.cpp
1 /*
2 
3 Copyright (c) 2005-2016, University of Oxford.
4 All rights reserved.
5 
6 University of Oxford means the Chancellor, Masters and Scholars of the
7 University of Oxford, having an administrative office at Wellington
8 Square, Oxford OX1 2JD, UK.
9 
10 This file is part of Chaste.
11 
12 Redistribution and use in source and binary forms, with or without
13 modification, 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 
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 
34 */
35 
36 #include "UblasCustomFunctions.hpp"
37 #include "AbstractTetrahedralElement.hpp"
38 #include "Exception.hpp"
39 
40 #include <cassert>
41 
43 // Implementation
45 
46 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
47 void AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>::RefreshJacobian(c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian)
48 {
49  if (this->mIsDeleted)
50  {
51  EXCEPTION("Attempting to Refresh a deleted element");
52  }
53  for (unsigned i=0; i<SPACE_DIM; i++)
54  {
55  for (unsigned j=0; j!=ELEMENT_DIM; j++) // Does a j<ELEMENT_DIM without ever having to test j<0U (#186: pointless comparison of unsigned integer with zero)
56  {
57  rJacobian(i,j) = this->GetNodeLocation(j+1,i) - this->GetNodeLocation(0,i);
58  }
59  }
60 }
61 
62 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
64  : AbstractElement<ELEMENT_DIM, SPACE_DIM>(index, rNodes)
65 {
66  // Sanity checking
67  unsigned num_vectices = ELEMENT_DIM+1;
68 
69  double det;
70 
71  if (SPACE_DIM == ELEMENT_DIM)
72  {
73  // This is so we know it's the first time of asking
74  // Create Jacobian
75  c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
76  try
77  {
78  CalculateJacobian(jacobian, det);
79  }
80  catch (Exception)
81  {
82  // if the Jacobian is negative the orientation of the element is probably
83  // wrong, so swap the last two nodes around.
84 
85  this->mNodes[num_vectices-1] = rNodes[num_vectices-2];
86  this->mNodes[num_vectices-2] = rNodes[num_vectices-1];
87 
88  CalculateJacobian(jacobian, det);
89  // If determinant < 0 then element nodes are listed clockwise.
90  // We want them anticlockwise.
91  assert(det > 0.0);
92  }
93  }
94  else
95  {
96  //This is not a full-dimensional element
97  c_vector<double, SPACE_DIM> weighted_direction;
98  CalculateWeightedDirection(weighted_direction, det);
99  }
100 
101 }
102 
103 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
105  : AbstractElement<ELEMENT_DIM,SPACE_DIM>(index)
106 {}
107 
108 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
109 void AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>::CalculateJacobian(c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, double& rJacobianDeterminant)
110 {
111 
112  assert(ELEMENT_DIM <= SPACE_DIM);
113  RefreshJacobian(rJacobian);
114 
115  {
116  rJacobianDeterminant = Determinant(rJacobian);
117  if (rJacobianDeterminant <= DBL_EPSILON)
118  {
119  std::stringstream message;
120  message << "Jacobian determinant is non-positive: "
121  << "determinant = " << rJacobianDeterminant
122  << " for element " << this->mIndex << " (" << ELEMENT_DIM
123  << "D element in " << SPACE_DIM << "D space). Nodes are at:" << std::endl;
124 
125  for (unsigned local_node_index=0u; local_node_index != ELEMENT_DIM+1; local_node_index++)
126  {
127  c_vector<double, SPACE_DIM> location = this->GetNodeLocation(local_node_index);
128  message << "Node " << this->GetNodeGlobalIndex(local_node_index) << ":\t";
129 
130  for (unsigned i=0; i<SPACE_DIM; i++)
131  {
132  message << location[i];
133  if (i==SPACE_DIM - 1u)
134  {
135  message << std::endl;
136  }
137  else
138  {
139  message << "\t";
140  }
141  }
142  }
143  EXCEPTION(message.str());
144  }
145  }
146 }
147 
148 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
149 void AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>::CalculateWeightedDirection(c_vector<double, SPACE_DIM>& rWeightedDirection, double& rJacobianDeterminant)
150 {
151 
152  if (ELEMENT_DIM >= SPACE_DIM)
153  {
154  assert(ELEMENT_DIM == SPACE_DIM);
155  EXCEPTION("WeightedDirection undefined for fully dimensional element");
156  }
157 
158  c_matrix<double, SPACE_DIM, ELEMENT_DIM> jacobian;
159  RefreshJacobian(jacobian);
160 
161  /*
162  * At this point we're only dealing with subspace (ELEMENT_DIM < SPACE_DIM) elem.
163  * We assume that the rWeightedDirection vector and rJacobianDeterminant (length
164  * of vector) are the values from a previous call.
165  */
166 
167  // This code is only used when ELEMENT_DIM<SPACE_DIM
168  switch (ELEMENT_DIM)
169  {
170  case 0:
171  // See specialised template for ELEMENT_DIM==0
173  break;
174  case 1:
175  // Linear edge in a 2D plane or in 3D
176  rWeightedDirection=matrix_column<c_matrix<double,SPACE_DIM,ELEMENT_DIM> >(jacobian, 0);
177  break;
178  case 2:
179  // Surface triangle in a 3d mesh
180  assert(SPACE_DIM == 3);
181  rWeightedDirection(0) = -SubDeterminant(jacobian, 0, 2);
182  rWeightedDirection(1) = SubDeterminant(jacobian, 1, 2);
183  rWeightedDirection(2) = -SubDeterminant(jacobian, 2, 2);
184  break;
185  default:
186  ; // Not going to happen
187  }
188  rJacobianDeterminant = norm_2(rWeightedDirection);
189 
190  if (rJacobianDeterminant < DBL_EPSILON)
191  {
192  EXCEPTION("Jacobian determinant is zero");
193  }
194 }
195 
196 
197 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
199 {
200  if (ELEMENT_DIM == 1 && SPACE_DIM == 3)
201  {
202  EXCEPTION("Don't have enough information to calculate a normal vector");
203  }
204  c_vector<double, SPACE_DIM> normal;
205  double determinant;
206  CalculateWeightedDirection(normal, determinant);
207  normal /= determinant;
208  if (ELEMENT_DIM == 1)
209  {
210  // Need to rotate so tangent becomes normal
211  double x = normal[0];
212  normal[0] = normal[1];
213  normal[1] = -x;
214  }
215  return normal;
216 }
217 
218 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
220 {
221  c_vector<double, SPACE_DIM> centroid = zero_vector<double>(SPACE_DIM);
222  for (unsigned i=0; i<=ELEMENT_DIM; i++)
223  {
224  centroid += this->mNodes[i]->rGetLocation();
225  }
226  return centroid/((double)(ELEMENT_DIM + 1));
227 }
228 
229 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
230 void AbstractTetrahedralElement<ELEMENT_DIM, SPACE_DIM>::CalculateInverseJacobian(c_matrix<double, SPACE_DIM, ELEMENT_DIM>& rJacobian, double& rJacobianDeterminant, c_matrix<double, ELEMENT_DIM, SPACE_DIM>& rInverseJacobian)
231 {
232  assert(ELEMENT_DIM <= SPACE_DIM);
233  CalculateJacobian(rJacobian, rJacobianDeterminant);
234 
235  // CalculateJacobian should make sure that the determinant is not close to zero (or, in fact, negative)
236  assert(rJacobianDeterminant > 0.0);
237  rInverseJacobian = Inverse(rJacobian);
238 }
239 
240 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
242 {
243 
244  if (this->mIsDeleted)
245  {
246  return 0.0;
247  }
248 
249  double scale_factor = 1.0;
250 
251  if (ELEMENT_DIM == 2)
252  {
253  scale_factor = 2.0; // both the volume of the canonical triangle is 1/2
254  }
255  else if (ELEMENT_DIM == 3)
256  {
257  scale_factor = 6.0; // both the volume of the canonical triangle is 1/6
258  }
259  return determinant/scale_factor;
260 }
261 
262 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
264 {
265  for (unsigned local_index=0; local_index<ELEMENT_DIM+1; local_index++)
266  {
267  unsigned node = this->GetNodeGlobalIndex(local_index);
268 
269  for (unsigned problem_index=0; problem_index<problemDim; problem_index++)
270  {
271  //std::cout << local_index*problemDim + problem_index << std::endl;
272  pIndices[local_index*problemDim + problem_index] = node*problemDim + problem_index;
273  }
274  }
275 }
276 
277 
279 // Specialization for 0d elements //
281 
286 template<unsigned SPACE_DIM>
288  : AbstractElement<0, SPACE_DIM>(index, rNodes)
289 {
290  // Sanity checking
291  //unsigned total_nodes = 1;
292  assert(this->mNodes.size() == 1);
293  assert(SPACE_DIM > 0);
294 
295  // This is so we know it's the first time of asking
296  // Create Jacobian
297  c_vector<double, SPACE_DIM> weighted_direction;
298  double det;
299 
300  CalculateWeightedDirection(weighted_direction, det);
301 
302  // If determinant < 0 then element nodes are listed clockwise.
303  // We want them anticlockwise.
304  assert(det > 0.0);
305 }
306 
307 template<unsigned SPACE_DIM>
309  : AbstractElement<0, SPACE_DIM>(index)
310 {
311 }
312 
313 template<unsigned SPACE_DIM>
315  c_vector<double, SPACE_DIM>& rWeightedDirection,
316  double& rJacobianDeterminant)
317 {
318  assert(SPACE_DIM > 0);
319 
320  // End point of a line
321  rWeightedDirection = zero_vector<double>(SPACE_DIM);
322  rWeightedDirection(0) = 1.0;
323 
324  rJacobianDeterminant = 1.0;
325 }
326 
327 template<unsigned SPACE_DIM>
329 {
330  assert(SPACE_DIM > 0);
331 
332  // End point of a line
333  c_vector<double, SPACE_DIM> normal = zero_vector<double>(SPACE_DIM);
335  return normal;
336 }
337 
338 template<unsigned SPACE_DIM>
340 {
341  c_vector<double, SPACE_DIM> centroid = this->mNodes[0]->rGetLocation();
342  return centroid;
343 }
344 
345 template<unsigned SPACE_DIM>
346 void AbstractTetrahedralElement<0, SPACE_DIM>::GetStiffnessMatrixGlobalIndices(unsigned problemDim, unsigned* pIndices) const
347 {
348  for (unsigned local_index=0; local_index<1; local_index++)
349  {
350  unsigned node = this->GetNodeGlobalIndex(local_index);
351 
352  for (unsigned problem_index=0; problem_index<problemDim; problem_index++)
353  {
354  //std::cout << local_index*problemDim + problem_index << std::endl;
355  pIndices[local_index*problemDim + problem_index] = node*problemDim + problem_index;
356  }
357  }
358 }
359 
361 // Explicit instantiation
363 
364 template class AbstractTetrahedralElement<0,1>;
365 template class AbstractTetrahedralElement<1,1>;
366 template class AbstractTetrahedralElement<0,2>;
367 template class AbstractTetrahedralElement<1,2>;
368 template class AbstractTetrahedralElement<2,2>;
369 template class AbstractTetrahedralElement<0,3>;
370 template class AbstractTetrahedralElement<1,3>;
371 template class AbstractTetrahedralElement<2,3>;
372 template class AbstractTetrahedralElement<3,3>;
void CalculateWeightedDirection(c_vector< double, SPACE_DIM > &rWeightedDirection, double &rJacobianDeterminant)
Definition: Node.hpp:58
double GetVolume(double determinant) const
boost::numeric::ublas::c_matrix< T, 1, 1 > Inverse(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM)
unsigned GetNodeGlobalIndex(unsigned localIndex) const
#define EXCEPTION(message)
Definition: Exception.hpp:143
void RefreshJacobian(c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian)
T Determinant(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM)
#define NEVER_REACHED
Definition: Exception.hpp:206
c_vector< double, SPACE_DIM > CalculateCentroid() const
void CalculateInverseJacobian(c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, double &rJacobianDeterminant, c_matrix< double, ELEMENT_DIM, SPACE_DIM > &rInverseJacobian)
std::vector< Node< SPACE_DIM > * > mNodes
AbstractTetrahedralElement(unsigned index, const std::vector< Node< SPACE_DIM > * > &rNodes)
void GetStiffnessMatrixGlobalIndices(unsigned problemDim, unsigned *pIndices) const
c_vector< double, SPACE_DIM > CalculateNormal()
T SubDeterminant(const boost::numeric::ublas::c_matrix< T, 1, 1 > &rM, const unsigned missrow, const unsigned misscol)
void CalculateJacobian(c_matrix< double, SPACE_DIM, ELEMENT_DIM > &rJacobian, double &rJacobianDeterminant)