Chaste Commit::9dfedaa0870fc7cfa8911a7ba21eba441bdba6b4
AbstractMesh.cpp
1/*
2
3Copyright (c) 2005-2026, 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 "AbstractMesh.hpp"
37#include "Exception.hpp"
39#include <algorithm>
40
42// Implementation
44
45template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
47 : mpDistributedVectorFactory(nullptr),
48 mMeshFileBaseName(""),
49 mMeshChangesDuringSimulation(false)
50{
51}
52
53template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
55{
56 // Iterate over nodes and free the memory
57 for (unsigned i = 0; i < mNodes.size(); i++)
58 {
59 delete mNodes[i];
60 }
61 if (mpDistributedVectorFactory)
62 {
63 delete mpDistributedVectorFactory;
64 }
65}
66
67template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
69{
70 return mNodes.size();
71}
72
73template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
75{
76 return mBoundaryNodes.size();
77}
78
79template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
81{
82 return mNodes.size();
83}
84
85template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
87{
88 /* Note, this implementation assumes that all nodes have the same number of attributes
89 * so that the first node in the container is indicative of the others.*/
90 assert(mNodes.size() != 0u);
91 return mNodes[0]->GetNumNodeAttributes();
92}
93
94template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
96{
97 unsigned local_index = SolveNodeMapping(index);
98 return mNodes[local_index];
99}
100
101template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
103{
104 return GetNode(index);
105}
106
107template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
109{
110 if (mNodePermutation.empty())
111 {
112 return GetNode(index);
113 }
114 else
115 {
116 return GetNode(mNodePermutation[index]);
117 }
118}
119
120// LCOV_EXCL_START
121template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
122void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile)
123{
125}
126// LCOV_EXCL_STOP
127
128template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
131 // Does nothing, since an AbstractMesh has no elements
132}
133
134template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
136{
137 if (mpDistributedVectorFactory == nullptr)
138 {
139 mpDistributedVectorFactory = new DistributedVectorFactory(GetNumNodes());
141 {
142 SetElementOwnerships(); // So any parallel implementation with shared mesh has owners set
143 }
144 }
145 return mpDistributedVectorFactory;
146}
147
148template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
150{
151 if (mpDistributedVectorFactory)
152 {
153 EXCEPTION("Cannot change the mesh's distributed vector factory once it has been set.");
154 }
155 if (pFactory->GetNumProcs() != PetscTools::GetNumProcs())
156 {
157 EXCEPTION("The distributed vector factory provided to the mesh is for the wrong number of processes.");
158 }
159 mpDistributedVectorFactory = pFactory;
160}
161
162// LCOV_EXCL_START
163template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
168// LCOV_EXCL_STOP
169
170template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
173 return mBoundaryNodes.begin();
174}
175
176template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
181
182template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
185 if (!IsMeshOnDisk())
186 {
187 EXCEPTION("This mesh was not constructed from a file.");
188 }
190 return mMeshFileBaseName;
191}
192
193template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
195{
196 return (mMeshFileBaseName != "");
197}
198
199template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
202 return mNodePermutation;
203}
204
205template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
207 const c_vector<double, SPACE_DIM>& rLocationA, const c_vector<double, SPACE_DIM>& rLocationB)
208{
209 c_vector<double, SPACE_DIM> vector = rLocationB - rLocationA;
210 return vector;
211}
212
213template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
215{
216 c_vector<double, SPACE_DIM> vector = GetVectorFromAtoB(mNodes[indexA]->rGetLocation(),
217 mNodes[indexB]->rGetLocation());
218 return norm_2(vector);
219}
220
221template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
222double AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetWidth(const unsigned& rDimension) const
223{
224 assert(rDimension < SPACE_DIM);
225 return CalculateBoundingBox(mNodes).GetWidth(rDimension);
226}
227
228template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
230{
231 // Work out the max and min location in each co-ordinate direction.
232 c_vector<double, SPACE_DIM> minimum_point = zero_vector<double>(SPACE_DIM);
233 c_vector<double, SPACE_DIM> maximum_point = zero_vector<double>(SPACE_DIM);
234
235 // Deal with the special case of no nodes by returning a cuboid with zero volume.
236 if (rNodes.empty())
237 {
238 minimum_point = scalar_vector<double>(SPACE_DIM, 0.0);
239 maximum_point = scalar_vector<double>(SPACE_DIM, 0.0);
241 else
242 {
243 minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX);
244 maximum_point = scalar_vector<double>(SPACE_DIM, -DBL_MAX);
246 // Iterate through nodes
248 for (unsigned index = 0; index < rNodes.size(); index++)
249 {
250 if (!rNodes[index]->IsDeleted())
251 {
252 c_vector<double, SPACE_DIM> 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 }
267 }
268 }
269
270 ChastePoint<SPACE_DIM> min(minimum_point);
271 ChastePoint<SPACE_DIM> max(maximum_point);
273 return ChasteCuboid<SPACE_DIM>(min, max);
274}
275
276template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278{
279 ChasteCuboid<SPACE_DIM> bounding_cuboid = CalculateBoundingBox(mNodes);
280
281 return bounding_cuboid;
282}
283
284template <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}
321template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
322void 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)
331 r_location[2] *= zScale;
332 }
333 if (SPACE_DIM >= 2)
334 {
335 r_location[1] *= yScale;
336 }
337 r_location[0] *= xScale;
338 }
340 RefreshMesh();
341}
342
343template <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 // \TODO When we adopt C++17 this should change to [[fallthrough]];
358 // https://en.cppreference.com/w/cpp/language/attributes/fallthrough
359 case 2:
360 displacement[1] = yMovement;
361 // fallthrough on purpose
362 case 1: // LCOV_EXCL_LINE
363 displacement[0] = xMovement;
364 // fallthrough on purpose
365 }
366
367 Translate(displacement);
368}
369
370template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
371void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Translate(const c_vector<double, SPACE_DIM>& rDisplacement)
372{
373 unsigned num_nodes = this->mNodes.size();
374
375 for (unsigned i = 0; i < num_nodes; i++)
376 {
377 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
378 r_location += rDisplacement;
379 }
381 RefreshMesh();
382}
383
384template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
385void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_matrix<double, SPACE_DIM, SPACE_DIM> rotationMatrix)
386{
387 unsigned num_nodes = this->mNodes.size();
388
389 for (unsigned i = 0; i < num_nodes; i++)
390 {
391 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
392 r_location = prod(rotationMatrix, r_location);
393 }
394
395 RefreshMesh();
396}
398template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
399void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_vector<double, 3> axis, double angle)
400{
401 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
402 const double norm = norm_2(axis);
403 c_vector<double, 3> unit_axis = axis / norm;
405 c_matrix<double, 3, 3> rotation_matrix;
406
407 const double c = cos(angle);
408 const double s = sin(angle);
409
410 rotation_matrix(0, 0) = unit_axis(0) * unit_axis(0) + c * (1 - unit_axis(0) * unit_axis(0));
411 rotation_matrix(0, 1) = unit_axis(0) * unit_axis(1) * (1 - c) - unit_axis(2) * s;
412 rotation_matrix(1, 0) = unit_axis(0) * unit_axis(1) * (1 - c) + unit_axis(2) * s;
413 rotation_matrix(1, 1) = unit_axis(1) * unit_axis(1) + c * (1 - unit_axis(1) * unit_axis(1));
414 rotation_matrix(0, 2) = unit_axis(0) * unit_axis(2) * (1 - c) + unit_axis(1) * s;
415 rotation_matrix(1, 2) = unit_axis(1) * unit_axis(2) * (1 - c) - unit_axis(0) * s;
416 rotation_matrix(2, 0) = unit_axis(0) * unit_axis(2) * (1 - c) - unit_axis(1) * s;
417 rotation_matrix(2, 1) = unit_axis(1) * unit_axis(2) * (1 - c) + unit_axis(0) * s;
418 rotation_matrix(2, 2) = unit_axis(2) * unit_axis(2) + c * (1 - unit_axis(2) * unit_axis(2));
419
420 Rotate(rotation_matrix);
421}
422
423template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
426 if (SPACE_DIM != 3)
427 {
428 EXCEPTION("This rotation is only valid in 3D");
429 }
430 c_matrix<double, 3, 3> x_rotation_matrix = identity_matrix<double>(3);
432 x_rotation_matrix(1, 1) = cos(theta);
433 x_rotation_matrix(1, 2) = sin(theta);
434 x_rotation_matrix(2, 1) = -sin(theta);
435 x_rotation_matrix(2, 2) = cos(theta);
436 Rotate(x_rotation_matrix);
437}
438
439template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
441{
442 if (SPACE_DIM != 3)
443 {
444 EXCEPTION("This rotation is only valid in 3D");
445 }
446 c_matrix<double, 3, 3> y_rotation_matrix = identity_matrix<double>(3);
447
448 y_rotation_matrix(0, 0) = cos(theta);
449 y_rotation_matrix(0, 2) = -sin(theta);
450 y_rotation_matrix(2, 0) = sin(theta);
451 y_rotation_matrix(2, 2) = cos(theta);
452
453 Rotate(y_rotation_matrix);
454}
455
456template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
458{
459 if (SPACE_DIM < 2)
460 {
461 EXCEPTION("This rotation is not valid in less than 2D");
462 }
463 c_matrix<double, SPACE_DIM, SPACE_DIM> z_rotation_matrix = identity_matrix<double>(SPACE_DIM);
464
465 z_rotation_matrix(0, 0) = cos(theta);
466 z_rotation_matrix(0, 1) = sin(theta);
467 z_rotation_matrix(1, 0) = -sin(theta);
468 z_rotation_matrix(1, 1) = cos(theta);
469
470 Rotate(z_rotation_matrix);
471}
472
473template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
475{
476 RotateZ(theta);
477}
478
479template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
483
484template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
486{
487 return mMeshChangesDuringSimulation;
488}
489
490template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
492{
493 if (mNodes.empty())
494 {
495 return 0u;
496 }
497 return (*std::max_element(mNodes.begin(), mNodes.end(),
498 [](const Node<SPACE_DIM>* a, const Node<SPACE_DIM>* b)
499 {
500 return a->GetNumContainingElements() < b->GetNumContainingElements();
501 }))->GetNumContainingElements();
502}
503
504template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
506{
507 // We just forget what the original file was, which has the desired effect
508 mMeshFileBaseName = "";
509}
510
511// Explicit instantiation
512template class AbstractMesh<1, 1>;
513template class AbstractMesh<1, 2>;
514template class AbstractMesh<1, 3>;
515template class AbstractMesh<2, 2>;
516template class AbstractMesh<2, 3>;
517template class AbstractMesh<3, 3>;
#define EXCEPTION(message)
#define NEVER_REACHED
virtual unsigned GetNumAllNodes() const
void RotateZ(const double theta)
virtual DistributedVectorFactory * GetDistributedVectorFactory()
void RotateY(const double theta)
virtual void Translate(const c_vector< double, SPACE_DIM > &rDisplacement)
virtual Node< SPACE_DIM > * GetNodeOrHaloNode(unsigned index) const
virtual void RefreshMesh()
BoundaryNodeIterator GetBoundaryNodeIteratorBegin() const
virtual void SetElementOwnerships()
bool IsMeshChanging() const
virtual void SetDistributedVectorFactory(DistributedVectorFactory *pFactory)
void RotateX(const double theta)
std::string GetMeshFileBaseName() const
std::vector< Node< SPACE_DIM > * >::const_iterator BoundaryNodeIterator
virtual unsigned GetNumNodes() const
virtual ChasteCuboid< SPACE_DIM > CalculateBoundingBox() const
virtual void ReadNodesPerProcessorFile(const std::string &rNodesPerProcessorFile)
virtual double GetWidth(const unsigned &rDimension) const
virtual c_vector< double, SPACE_DIM > GetVectorFromAtoB(const c_vector< double, SPACE_DIM > &rLocationA, const c_vector< double, SPACE_DIM > &rLocationB)
virtual void PermuteNodes()
double GetDistanceBetweenNodes(unsigned indexA, unsigned indexB)
bool IsMeshOnDisk() const
Node< SPACE_DIM > * GetNode(unsigned index) const
BoundaryNodeIterator GetBoundaryNodeIteratorEnd() const
virtual void Rotate(c_matrix< double, SPACE_DIM, SPACE_DIM > rotationMatrix)
virtual unsigned GetNearestNodeIndex(const ChastePoint< SPACE_DIM > &rTestPoint)
virtual ~AbstractMesh()
virtual void Scale(const double xFactor=1.0, const double yFactor=1.0, const double zFactor=1.0)
const std::vector< unsigned > & rGetNodePermutation() const
unsigned GetNumNodeAttributes() const
unsigned CalculateMaximumContainingElementsPerProcess() const
unsigned GetNumBoundaryNodes() const
void SetMeshHasChangedSinceLoading()
Node< SPACE_DIM > * GetNodeFromPrePermutationIndex(unsigned index) const
c_vector< double, DIM > & rGetLocation()
Definition Node.hpp:59
static bool IsParallel()
static unsigned GetNumProcs()