Chaste Commit::f2ff7ee04e70ac9d06c57344df8d017dbb12b97b
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"
38
40// Implementation
42
43template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
45 : mpDistributedVectorFactory(nullptr),
46 mMeshFileBaseName(""),
47 mMeshChangesDuringSimulation(false)
48{
49}
50
51template <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
65template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
67{
68 return mNodes.size();
69}
70
71template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
73{
74 return mBoundaryNodes.size();
75}
76
77template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
79{
80 return mNodes.size();
81}
82
83template <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
92template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
94{
95 unsigned local_index = SolveNodeMapping(index);
96 return mNodes[local_index];
97}
98
99template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
101{
102 return GetNode(index);
103}
104
105template <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
119template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
120void AbstractMesh<ELEMENT_DIM, SPACE_DIM>::ReadNodesPerProcessorFile(const std::string& rNodesPerProcessorFile)
121{
123}
124// LCOV_EXCL_STOP
125
126template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
128{
129 // Does nothing, since an AbstractMesh has no elements
131
132template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
134{
135 if (mpDistributedVectorFactory == nullptr)
136 {
137 mpDistributedVectorFactory = new DistributedVectorFactory(GetNumNodes());
140 SetElementOwnerships(); // So any parallel implementation with shared mesh has owners set
141 }
142 }
143 return mpDistributedVectorFactory;
144}
145
146template <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
161template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
166// LCOV_EXCL_STOP
167
168template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
173
174template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
176{
177 return mBoundaryNodes.end();
178}
179
180template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
182{
183 if (!IsMeshOnDisk())
185 EXCEPTION("This mesh was not constructed from a file.");
186 }
187
188 return mMeshFileBaseName;
190
191template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
193{
194 return (mMeshFileBaseName != "");
195}
196
197template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
199{
200 return mNodePermutation;
202
203template <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;
210
211template <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);
218
219template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
220double AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetWidth(const unsigned& rDimension) const
221{
222 assert(rDimension < SPACE_DIM);
223 return CalculateBoundingBox(mNodes).GetWidth(rDimension);
224}
225
226template <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
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 }
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 unsigned max_num = 0u;
494 for (unsigned local_node_index = 0; local_node_index < mNodes.size(); local_node_index++)
495 {
496 unsigned num = mNodes[local_node_index]->GetNumContainingElements();
497 if (num > max_num)
498 {
499 max_num = num;
500 }
501 }
502 return max_num;
503}
504
505template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
507{
508 // We just forget what the original file was, which has the desired effect
509 mMeshFileBaseName = "";
510}
511
512// Explicit instantiation
513template class AbstractMesh<1, 1>;
514template class AbstractMesh<1, 2>;
515template class AbstractMesh<1, 3>;
516template class AbstractMesh<2, 2>;
517template class AbstractMesh<2, 3>;
518template 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()