Chaste Commit::1fd4e48e3990e67db148bc1bc4cf6991a0049d0c
AbstractMesh.cpp
1/*
2
3Copyright (c) 2005-2024, 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
41// Implementation
43
44template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
46 : mpDistributedVectorFactory(nullptr),
47 mMeshFileBaseName(""),
48 mMeshChangesDuringSimulation(false)
49{
50}
51
52template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
54{
55 // Iterate over nodes and free the memory
56 for (unsigned i = 0; i < mNodes.size(); i++)
57 {
58 delete mNodes[i];
59 }
60 if (mpDistributedVectorFactory)
61 {
62 delete mpDistributedVectorFactory;
63 }
64}
65
66template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
68{
69 return mNodes.size();
70}
71
72template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
74{
75 return mBoundaryNodes.size();
76}
77
78template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
80{
81 return mNodes.size();
82}
83
84template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
86{
87 /* Note, this implementation assumes that all nodes have the same number of attributes
88 * so that the first node in the container is indicative of the others.*/
89 assert(mNodes.size() != 0u);
90 return mNodes[0]->GetNumNodeAttributes();
91}
92
93template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
95{
96 unsigned local_index = SolveNodeMapping(index);
97 return mNodes[local_index];
98}
99
100template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
102{
103 return GetNode(index);
104}
105
106template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
108{
109 if (mNodePermutation.empty())
110 {
111 return GetNode(index);
112 }
113 else
114 {
115 return GetNode(mNodePermutation[index]);
116 }
117}
118
119// LCOV_EXCL_START
120template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
121void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile)
122{
124}
125// LCOV_EXCL_STOP
126
127template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
129{
130 // Does nothing, since an AbstractMesh has no elements
131}
132
133template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
135{
136 if (mpDistributedVectorFactory == nullptr)
137 {
138 mpDistributedVectorFactory = new DistributedVectorFactory(GetNumNodes());
140 {
141 SetElementOwnerships(); // So any parallel implementation with shared mesh has owners set
142 }
143 }
144 return mpDistributedVectorFactory;
145}
146
147template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
149{
150 if (mpDistributedVectorFactory)
151 {
152 EXCEPTION("Cannot change the mesh's distributed vector factory once it has been set.");
153 }
154 if (pFactory->GetNumProcs() != PetscTools::GetNumProcs())
155 {
156 EXCEPTION("The distributed vector factory provided to the mesh is for the wrong number of processes.");
157 }
158 mpDistributedVectorFactory = pFactory;
159}
160
161// LCOV_EXCL_START
162template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
167// LCOV_EXCL_STOP
168
169template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
171{
172 return mBoundaryNodes.begin();
173}
174
175template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
178 return mBoundaryNodes.end();
179}
180
181template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
183{
184 if (!IsMeshOnDisk())
185 {
186 EXCEPTION("This mesh was not constructed from a file.");
187 }
188
189 return mMeshFileBaseName;
190}
191
192template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
195 return (mMeshFileBaseName != "");
196}
197
198template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
200{
201 return mNodePermutation;
202}
203
204template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
206 const c_vector<double, SPACE_DIM>& rLocationA, const c_vector<double, SPACE_DIM>& rLocationB)
207{
208 c_vector<double, SPACE_DIM> vector = rLocationB - rLocationA;
209 return vector;
210}
211
212template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
214{
215 c_vector<double, SPACE_DIM> vector = GetVectorFromAtoB(mNodes[indexA]->rGetLocation(),
216 mNodes[indexB]->rGetLocation());
217 return norm_2(vector);
218}
219
220template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
221double AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetWidth(const unsigned& rDimension) const
222{
223 assert(rDimension < SPACE_DIM);
224 return CalculateBoundingBox(mNodes).GetWidth(rDimension);
225}
226
227template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
229{
230 // Work out the max and min location in each co-ordinate direction.
231 c_vector<double, SPACE_DIM> minimum_point = zero_vector<double>(SPACE_DIM);
232 c_vector<double, SPACE_DIM> maximum_point = zero_vector<double>(SPACE_DIM);
234 // Deal with the special case of no nodes by returning a cuboid with zero volume.
235 if (rNodes.empty())
236 {
237 minimum_point = scalar_vector<double>(SPACE_DIM, 0.0);
238 maximum_point = scalar_vector<double>(SPACE_DIM, 0.0);
239 }
240 else
241 {
242 minimum_point = scalar_vector<double>(SPACE_DIM, DBL_MAX);
243 maximum_point = scalar_vector<double>(SPACE_DIM, -DBL_MAX);
244
245 // Iterate through nodes
247 for (unsigned index = 0; index < rNodes.size(); index++)
248 {
249 if (!rNodes[index]->IsDeleted())
250 {
251 c_vector<double, SPACE_DIM> position = rNodes[index]->rGetLocation();
252
253 // Update max/min
254 for (unsigned i = 0; i < SPACE_DIM; i++)
256 if (position[i] < minimum_point[i])
257 {
258 minimum_point[i] = position[i];
259 }
260 if (position[i] > maximum_point[i])
262 maximum_point[i] = position[i];
263 }
264 }
265 }
267 }
268
269 ChastePoint<SPACE_DIM> min(minimum_point);
270 ChastePoint<SPACE_DIM> max(maximum_point);
271
272 return ChasteCuboid<SPACE_DIM>(min, max);
273}
274
275template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
278 ChasteCuboid<SPACE_DIM> bounding_cuboid = CalculateBoundingBox(mNodes);
279
280 return bounding_cuboid;
281}
282
283template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
285{
286 if (mNodes.empty())
287 {
288 /*
289 * This happens in parallel if a process isn't assigned any nodes.
290 * This case is covered in TestDistributedTetrahedralMesh::TestConstructLinearMeshSmallest
291 * but only when there are 3 or more processes
292 */
293 return UINT_MAX; // LCOV_EXCL_LINE
295 // Hold the best distance from node to point found so far
296 // and the (local) node at which this was recorded
297 unsigned best_node_index = 0u;
298 double best_node_point_distance = DBL_MAX;
299
300 const c_vector<double, SPACE_DIM>& test_location = rTestPoint.rGetLocation();
301 // Now loop through the nodes, calculating the distance and updating best_node_point_distance
302 for (unsigned node_index = 0; node_index < mNodes.size(); node_index++)
303 {
304 // Calculate the distance from the chosen point to the current node
305 double node_point_distance = norm_2(GetVectorFromAtoB(mNodes[node_index]->rGetLocation(), test_location));
306 // Update the "best" distance and node index if necessary
307 if (node_point_distance < best_node_point_distance)
308 {
309 best_node_index = node_index;
310 best_node_point_distance = node_point_distance;
311 }
312 }
313
314 // Return the global index of the closest node to the point
315 // (which differs from the local index "best_node_index" in parallel)
316 // In the distributed case, we'll have to do an AllReduce next
317 return mNodes[best_node_index]->GetIndex();
318}
319
320template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
321void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Scale(const double xScale, const double yScale, const double zScale)
322{
323 unsigned num_nodes = mNodes.size();
324
325 for (unsigned i = 0; i < num_nodes; i++)
326 {
327 c_vector<double, SPACE_DIM>& r_location = mNodes[i]->rGetModifiableLocation();
328 if (SPACE_DIM >= 3)
329 {
330 r_location[2] *= zScale;
331 }
332 if (SPACE_DIM >= 2)
333 {
334 r_location[1] *= yScale;
335 }
336 r_location[0] *= xScale;
337 }
338
339 RefreshMesh();
340}
341
342template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
344 const double xMovement,
345 const double yMovement,
346 const double zMovement)
347{
348 c_vector<double, SPACE_DIM> displacement;
349
350 switch (SPACE_DIM)
351 {
352 case 3:
353 displacement[2] = zMovement;
354 // purposeful fallthrough - Note the presence of this comment stops compiler warning for gcc >= 7!
355 // See https://developers.redhat.com/blog/2017/03/10/wimplicit-fallthrough-in-gcc-7/
356 // \TODO When we adopt C++17 this should change to [[fallthrough]];
357 // https://en.cppreference.com/w/cpp/language/attributes/fallthrough
358 case 2:
359 displacement[1] = yMovement;
360 // fallthrough on purpose
361 case 1: // LCOV_EXCL_LINE
362 displacement[0] = xMovement;
363 // fallthrough on purpose
364 }
365
366 Translate(displacement);
367}
368
369template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
370void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Translate(const c_vector<double, SPACE_DIM>& rDisplacement)
372 unsigned num_nodes = this->mNodes.size();
373
374 for (unsigned i = 0; i < num_nodes; i++)
375 {
376 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
377 r_location += rDisplacement;
378 }
379
380 RefreshMesh();
381}
382
383template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
384void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_matrix<double, SPACE_DIM, SPACE_DIM> rotationMatrix)
385{
386 unsigned num_nodes = this->mNodes.size();
387
388 for (unsigned i = 0; i < num_nodes; i++)
390 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
391 r_location = prod(rotationMatrix, r_location);
392 }
393
394 RefreshMesh();
395}
396
397template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
398void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_vector<double, 3> axis, double angle)
399{
400 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
401 const double norm = norm_2(axis);
402 c_vector<double, 3> unit_axis = axis / norm;
403
404 c_matrix<double, 3, 3> rotation_matrix;
405
406 const double c = cos(angle);
407 const double s = sin(angle);
408
409 rotation_matrix(0, 0) = unit_axis(0) * unit_axis(0) + c * (1 - unit_axis(0) * unit_axis(0));
410 rotation_matrix(0, 1) = unit_axis(0) * unit_axis(1) * (1 - c) - unit_axis(2) * s;
411 rotation_matrix(1, 0) = unit_axis(0) * unit_axis(1) * (1 - c) + unit_axis(2) * s;
412 rotation_matrix(1, 1) = unit_axis(1) * unit_axis(1) + c * (1 - unit_axis(1) * unit_axis(1));
413 rotation_matrix(0, 2) = unit_axis(0) * unit_axis(2) * (1 - c) + unit_axis(1) * s;
414 rotation_matrix(1, 2) = unit_axis(1) * unit_axis(2) * (1 - c) - unit_axis(0) * s;
415 rotation_matrix(2, 0) = unit_axis(0) * unit_axis(2) * (1 - c) - unit_axis(1) * s;
416 rotation_matrix(2, 1) = unit_axis(1) * unit_axis(2) * (1 - c) + unit_axis(0) * s;
417 rotation_matrix(2, 2) = unit_axis(2) * unit_axis(2) + c * (1 - unit_axis(2) * unit_axis(2));
419 Rotate(rotation_matrix);
420}
421
422template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
424{
425 if (SPACE_DIM != 3)
426 {
427 EXCEPTION("This rotation is only valid in 3D");
428 }
429 c_matrix<double, 3, 3> x_rotation_matrix = identity_matrix<double>(3);
430
431 x_rotation_matrix(1, 1) = cos(theta);
432 x_rotation_matrix(1, 2) = sin(theta);
433 x_rotation_matrix(2, 1) = -sin(theta);
434 x_rotation_matrix(2, 2) = cos(theta);
435 Rotate(x_rotation_matrix);
437
438template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
440{
441 if (SPACE_DIM != 3)
443 EXCEPTION("This rotation is only valid in 3D");
444 }
445 c_matrix<double, 3, 3> y_rotation_matrix = identity_matrix<double>(3);
446
447 y_rotation_matrix(0, 0) = cos(theta);
448 y_rotation_matrix(0, 2) = -sin(theta);
449 y_rotation_matrix(2, 0) = sin(theta);
450 y_rotation_matrix(2, 2) = cos(theta);
451
452 Rotate(y_rotation_matrix);
453}
454
455template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
457{
458 if (SPACE_DIM < 2)
459 {
460 EXCEPTION("This rotation is not valid in less than 2D");
461 }
462 c_matrix<double, SPACE_DIM, SPACE_DIM> z_rotation_matrix = identity_matrix<double>(SPACE_DIM);
463
464 z_rotation_matrix(0, 0) = cos(theta);
465 z_rotation_matrix(0, 1) = sin(theta);
466 z_rotation_matrix(1, 0) = -sin(theta);
467 z_rotation_matrix(1, 1) = cos(theta);
468
469 Rotate(z_rotation_matrix);
470}
471
472template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
474{
475 RotateZ(theta);
476}
477
478template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
482
483template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
485{
486 return mMeshChangesDuringSimulation;
487}
488
489template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
491{
492 unsigned max_num = 0u;
493 for (unsigned local_node_index = 0; local_node_index < mNodes.size(); local_node_index++)
494 {
495 unsigned num = mNodes[local_node_index]->GetNumContainingElements();
496 if (num > max_num)
497 {
498 max_num = num;
499 }
500 }
501 return max_num;
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()