Chaste  Release::2017.1
AbstractTetrahedralElement.cpp
1 /*
2 
3 Copyright (c) 2005-2017, 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 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
198 {
199  if (ELEMENT_DIM == 1 && SPACE_DIM == 3)
200  {
201  EXCEPTION("Don't have enough information to calculate a normal vector");
202  }
203  c_vector<double, SPACE_DIM> normal;
204  double determinant;
205  CalculateWeightedDirection(normal, determinant);
206  normal /= determinant;
207  if (ELEMENT_DIM == 1)
208  {
209  // Need to rotate so tangent becomes normal
210  double x = normal[0];
211  normal[0] = normal[1];
212  normal[1] = -x;
213  }
214  return normal;
215 }
216 
217 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
219 {
220  c_vector<double, SPACE_DIM> centroid = zero_vector<double>(SPACE_DIM);
221  for (unsigned i=0; i<=ELEMENT_DIM; i++)
222  {
223  centroid += this->mNodes[i]->rGetLocation();
224  }
225  return centroid/((double)(ELEMENT_DIM + 1));
226 }
227 
228 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
229 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)
230 {
231  assert(ELEMENT_DIM <= SPACE_DIM); // LCOV_EXCL_LINE
232  CalculateJacobian(rJacobian, rJacobianDeterminant);
233 
234  // CalculateJacobian should make sure that the determinant is not close to zero (or, in fact, negative)
235  assert(rJacobianDeterminant > 0.0);
236  rInverseJacobian = Inverse(rJacobian);
237 }
238 
239 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
241 {
242 
243  if (this->mIsDeleted)
244  {
245  return 0.0;
246  }
247 
248  double scale_factor = 1.0;
249 
250  if (ELEMENT_DIM == 2)
251  {
252  scale_factor = 2.0; // both the volume of the canonical triangle is 1/2
253  }
254  else if (ELEMENT_DIM == 3)
255  {
256  scale_factor = 6.0; // both the volume of the canonical triangle is 1/6
257  }
258  return determinant/scale_factor;
259 }
260 
261 template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
263 {
264  for (unsigned local_index=0; local_index<ELEMENT_DIM+1; local_index++)
265  {
266  unsigned node = this->GetNodeGlobalIndex(local_index);
267 
268  for (unsigned problem_index=0; problem_index<problemDim; problem_index++)
269  {
270  //std::cout << local_index*problemDim + problem_index << std::endl;
271  pIndices[local_index*problemDim + problem_index] = node*problemDim + problem_index;
272  }
273  }
274 }
275 
276 
278 // Specialization for 0d elements //
280 
285 template<unsigned SPACE_DIM>
287  : AbstractElement<0, SPACE_DIM>(index, rNodes)
288 {
289  // Sanity checking
290  //unsigned total_nodes = 1;
291  assert(this->mNodes.size() == 1);
292  assert(SPACE_DIM > 0); // LCOV_EXCL_LINE
293 
294  // This is so we know it's the first time of asking
295  // Create Jacobian
296  c_vector<double, SPACE_DIM> weighted_direction;
297  double det;
298 
299  CalculateWeightedDirection(weighted_direction, det);
300 
301  // If determinant < 0 then element nodes are listed clockwise.
302  // We want them anticlockwise.
303  assert(det > 0.0);
304 }
305 
306 template<unsigned SPACE_DIM>
308  : AbstractElement<0, SPACE_DIM>(index)
309 {
310 }
311 
312 template<unsigned SPACE_DIM>
314  c_vector<double, SPACE_DIM>& rWeightedDirection,
315  double& rJacobianDeterminant)
316 {
317  assert(SPACE_DIM > 0); // LCOV_EXCL_LINE
318 
319  // End point of a line
320  rWeightedDirection = zero_vector<double>(SPACE_DIM);
321  rWeightedDirection(0) = 1.0;
322 
323  rJacobianDeterminant = 1.0;
324 }
325 
326 template<unsigned SPACE_DIM>
328 {
329  assert(SPACE_DIM > 0); // LCOV_EXCL_LINE
330 
331  // End point of a line
332  c_vector<double, SPACE_DIM> normal = zero_vector<double>(SPACE_DIM);
334  return normal;
335 }
336 
337 template<unsigned SPACE_DIM>
339 {
340  c_vector<double, SPACE_DIM> centroid = this->mNodes[0]->rGetLocation();
341  return centroid;
342 }
343 
344 template<unsigned SPACE_DIM>
345 void AbstractTetrahedralElement<0, SPACE_DIM>::GetStiffnessMatrixGlobalIndices(unsigned problemDim, unsigned* pIndices) const
346 {
347  for (unsigned local_index=0; local_index<1; local_index++)
348  {
349  unsigned node = this->GetNodeGlobalIndex(local_index);
350 
351  for (unsigned problem_index=0; problem_index<problemDim; problem_index++)
352  {
353  //std::cout << local_index*problemDim + problem_index << std::endl;
354  pIndices[local_index*problemDim + problem_index] = node*problemDim + problem_index;
355  }
356  }
357 }
358 
359 // Explicit instantiation
360 template class AbstractTetrahedralElement<0,1>;
361 template class AbstractTetrahedralElement<1,1>;
362 template class AbstractTetrahedralElement<0,2>;
363 template class AbstractTetrahedralElement<1,2>;
364 template class AbstractTetrahedralElement<2,2>;
365 template class AbstractTetrahedralElement<0,3>;
366 template class AbstractTetrahedralElement<1,3>;
367 template class AbstractTetrahedralElement<2,3>;
368 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)
double GetNodeLocation(unsigned localIndex, unsigned dimension) const