Chaste  Release::2018.1
AbstractMesh.cpp
1 /*
2 
3 Copyright (c) 2005-2018, 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 "AbstractMesh.hpp"
37 #include "Exception.hpp"
38 
40 // Implementation
42 
43 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
45  : mpDistributedVectorFactory(nullptr),
46  mMeshFileBaseName(""),
47  mMeshChangesDuringSimulation(false)
48 {
49 }
50 
51 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
53 {
54  // Iterate over nodes and free the memory
55  for (unsigned i = 0; i < mNodes.size(); i++)
56  {
57  delete mNodes[i];
58  }
59  if (mpDistributedVectorFactory)
60  {
61  delete mpDistributedVectorFactory;
62  }
63 }
64 
65 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
67 {
68  return mNodes.size();
69 }
70 
71 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
73 {
74  return mBoundaryNodes.size();
75 }
76 
77 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
79 {
80  return mNodes.size();
81 }
82 
83 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
85 {
86  /* Note, this implementation assumes that all nodes have the same number of attributes
87  * so that the first node in the container is indicative of the others.*/
88  assert(mNodes.size() != 0u);
89  return mNodes[0]->GetNumNodeAttributes();
90 }
91 
92 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
94 {
95  unsigned local_index = SolveNodeMapping(index);
96  return mNodes[local_index];
97 }
98 
99 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
101 {
102  return GetNode(index);
103 }
104 
105 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
107 {
108  if (mNodePermutation.empty())
109  {
110  return GetNode(index);
111  }
112  else
113  {
114  return GetNode(mNodePermutation[index]);
115  }
116 }
117 
118 // LCOV_EXCL_START
119 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
120 void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile)
121 {
123 }
124 // LCOV_EXCL_STOP
125 
126 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
128 {
129  // Does nothing, since an AbstractMesh has no elements
130 }
131 
132 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
134 {
135  if (mpDistributedVectorFactory == nullptr)
136  {
137  mpDistributedVectorFactory = new DistributedVectorFactory(GetNumNodes());
139  {
140  SetElementOwnerships(); // So any parallel implementation with shared mesh has owners set
141  }
142  }
143  return mpDistributedVectorFactory;
144 }
145 
146 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
148 {
149  if (mpDistributedVectorFactory)
150  {
151  EXCEPTION("Cannot change the mesh's distributed vector factory once it has been set.");
152  }
153  if (pFactory->GetNumProcs() != PetscTools::GetNumProcs())
154  {
155  EXCEPTION("The distributed vector factory provided to the mesh is for the wrong number of processes.");
156  }
157  mpDistributedVectorFactory = pFactory;
158 }
159 
160 // LCOV_EXCL_START
161 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
163 {
165 }
166 // LCOV_EXCL_STOP
167 
168 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
170 {
171  return mBoundaryNodes.begin();
172 }
173 
174 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
176 {
177  return mBoundaryNodes.end();
178 }
179 
180 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
182 {
183  if (!IsMeshOnDisk())
184  {
185  EXCEPTION("This mesh was not constructed from a file.");
186  }
187 
188  return mMeshFileBaseName;
189 }
190 
191 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
193 {
194  return (mMeshFileBaseName != "");
195 }
196 
197 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
199 {
200  return mNodePermutation;
201 }
202 
203 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
205  const c_vector<double, SPACE_DIM>& rLocationA, const c_vector<double, SPACE_DIM>& rLocationB)
206 {
207  c_vector<double, SPACE_DIM> vector = rLocationB - rLocationA;
208  return vector;
209 }
210 
211 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
213 {
214  c_vector<double, SPACE_DIM> vector = GetVectorFromAtoB(mNodes[indexA]->rGetLocation(),
215  mNodes[indexB]->rGetLocation());
216  return norm_2(vector);
217 }
218 
219 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
220 double AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetWidth(const unsigned& rDimension) const
221 {
222  assert(rDimension < SPACE_DIM);
223  return CalculateBoundingBox(mNodes).GetWidth(rDimension);
224 }
225 
226 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
228 {
229  // Work out the max and min location in each co-ordinate direction.
230  c_vector<double, SPACE_DIM> minimum_point;
231  c_vector<double, SPACE_DIM> maximum_point;
232 
233  // Deal with the special case of no nodes by returning a cuboid with zero volume.
234  if (rNodes.empty())
235  {
236  minimum_point = scalar_vector<double>(SPACE_DIM, 0.0);
237  maximum_point = scalar_vector<double>(SPACE_DIM, 0.0);
238  }
239  else
240  {
241  minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX);
242  maximum_point = scalar_vector<double>(SPACE_DIM, -DBL_MAX);
243 
244  // Iterate through nodes
246  for (unsigned index = 0; index < rNodes.size(); index++)
247  {
248  if (!rNodes[index]->IsDeleted())
249  {
250  // Note that we define this vector before setting it as otherwise the profiling build will break (see #2367)
251  c_vector<double, SPACE_DIM> position;
252  position = rNodes[index]->rGetLocation();
253 
254  // Update max/min
255  for (unsigned i = 0; i < SPACE_DIM; i++)
256  {
257  if (position[i] < minimum_point[i])
258  {
259  minimum_point[i] = position[i];
260  }
261  if (position[i] > maximum_point[i])
262  {
263  maximum_point[i] = position[i];
264  }
265  }
266  }
267  }
268  }
269 
270  ChastePoint<SPACE_DIM> min(minimum_point);
271  ChastePoint<SPACE_DIM> max(maximum_point);
272 
273  return ChasteCuboid<SPACE_DIM>(min, max);
274 }
275 
276 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278 {
279  ChasteCuboid<SPACE_DIM> bounding_cuboid = CalculateBoundingBox(mNodes);
280 
281  return bounding_cuboid;
282 }
283 
284 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
286 {
287  if (mNodes.empty())
288  {
289  /*
290  * This happens in parallel if a process isn't assigned any nodes.
291  * This case is covered in TestDistributedTetrahedralMesh::TestConstructLinearMeshSmallest
292  * but only when there are 3 or more processes
293  */
294  return UINT_MAX; // LCOV_EXCL_LINE
295  }
296  // Hold the best distance from node to point found so far
297  // and the (local) node at which this was recorded
298  unsigned best_node_index = 0u;
299  double best_node_point_distance = DBL_MAX;
300 
301  const c_vector<double, SPACE_DIM>& test_location = rTestPoint.rGetLocation();
302  // Now loop through the nodes, calculating the distance and updating best_node_point_distance
303  for (unsigned node_index = 0; node_index < mNodes.size(); node_index++)
304  {
305  // Calculate the distance from the chosen point to the current node
306  double node_point_distance = norm_2(GetVectorFromAtoB(mNodes[node_index]->rGetLocation(), test_location));
307  // Update the "best" distance and node index if necessary
308  if (node_point_distance < best_node_point_distance)
309  {
310  best_node_index = node_index;
311  best_node_point_distance = node_point_distance;
312  }
313  }
314 
315  // Return the global index of the closest node to the point
316  // (which differs from the local index "best_node_index" in parallel)
317  // In the distributed case, we'll have to do an AllReduce next
318  return mNodes[best_node_index]->GetIndex();
319 }
320 
321 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
322 void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Scale(const double xScale, const double yScale, const double zScale)
323 {
324  unsigned num_nodes = mNodes.size();
325 
326  for (unsigned i = 0; i < num_nodes; i++)
327  {
328  c_vector<double, SPACE_DIM>& r_location = mNodes[i]->rGetModifiableLocation();
329  if (SPACE_DIM >= 3)
330  {
331  r_location[2] *= zScale;
332  }
333  if (SPACE_DIM >= 2)
334  {
335  r_location[1] *= yScale;
336  }
337  r_location[0] *= xScale;
338  }
339 
340  RefreshMesh();
341 }
342 
343 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
345  const double xMovement,
346  const double yMovement,
347  const double zMovement)
348 {
349  c_vector<double, SPACE_DIM> displacement;
350 
351  switch (SPACE_DIM)
352  {
353  case 3:
354  displacement[2] = zMovement;
355  // purposeful fallthrough - Note the presence of this comment stops compiler warning for gcc >= 7!
356  // See https://developers.redhat.com/blog/2017/03/10/wimplicit-fallthrough-in-gcc-7/
357  case 2:
358  displacement[1] = yMovement;
359  // fallthrough on purpose
360  case 1:
361  displacement[0] = xMovement;
362  // fallthrough on purpose
363  }
364 
365  Translate(displacement);
366 }
367 
368 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
369 void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Translate(const c_vector<double, SPACE_DIM>& rDisplacement)
370 {
371  unsigned num_nodes = this->mNodes.size();
372 
373  for (unsigned i = 0; i < num_nodes; i++)
374  {
375  c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
376  r_location += rDisplacement;
377  }
378 
379  RefreshMesh();
380 }
381 
382 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
383 void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_matrix<double, SPACE_DIM, SPACE_DIM> rotationMatrix)
384 {
385  unsigned num_nodes = this->mNodes.size();
386 
387  for (unsigned i = 0; i < num_nodes; i++)
388  {
389  c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
390  r_location = prod(rotationMatrix, r_location);
391  }
392 
393  RefreshMesh();
394 }
395 
396 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
397 void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_vector<double, 3> axis, double angle)
398 {
399  assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
400  double norm = norm_2(axis);
401  c_vector<double, 3> unit_axis = axis / norm;
402 
403  c_matrix<double, SPACE_DIM, SPACE_DIM> rotation_matrix;
404 
405  double c = cos(angle);
406  double s = sin(angle);
407 
408  rotation_matrix(0, 0) = unit_axis(0) * unit_axis(0) + c * (1 - unit_axis(0) * unit_axis(0));
409  rotation_matrix(0, 1) = unit_axis(0) * unit_axis(1) * (1 - c) - unit_axis(2) * s;
410  rotation_matrix(1, 0) = unit_axis(0) * unit_axis(1) * (1 - c) + unit_axis(2) * s;
411  rotation_matrix(1, 1) = unit_axis(1) * unit_axis(1) + c * (1 - unit_axis(1) * unit_axis(1));
412  rotation_matrix(0, 2) = unit_axis(0) * unit_axis(2) * (1 - c) + unit_axis(1) * s;
413  rotation_matrix(1, 2) = unit_axis(1) * unit_axis(2) * (1 - c) - unit_axis(0) * s;
414  rotation_matrix(2, 0) = unit_axis(0) * unit_axis(2) * (1 - c) - unit_axis(1) * s;
415  rotation_matrix(2, 1) = unit_axis(1) * unit_axis(2) * (1 - c) + unit_axis(0) * s;
416  rotation_matrix(2, 2) = unit_axis(2) * unit_axis(2) + c * (1 - unit_axis(2) * unit_axis(2));
417 
418  Rotate(rotation_matrix);
419 }
420 
421 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
423 {
424  if (SPACE_DIM != 3)
425  {
426  EXCEPTION("This rotation is only valid in 3D");
427  }
428  c_matrix<double, SPACE_DIM, SPACE_DIM> x_rotation_matrix = identity_matrix<double>(SPACE_DIM);
429 
430  x_rotation_matrix(1, 1) = cos(theta);
431  x_rotation_matrix(1, 2) = sin(theta);
432  x_rotation_matrix(2, 1) = -sin(theta);
433  x_rotation_matrix(2, 2) = cos(theta);
434  Rotate(x_rotation_matrix);
435 }
436 
437 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
439 {
440  if (SPACE_DIM != 3)
441  {
442  EXCEPTION("This rotation is only valid in 3D");
443  }
444  c_matrix<double, SPACE_DIM, SPACE_DIM> y_rotation_matrix = identity_matrix<double>(SPACE_DIM);
445 
446  y_rotation_matrix(0, 0) = cos(theta);
447  y_rotation_matrix(0, 2) = -sin(theta);
448  y_rotation_matrix(2, 0) = sin(theta);
449  y_rotation_matrix(2, 2) = cos(theta);
450 
451  Rotate(y_rotation_matrix);
452 }
453 
454 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
456 {
457  if (SPACE_DIM < 2)
458  {
459  EXCEPTION("This rotation is not valid in less than 2D");
460  }
461  c_matrix<double, SPACE_DIM, SPACE_DIM> z_rotation_matrix = identity_matrix<double>(SPACE_DIM);
462 
463  z_rotation_matrix(0, 0) = cos(theta);
464  z_rotation_matrix(0, 1) = sin(theta);
465  z_rotation_matrix(1, 0) = -sin(theta);
466  z_rotation_matrix(1, 1) = cos(theta);
467 
468  Rotate(z_rotation_matrix);
469 }
470 
471 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
473 {
474  RotateZ(theta);
475 }
476 
477 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
479 {
480 }
481 
482 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
484 {
485  return mMeshChangesDuringSimulation;
486 }
487 
488 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
490 {
491  unsigned max_num = 0u;
492  for (unsigned local_node_index = 0; local_node_index < mNodes.size(); local_node_index++)
493  {
494  unsigned num = mNodes[local_node_index]->GetNumContainingElements();
495  if (num > max_num)
496  {
497  max_num = num;
498  }
499  }
500  return max_num;
501 }
502 
503 template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
505 {
506  // We just forget what the original file was, which has the desired effect
507  mMeshFileBaseName = "";
508 }
509 
510 // Explicit instantiation
511 template class AbstractMesh<1, 1>;
512 template class AbstractMesh<1, 2>;
513 template class AbstractMesh<1, 3>;
514 template class AbstractMesh<2, 2>;
515 template class AbstractMesh<2, 3>;
516 template class AbstractMesh<3, 3>;
virtual DistributedVectorFactory * GetDistributedVectorFactory()
virtual void ReadNodesPerProcessorFile(const std::string &rNodesPerProcessorFile)
virtual unsigned GetNearestNodeIndex(const ChastePoint< SPACE_DIM > &rTestPoint)
Definition: Node.hpp:58
c_vector< double, DIM > & rGetLocation()
Definition: ChastePoint.cpp:76
virtual void RefreshMesh()
virtual Node< SPACE_DIM > * GetNodeOrHaloNode(unsigned index) const
const std::vector< unsigned > & rGetNodePermutation() const
bool IsMeshChanging() const
virtual void Translate(const c_vector< double, SPACE_DIM > &rDisplacement)
Node< SPACE_DIM > * GetNode(unsigned index) const
#define EXCEPTION(message)
Definition: Exception.hpp:143
unsigned CalculateMaximumContainingElementsPerProcess() const
std::string GetMeshFileBaseName() const
virtual ChasteCuboid< SPACE_DIM > CalculateBoundingBox() const
virtual unsigned GetNumNodes() const
virtual void PermuteNodes()
BoundaryNodeIterator GetBoundaryNodeIteratorBegin() const
virtual unsigned GetNumAllNodes() const
#define NEVER_REACHED
Definition: Exception.hpp:206
virtual void SetElementOwnerships()
double GetDistanceBetweenNodes(unsigned indexA, unsigned indexB)
unsigned GetNumBoundaryNodes() const
unsigned GetNumNodeAttributes() const
std::vector< Node< SPACE_DIM > * >::const_iterator BoundaryNodeIterator
bool IsMeshOnDisk() const
virtual ~AbstractMesh()
void SetMeshHasChangedSinceLoading()
static bool IsParallel()
Definition: PetscTools.cpp:97
virtual double GetWidth(const unsigned &rDimension) const
virtual void Rotate(c_matrix< double, SPACE_DIM, SPACE_DIM > rotationMatrix)
virtual c_vector< double, SPACE_DIM > GetVectorFromAtoB(const c_vector< double, SPACE_DIM > &rLocationA, const c_vector< double, SPACE_DIM > &rLocationB)
BoundaryNodeIterator GetBoundaryNodeIteratorEnd() const
virtual void Scale(const double xFactor=1.0, const double yFactor=1.0, const double zFactor=1.0)
virtual void SetDistributedVectorFactory(DistributedVectorFactory *pFactory)
static unsigned GetNumProcs()
Definition: PetscTools.cpp:108
void RotateY(const double theta)
void RotateZ(const double theta)
Node< SPACE_DIM > * GetNodeFromPrePermutationIndex(unsigned index) const
void RotateX(const double theta)