Chaste Commit::6e4f5fe395bca70eb7641cf6e0e87f450383ca5a
DistributedTetrahedralMesh.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 "DistributedTetrahedralMesh.hpp"
37
38#include <cassert>
39#include <sstream>
40#include <string>
41#include <iterator>
42#include <algorithm>
43#include <boost/scoped_array.hpp>
44
45#include "Exception.hpp"
46#include "Element.hpp"
47#include "BoundaryElement.hpp"
48
49#include "PetscTools.hpp"
50#include "PetscMatTools.hpp"
51#include "DistributedVectorFactory.hpp"
52#include "OutputFileHandler.hpp"
53#include "NodePartitioner.hpp"
54
55#include "RandomNumberGenerator.hpp"
56
57#include "Timer.hpp"
58#include "TetrahedralMesh.hpp"
59#include "Warnings.hpp"
60
61#include "petscao.h"
62
64// IMPLEMENTATION
66
67template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
69 :
70 mTotalNumElements(0u),
71 mTotalNumBoundaryElements(0u),
72 mTotalNumNodes(0u),
73 mpSpaceRegion(nullptr),
74 mPartitioning(partitioningMethod)
75{
76 if (ELEMENT_DIM == 1 && (partitioningMethod != DistributedTetrahedralMeshPartitionType::GEOMETRIC))
77 {
78 //No partition is possible - revert to DUMB
79 mPartitioning = DistributedTetrahedralMeshPartitionType::DUMB;
80 }
81}
82
83template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
85{
86 for (unsigned i=0; i<this->mHaloNodes.size(); i++)
87 {
88 delete this->mHaloNodes[i];
89 }
90}
91
92template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
98
99template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
102 std::set<unsigned>& rNodesOwned,
103 std::set<unsigned>& rHaloNodesOwned,
104 std::set<unsigned>& rElementsOwned,
105 std::vector<unsigned>& rProcessorsOffset)
106{
107 if (mPartitioning == DistributedTetrahedralMeshPartitionType::METIS_LIBRARY)
108 {
109 EXCEPTION("METIS partitioning is deprecated. Please use PARMETIS_LIBRARY for parMETIS (or the parMETIS interface to PT-Scotch).");
110 }
111 if (mPartitioning == DistributedTetrahedralMeshPartitionType::PETSC_MAT_PARTITION && !PetscTools::HasParMetis())
112 {
113 // The following warning can only be reproduced on machines which do not have the PETSc/parMETIS interface.
114// LCOV_EXCL_START
115 WARNING("PETSc/parMETIS partitioning requires PETSc to be configured with parMETIS as an option. Current install has PETSc and parMETIS installed independently. Switching to parMETIS");
116 mPartitioning = DistributedTetrahedralMeshPartitionType::PARMETIS_LIBRARY;
117// LCOV_EXCL_STOP
118 }
120 if (mPartitioning==DistributedTetrahedralMeshPartitionType::PARMETIS_LIBRARY && PetscTools::IsParallel())
121 {
122 /*
123 * With ParMetisLibraryNodeAndElementPartitioning we compute the element partition first
124 * and then we work out the node ownership.
125 */
126 ParMetisLibraryNodeAndElementPartitioning(rMeshReader, rElementsOwned, rNodesOwned, rHaloNodesOwned, rProcessorsOffset);
127 }
128 else
129 {
130 /*
131 * Otherwise we compute the node partition and then we work out element distribution
132 */
133 if (mPartitioning==DistributedTetrahedralMeshPartitionType::PETSC_MAT_PARTITION && PetscTools::IsParallel())
134 {
135 NodePartitioner<ELEMENT_DIM, SPACE_DIM>::PetscMatrixPartitioning(rMeshReader, this->mNodePermutation, rNodesOwned, rProcessorsOffset);
136 }
137 else if (mPartitioning==DistributedTetrahedralMeshPartitionType::GEOMETRIC && PetscTools::IsParallel())
138 {
139 if (!mpSpaceRegion)
140 {
141 EXCEPTION("Using GEOMETRIC partition for DistributedTetrahedralMesh with local regions not set. Call SetProcessRegion(ChasteCuboid)");
142 }
143 NodePartitioner<ELEMENT_DIM, SPACE_DIM>::GeometricPartitioning(rMeshReader, this->mNodePermutation, rNodesOwned, rProcessorsOffset, mpSpaceRegion);
144 }
145 else
146 {
148 }
149
150 if (rMeshReader.HasNclFile())
151 {
152 // Form a set of all the element indices we are going to own
153 // (union of the sets from the lines in the NCL file)
154 for (std::set<unsigned>::iterator iter = rNodesOwned.begin();
155 iter != rNodesOwned.end();
156 ++iter)
157 {
158 std::vector<unsigned> containing_elements = rMeshReader.GetContainingElementIndices( *iter );
159 rElementsOwned.insert( containing_elements.begin(), containing_elements.end() );
160 }
161
162 // Iterate through that set rather than mTotalNumElements (knowing that we own a least one node in each line)
163 // Then read all the data into a node_index set
164 std::set<unsigned> node_index_set;
165
166 for (std::set<unsigned>::iterator iter = rElementsOwned.begin();
167 iter != rElementsOwned.end();
168 ++iter)
169 {
170 ElementData element_data = rMeshReader.GetElementData(*iter);
171 node_index_set.insert( element_data.NodeIndices.begin(), element_data.NodeIndices.end() );
173
174 // Subtract off the rNodesOwned set to produce rHaloNodesOwned.
175 // Note that rNodesOwned is a subset of node_index_set.
176 std::set_difference(node_index_set.begin(), node_index_set.end(),
177 rNodesOwned.begin(), rNodesOwned.end(),
178 std::inserter(rHaloNodesOwned, rHaloNodesOwned.begin()));
179 }
180 else
181 {
182 for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
184 ElementData element_data = rMeshReader.GetNextElementData();
185
186 bool element_owned = false;
187 std::set<unsigned> temp_halo_nodes;
188
189 for (std::vector<unsigned>::const_iterator it = element_data.NodeIndices.begin();
190 it != element_data.NodeIndices.end();
191 ++it)
192 {
193 if (rNodesOwned.find(*it) != rNodesOwned.end())
195 element_owned = true;
196 rElementsOwned.insert(element_number);
197 }
198 else
200 temp_halo_nodes.insert(*it);
201 }
202 }
203
204 if (element_owned)
205 {
206 rHaloNodesOwned.insert(temp_halo_nodes.begin(), temp_halo_nodes.end());
207 }
208 }
209 }
210
211 if (mPartitioning==DistributedTetrahedralMeshPartitionType::PETSC_MAT_PARTITION && PetscTools::IsParallel())
212 {
215 {
216 Timer::PrintAndReset("Element and halo node assignation");
217 }
218 }
219 }
220 rMeshReader.Reset();
221}
223template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
226{
227 std::set<unsigned> nodes_owned;
228 std::set<unsigned> halo_nodes_owned;
229 std::set<unsigned> elements_owned;
230 std::vector<unsigned> proc_offsets;//(PetscTools::GetNumProcs());
231
232 this->mMeshFileBaseName = rMeshReader.GetMeshFileBaseName();
233 mTotalNumElements = rMeshReader.GetNumElements();
234 mTotalNumBoundaryElements = rMeshReader.GetNumFaces();
235 mTotalNumNodes = rMeshReader.GetNumNodes();
237
239 Timer::Reset();
240 ComputeMeshPartitioning(rMeshReader, nodes_owned, halo_nodes_owned, elements_owned, proc_offsets);
242 //Timer::Print("partitioning");
243
244 // Reserve memory
245 this->mElements.reserve(elements_owned.size());
246 this->mNodes.reserve(nodes_owned.size());
247
248 if (rMeshReader.IsFileFormatBinary())
249 {
252 std::vector<double> coords;
253 // Binary : load only the nodes which are needed
254 for (typename AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>::NodeIterator node_it = rMeshReader.GetNodeIteratorBegin(nodes_owned);
255 node_it != rMeshReader.GetNodeIteratorEnd();
256 ++node_it)
257 {
258 // Loop over wholly-owned nodes
259 unsigned global_node_index = node_it.GetIndex();
260 coords = *node_it;
261 RegisterNode(global_node_index);
262 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, coords, false);
263
264// Node attributes in binary format are not yet supported, see #1730
265// for (unsigned i = 0; i < rMeshReader.GetNodeAttributes().size(); i++)
266// {
267// double attribute = rMeshReader.GetNodeAttributes()[i];
268// p_node->AddNodeAttribute(attribute);
269// }
270
271 this->mNodes.push_back(p_node);
272 }
273 for (typename AbstractMeshReader<ELEMENT_DIM, SPACE_DIM>::NodeIterator node_it = rMeshReader.GetNodeIteratorBegin(halo_nodes_owned);
274 node_it != rMeshReader.GetNodeIteratorEnd();
275 ++node_it)
276 {
277 // Loop over halo-owned nodes
278 unsigned global_node_index = node_it.GetIndex();
279 coords = *node_it;
280 RegisterHaloNode(global_node_index);
281 mHaloNodes.push_back(new Node<SPACE_DIM>(global_node_index, coords, false));
282 }
283 }
284 else
285 {
286 // Ascii : Sequentially load all the nodes and store those owned (or halo-owned) by the process
288 for (unsigned node_index=0; node_index < mTotalNumNodes; node_index++)
290 std::vector<double> coords;
292 coords = rMeshReader.GetNextNode();
293
294 // The node is owned by the processor
295 if (nodes_owned.find(node_index) != nodes_owned.end())
296 {
297 RegisterNode(node_index);
298 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index, coords, false);
299
300 for (unsigned i = 0; i < rMeshReader.GetNodeAttributes().size(); i++)
301 {
302 double attribute = rMeshReader.GetNodeAttributes()[i];
303 p_node->AddNodeAttribute(attribute);
304 }
305
306 this->mNodes.push_back(p_node);
307 }
308
309 // The node is a halo node in this processor
310 if (halo_nodes_owned.find(node_index) != halo_nodes_owned.end())
311 {
312 RegisterHaloNode(node_index);
313 mHaloNodes.push_back(new Node<SPACE_DIM>(node_index, coords, false));
314 }
315 }
316 }
317
318 for (auto elem_it = rMeshReader.GetElementIteratorBegin(elements_owned);
319 elem_it != rMeshReader.GetElementIteratorEnd();
320 ++elem_it)
321 {
322 ElementData element_data = *elem_it;
323 unsigned global_element_index = elem_it.GetIndex();
324
325 std::vector<Node<SPACE_DIM>*> nodes;
326 for (unsigned j=0; j<ELEMENT_DIM+1; j++)
327 {
328 // Because we have populated mNodes and mHaloNodes above, we can now use this method, which should never throw
329 nodes.push_back(this->GetNodeOrHaloNode(element_data.NodeIndices[j]));
330 }
331
332 RegisterElement(global_element_index);
333 Element<ELEMENT_DIM,SPACE_DIM>* p_element = new Element<ELEMENT_DIM,SPACE_DIM>(global_element_index, nodes);
334 this->mElements.push_back(p_element);
335
336 if (rMeshReader.GetNumElementAttributes() > 0)
337 {
338 assert(rMeshReader.GetNumElementAttributes() == 1);
339 double attribute_value = element_data.AttributeValue;
340 p_element->SetAttribute(attribute_value);
341 }
342 }
343
344 // Boundary nodes and elements
345 try
346 {
347 for (unsigned face_index=0; face_index<mTotalNumBoundaryElements; face_index++)
348 {
349 ElementData face_data = rMeshReader.GetNextFaceData();
350 std::vector<unsigned> node_indices = face_data.NodeIndices;
351
352 bool own = false;
353
354 for (unsigned node_index=0; node_index<node_indices.size(); node_index++)
355 {
356 // if I own this node
357 if (mNodesMapping.find(node_indices[node_index]) != mNodesMapping.end())
358 {
359 own = true;
360 break;
361 }
362 }
363
364 if (!own)
365 {
366 continue;
367 }
368
369 // Determine if this is a boundary face
370 //std::set<unsigned> containing_element_indices; // Elements that contain this face
371 std::vector<Node<SPACE_DIM>*> nodes;
372
373 for (unsigned node_index=0; node_index<node_indices.size(); node_index++)
375 //because we have populated mNodes and mHaloNodes above, we can now use this method,
376 //which SHOULD never throw (but it does).
377 try
378 {
379 nodes.push_back(this->GetNodeOrHaloNode(node_indices[node_index]));
380 }
381 catch (Exception &)
383 EXCEPTION("Face does not appear in element file (Face " << face_index << " in "<<this->mMeshFileBaseName<< ")");
384 }
385 }
386
387 // This is a boundary face
388 // Ensure all its nodes are marked as boundary nodes
389 for (unsigned j=0; j<nodes.size(); j++)
391 if (!nodes[j]->IsBoundaryNode())
392 {
393 nodes[j]->SetAsBoundaryNode();
394 this->mBoundaryNodes.push_back(nodes[j]);
395 }
396 // Register the index that this boundary element will have with the node
397 nodes[j]->AddBoundaryElement(face_index);
398 }
399
400 RegisterBoundaryElement(face_index);
401 BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>* p_boundary_element = new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(face_index, nodes);
402 this->mBoundaryElements.push_back(p_boundary_element);
403
404 if (rMeshReader.GetNumFaceAttributes() > 0)
405 {
406 assert(rMeshReader.GetNumFaceAttributes() == 1);
407 p_boundary_element->SetAttribute(face_data.AttributeValue);
408 }
409 }
410 }
411 catch (Exception &e)
412 {
413 PetscTools::ReplicateException(true); //Bad face exception
414 throw e;
415 }
417
418 if (mPartitioning != DistributedTetrahedralMeshPartitionType::DUMB && PetscTools::IsParallel())
419 {
420 assert(this->mNodePermutation.size() != 0);
421 // If we are partitioning (and permuting) a mesh, we need to be certain that we aren't doing it twice
422 assert(rMeshReader.HasNodePermutation() == false);
423
424 // We reorder so that each process owns a contiguous set of the indices and we can then build a distributed vector factory.
425 ReorderNodes();
426
427 unsigned num_owned;
428 unsigned rank = PetscTools::GetMyRank();
430 {
431 num_owned = proc_offsets[rank+1]-proc_offsets[rank];
432 }
433 else
434 {
435 num_owned = mTotalNumNodes - proc_offsets[rank];
436 }
437
438 assert(!this->mpDistributedVectorFactory);
439 this->mpDistributedVectorFactory = new DistributedVectorFactory(this->GetNumNodes(), num_owned);
440 }
441 else
443 // Dumb or sequential partition
444 assert(this->mpDistributedVectorFactory);
445
446 if (rMeshReader.HasNodePermutation())
447 {
448 // This is probably an unarchiving operation where the original run applied a permutation to the mesh
449 // We need to re-record that the permutation has happened (so that we can archive it correctly later).
450 this->mNodePermutation = rMeshReader.rGetNodePermutation();
451 }
452 }
453 rMeshReader.Reset();
455
456template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
458{
459 return this->mNodes.size();
460}
461
462template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
464{
465 return this->mHaloNodes.size();
466}
467
468template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
471 return this->mElements.size();
472}
473
474template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
476{
477 return this->mBoundaryElements.size();
478}
479
480template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
482{
483 return mTotalNumNodes;
484}
485
486template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
488{
489 return mTotalNumNodes;
490}
491
492template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
494{
495 return mTotalNumElements;
496}
497
498template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
503
504template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
506{
507 return mTotalNumBoundaryElements;
508}
509
510template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
512{
513 //Make sure the output vector is empty
514 rHaloIndices.clear();
515 for (unsigned i=0; i<mHaloNodes.size(); i++)
516 {
517 rHaloIndices.push_back(mHaloNodes[i]->GetIndex());
518 }
519}
520
521template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
523{
524 if (mpSpaceRegion == nullptr)
525 {
526 EXCEPTION("Trying to get unset mpSpaceRegion");
527 }
528 return mpSpaceRegion;
529}
530
531template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
533{
534 // All the local elements are owned by the processor (obviously...)
535 //Does nothing - unlike the non-distributed version
536}
537
538template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
543
544template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
546{
547 try
548 {
550 }
551 catch(Exception&) // we don't own the element
552 {
553 return false;
554 }
555}
556
557template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
559{
560 try
561 {
563 }
564 catch(Exception&) // we don't own the face
565 {
566 return false;
567 }
568}
569
570template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
572{
573 mNodesMapping[index] = this->mNodes.size();
574}
575
576template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
578{
579 mHaloNodesMapping[index] = mHaloNodes.size();
580}
581
582template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
584{
585 mElementsMapping[index] = this->mElements.size();
586}
587
588template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
590{
591 mBoundaryElementsMapping[index] = this->mBoundaryElements.size();
592}
593
594template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
596{
597 std::map<unsigned, unsigned>::const_iterator node_position = mNodesMapping.find(index);
598
599 if (node_position == mNodesMapping.end())
600 {
601 EXCEPTION("Requested node " << index << " does not belong to processor " << PetscTools::GetMyRank());
602 }
603 return node_position->second;
604}
605
606//template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
607//unsigned DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::SolveHaloNodeMapping(unsigned index)
608//{
609// assert(mHaloNodesMapping.find(index) != mHaloNodesMapping.end());
610// return mHaloNodesMapping[index];
611//}
612
613template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
615{
616 std::map<unsigned, unsigned>::const_iterator element_position = mElementsMapping.find(index);
617
618 if (element_position == mElementsMapping.end())
619 {
620 EXCEPTION("Requested element " << index << " does not belong to processor " << PetscTools::GetMyRank());
621 }
622
623 return element_position->second;
624}
625
626template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
628{
629 std::map<unsigned, unsigned>::const_iterator boundary_element_position = mBoundaryElementsMapping.find(index);
630
631 if (boundary_element_position == mBoundaryElementsMapping.end())
632 {
633 EXCEPTION("Requested boundary element " << index << " does not belong to processor " << PetscTools::GetMyRank());
634 }
635
636 return boundary_element_position->second;
637}
638
639template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
641{
642 std::map<unsigned, unsigned>::const_iterator node_position;
643 // First search the halo (expected to be a smaller map so quicker)
644 if ((node_position=mHaloNodesMapping.find(index)) != mHaloNodesMapping.end())
645 {
646 return mHaloNodes[node_position->second];
647 }
648 // Next search the owned node
649 if ((node_position=mNodesMapping.find(index)) != mNodesMapping.end())
650 {
651 //Found an owned node
652 return this->mNodes[node_position->second];
653 }
654 // Not here
655 EXCEPTION("Requested node/halo " << index << " does not belong to processor " << PetscTools::GetMyRank());
656}
657
658template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
660{
661 assert(PetscTools::IsParallel());
662
663 // Need to rebuild global-local maps
664 mNodesMapping.clear();
665 mHaloNodesMapping.clear();
666
667 // Update indices
668 for (unsigned index=0; index<this->mNodes.size(); index++)
669 {
670 unsigned old_index = this->mNodes[index]->GetIndex();
671 unsigned new_index = this->mNodePermutation[old_index];
672
673 this->mNodes[index]->SetIndex(new_index);
674 mNodesMapping[new_index] = index;
675 }
676
677 for (unsigned index=0; index<mHaloNodes.size(); index++)
678 {
679 unsigned old_index = mHaloNodes[index]->GetIndex();
680 unsigned new_index = this->mNodePermutation[old_index];
681
682 mHaloNodes[index]->SetIndex(new_index);
683 mHaloNodesMapping[new_index] = index;
684 }
685}
686
687template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
689{
690 assert(ELEMENT_DIM == 1); // LCOV_EXCL_LINE
691
692 //Check that there are enough nodes to make the parallelisation worthwhile
693 if (width==0)
694 {
695 EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
696 }
697
698 // Hook to pick up when we are using a geometric partition.
699 if (mPartitioning == DistributedTetrahedralMeshPartitionType::GEOMETRIC)
700 {
701 if (!mpSpaceRegion)
702 {
703 EXCEPTION("Space region not set for GEOMETRIC partition of DistributedTetrahedralMesh");
704 }
705
706 // Write a serial file, the load on distributed processors.
708 {
709 TrianglesMeshWriter<ELEMENT_DIM,SPACE_DIM> mesh_writer("", "temp_linear_mesh");
711 base_mesh.ConstructLinearMesh(width);
712 mesh_writer.WriteFilesUsingMesh(base_mesh);
713 }
715
716 OutputFileHandler output_handler("", false);
717
718 std::string output_dir = output_handler.GetOutputDirectoryFullPath();
719 TrianglesMeshReader<ELEMENT_DIM,SPACE_DIM> mesh_reader(output_dir+"temp_linear_mesh");
720
721 this->ConstructFromMeshReader(mesh_reader);
722 }
723 else // use a default partition.
724 {
725 //Use dumb partition so that archiving doesn't permute anything
726 mPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
727 mTotalNumNodes=width+1;
728 mTotalNumBoundaryElements=2u;
729 mTotalNumElements=width;
730
731 //Use DistributedVectorFactory to make a dumb partition of the nodes
732 assert(!this->mpDistributedVectorFactory);
733 this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes);
734 if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
735 {
736 // It's a short mesh and this process owns no nodes.
737 // This return cannot be covered by regular testing, but is covered by the Nightly -np 3 builder
738 return; //LCOV_EXCL_LINE
739 }
740
741 /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
742 * higher numbered process may have dropped out of this construction altogether
743 * (because is has no local ownership)
744 */
745 bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
746
747 unsigned lo_node=this->mpDistributedVectorFactory->GetLow();
748 unsigned hi_node=this->mpDistributedVectorFactory->GetHigh();
750 {
751 //Allow for a halo node
752 lo_node--;
753 }
754 if (!am_top_most)
755 {
756 //Allow for a halo node
757 hi_node++;
758 }
759 Node<SPACE_DIM>* p_old_node=nullptr;
760 for (unsigned node_index=lo_node; node_index<hi_node; node_index++)
761 {
762 // create node or halo-node
763 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(node_index, node_index==0 || node_index==width, node_index);
764 if (node_index<this->mpDistributedVectorFactory->GetLow() ||
765 node_index==this->mpDistributedVectorFactory->GetHigh() )
766 {
767 //Beyond left or right it's a halo node
768 RegisterHaloNode(node_index);
769 mHaloNodes.push_back(p_node);
770 }
771 else
772 {
773 RegisterNode(node_index);
774 this->mNodes.push_back(p_node); // create node
775
776 //A boundary face has to be wholely owned by the process
777 //Since, when ELEMENT_DIM>1 we have *at least* boundary node as a non-halo
778 if (node_index==0) // create left boundary node and boundary element
779 {
780 this->mBoundaryNodes.push_back(p_node);
781 RegisterBoundaryElement(0);
782 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(0, p_node) );
783 }
784 if (node_index==width) // create right boundary node and boundary element
785 {
786 this->mBoundaryNodes.push_back(p_node);
787 RegisterBoundaryElement(1);
788 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(1, p_node) );
789 }
790 }
791 if (node_index>lo_node) // create element
792 {
793 std::vector<Node<SPACE_DIM>*> nodes;
794 nodes.push_back(p_old_node);
795 nodes.push_back(p_node);
796 RegisterElement(node_index-1);
797 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(node_index-1, nodes) );
798 }
799 //Keep track of the node which we've just created
800 p_old_node=p_node;
801 }
802 }
803}
804
805template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
807{
808 assert(SPACE_DIM == 2); // LCOV_EXCL_LINE
809 assert(ELEMENT_DIM == 2); // LCOV_EXCL_LINE
810 //Check that there are enough nodes to make the parallelisation worthwhile
811 if (height==0)
812 {
813 EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
814 }
815
816 // Hook to pick up when we are using a geometric partition.
817 if (mPartitioning == DistributedTetrahedralMeshPartitionType::GEOMETRIC)
818 {
819 if (!mpSpaceRegion)
820 {
821 EXCEPTION("Space region not set for GEOMETRIC partition of DistributedTetrahedralMesh");
822 }
823
824 // Write a serial file, the load on distributed processors.
826 {
827 TrianglesMeshWriter<ELEMENT_DIM,SPACE_DIM> mesh_writer("", "temp_rectangular_mesh");
829 base_mesh.ConstructRectangularMesh(width, height);
830 mesh_writer.WriteFilesUsingMesh(base_mesh);
831 }
833
834 OutputFileHandler output_handler("", false);
835
836 std::string output_dir = output_handler.GetOutputDirectoryFullPath();
837 TrianglesMeshReader<ELEMENT_DIM,SPACE_DIM> mesh_reader(output_dir+"temp_rectangular_mesh");
838
839 this->ConstructFromMeshReader(mesh_reader);
840 }
841 else
842 {
843 //Use dumb partition so that archiving doesn't permute anything
844 mPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
845
846 mTotalNumNodes=(width+1)*(height+1);
847 mTotalNumBoundaryElements=(width+height)*2;
848 mTotalNumElements=width*height*2;
849
850 //Use DistributedVectorFactory to make a dumb partition of space
851 DistributedVectorFactory y_partition(height+1);
852 unsigned lo_y = y_partition.GetLow();
853 unsigned hi_y = y_partition.GetHigh();
854 //Dumb partition of nodes has to be such that each process gets complete slices
855 assert(!this->mpDistributedVectorFactory);
856 this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes, (width+1)*y_partition.GetLocalOwnership());
857 if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
858 {
859 // It's a short mesh and this process owns no nodes.
860 // This return cannot be covered by regular testing, but is covered by the Nightly -np 3 builder
861 return; //LCOV_EXCL_LINE
862 }
863
864 /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
865 * higher numbered process may have dropped out of this construction altogether
866 * (because is has no local ownership)
867 */
868 bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
869
870
872 {
873 //Allow for a halo node
874 lo_y--;
875 }
876 if (!am_top_most)
877 {
878 //Allow for a halo node
879 hi_y++;
880 }
881
882 //Construct the nodes
883 for (unsigned j=lo_y; j<hi_y; j++)
884 {
885 for (unsigned i=0; i<width+1; i++)
886 {
887 bool is_boundary=false;
888 if (i==0 || j==0 || i==width || j==height)
889 {
890 is_boundary=true;
891 }
892 unsigned global_node_index=((width+1)*(j) + i); //Verified from sequential
893 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, is_boundary, i, j);
894 if (j<y_partition.GetLow() || j==y_partition.GetHigh() )
895 {
896 //Beyond left or right it's a halo node
897 RegisterHaloNode(global_node_index);
898 mHaloNodes.push_back(p_node);
899 }
900 else
901 {
902 RegisterNode(global_node_index);
903 this->mNodes.push_back(p_node);
904 }
905 if (is_boundary)
906 {
907 this->mBoundaryNodes.push_back(p_node);
908 }
909 }
910 }
911
912 //Construct the boundary elements
913 unsigned belem_index;
914 //Top
915 if (am_top_most)
916 {
917 for (unsigned i=0; i<width; i++)
918 {
919 std::vector<Node<SPACE_DIM>*> nodes;
920 nodes.push_back(GetNodeOrHaloNode( height*(width+1)+i+1 ));
921 nodes.push_back(GetNodeOrHaloNode( height*(width+1)+i ));
922 belem_index=i;
923 RegisterBoundaryElement(belem_index);
924 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
925 }
926 }
927
928 //Right
929 for (unsigned j=lo_y+1; j<hi_y; j++)
930 {
931 std::vector<Node<SPACE_DIM>*> nodes;
932 nodes.push_back(GetNodeOrHaloNode( (width+1)*j-1 ));
933 nodes.push_back(GetNodeOrHaloNode( (width+1)*(j+1)-1 ));
934 belem_index=width+j-1;
935 RegisterBoundaryElement(belem_index);
936 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
937 }
938
939 //Bottom
941 {
942 for (unsigned i=0; i<width; i++)
943 {
944 std::vector<Node<SPACE_DIM>*> nodes;
945 nodes.push_back(GetNodeOrHaloNode( i ));
946 nodes.push_back(GetNodeOrHaloNode( i+1 ));
947 belem_index=width+height+i;
948 RegisterBoundaryElement(belem_index);
949 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
950 }
951 }
952
953 //Left
954 for (unsigned j=lo_y; j<hi_y-1; j++)
955 {
956 std::vector<Node<SPACE_DIM>*> nodes;
957 nodes.push_back(GetNodeOrHaloNode( (width+1)*(j+1) ));
958 nodes.push_back(GetNodeOrHaloNode( (width+1)*(j) ));
959 belem_index=2*width+height+j;
960 RegisterBoundaryElement(belem_index);
961 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index,nodes));
962 }
963
964
965 //Construct the elements
966 unsigned elem_index;
967 for (unsigned j=lo_y; j<hi_y-1; j++)
968 {
969 for (unsigned i=0; i<width; i++)
970 {
971 unsigned parity=(i+(height-j))%2;//Note that parity is measured from the top-left (not bottom left) for historical reasons
972 unsigned nw=(j+1)*(width+1)+i; //ne=nw+1
973 unsigned sw=(j)*(width+1)+i; //se=sw+1
974 std::vector<Node<SPACE_DIM>*> upper_nodes;
975 upper_nodes.push_back(GetNodeOrHaloNode( nw ));
976 upper_nodes.push_back(GetNodeOrHaloNode( nw+1 ));
977 if (stagger==false || parity == 1)
978 {
979 upper_nodes.push_back(GetNodeOrHaloNode( sw+1 ));
980 }
981 else
982 {
983 upper_nodes.push_back(GetNodeOrHaloNode( sw ));
984 }
985 elem_index=2*(j*width+i);
986 RegisterElement(elem_index);
987 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index,upper_nodes));
988 std::vector<Node<SPACE_DIM>*> lower_nodes;
989 lower_nodes.push_back(GetNodeOrHaloNode( sw+1 ));
990 lower_nodes.push_back(GetNodeOrHaloNode( sw ));
991 if (stagger==false ||parity == 1)
992 {
993 lower_nodes.push_back(GetNodeOrHaloNode( nw ));
994 }
995 else
996 {
997 lower_nodes.push_back(GetNodeOrHaloNode( nw+1 ));
998 }
999 elem_index++;
1000 RegisterElement(elem_index);
1001 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index,lower_nodes));
1002 }
1003 }
1004 }
1005}
1006
1007
1008template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1010 unsigned height,
1011 unsigned depth)
1012{
1013 assert(SPACE_DIM == 3); // LCOV_EXCL_LINE
1014 assert(ELEMENT_DIM == 3); // LCOV_EXCL_LINE
1015 //Check that there are enough nodes to make the parallelisation worthwhile
1016 if (depth==0)
1017 {
1018 EXCEPTION("There aren't enough nodes to make parallelisation worthwhile");
1019 }
1020
1021 // Hook to pick up when we are using a geometric partition.
1022 if (mPartitioning == DistributedTetrahedralMeshPartitionType::GEOMETRIC)
1023 {
1024 if (!mpSpaceRegion)
1025 {
1026 EXCEPTION("Space region not set for GEOMETRIC partition of DistributedTetrahedralMesh");
1027 }
1028
1029 // Write a serial file, the load on distributed processors.
1031 {
1032 TrianglesMeshWriter<ELEMENT_DIM,SPACE_DIM> mesh_writer("", "temp_cuboid_mesh");
1034 base_mesh.ConstructCuboid(width, height, depth);
1035 mesh_writer.WriteFilesUsingMesh(base_mesh);
1036 }
1038
1039 OutputFileHandler output_handler("", false);
1040
1041 std::string output_dir = output_handler.GetOutputDirectoryFullPath();
1042 TrianglesMeshReader<ELEMENT_DIM,SPACE_DIM> mesh_reader(output_dir+"temp_cuboid_mesh");
1043
1044 this->ConstructFromMeshReader(mesh_reader);
1045 }
1046 else
1047 {
1048 //Use dumb partition so that archiving doesn't permute anything
1049 mPartitioning=DistributedTetrahedralMeshPartitionType::DUMB;
1050
1051 mTotalNumNodes=(width+1)*(height+1)*(depth+1);
1052 mTotalNumBoundaryElements=((width*height)+(width*depth)+(height*depth))*4;//*2 for top-bottom, *2 for tessellating each unit square
1053 mTotalNumElements=width*height*depth*6;
1054
1055 //Use DistributedVectorFactory to make a dumb partition of space
1056 DistributedVectorFactory z_partition(depth+1);
1057 unsigned lo_z = z_partition.GetLow();
1058 unsigned hi_z = z_partition.GetHigh();
1059
1060 //Dumb partition of nodes has to be such that each process gets complete slices
1061 assert(!this->mpDistributedVectorFactory);
1062 this->mpDistributedVectorFactory = new DistributedVectorFactory(mTotalNumNodes, (width+1)*(height+1)*z_partition.GetLocalOwnership());
1063 if (this->mpDistributedVectorFactory->GetLocalOwnership() == 0)
1064 {
1065 // It's a short mesh and this process owns no nodes.
1066 // This return cannot be covered by regular testing, but is covered by the Nightly -np 3 builder
1067 return; //LCOV_EXCL_LINE
1068 }
1069
1070 /* am_top_most is like PetscTools::AmTopMost() but accounts for the fact that a
1071 * higher numbered process may have dropped out of this construction altogether
1072 * (because is has no local ownership)
1073 */
1074 bool am_top_most = (this->mpDistributedVectorFactory->GetHigh() == mTotalNumNodes);
1075
1076 if (!PetscTools::AmMaster())
1077 {
1078 //Allow for a halo node
1079 lo_z--;
1080 }
1081 if (!am_top_most)
1082 {
1083 //Allow for a halo node
1084 hi_z++;
1085 }
1086
1087 //Construct the nodes
1088 unsigned global_node_index;
1089 for (unsigned k=lo_z; k<hi_z; k++)
1090 {
1091 for (unsigned j=0; j<height+1; j++)
1092 {
1093 for (unsigned i=0; i<width+1; i++)
1094 {
1095 bool is_boundary = false;
1096 if (i==0 || j==0 || k==0 || i==width || j==height || k==depth)
1097 {
1098 is_boundary = true;
1099 }
1100 global_node_index = (k*(height+1)+j)*(width+1)+i;
1101
1102 Node<SPACE_DIM>* p_node = new Node<SPACE_DIM>(global_node_index, is_boundary, i, j, k);
1103
1104 if (k<z_partition.GetLow() || k==z_partition.GetHigh() )
1105 {
1106 //Beyond left or right it's a halo node
1107 RegisterHaloNode(global_node_index);
1108 mHaloNodes.push_back(p_node);
1109 }
1110 else
1111 {
1112 RegisterNode(global_node_index);
1113 this->mNodes.push_back(p_node);
1114 }
1115
1116 if (is_boundary)
1117 {
1118 this->mBoundaryNodes.push_back(p_node);
1119 }
1120 }
1121 }
1122 }
1123
1124 // Construct the elements
1125
1126 unsigned element_nodes[6][4] = {{0, 1, 5, 7}, {0, 1, 3, 7},
1127 {0, 2, 3, 7}, {0, 2, 6, 7},
1128 {0, 4, 6, 7}, {0, 4, 5, 7}};
1129 std::vector<Node<SPACE_DIM>*> tetrahedra_nodes;
1130
1131 for (unsigned k=lo_z; k<hi_z-1; k++)
1132 {
1133 unsigned belem_index = 0;
1134 if (k != 0)
1135 {
1136 // height*width squares on upper face, k layers of 2*height+2*width square aroun
1137 belem_index = 2*(height*width+k*2*(height+width));
1138 }
1139
1140 for (unsigned j=0; j<height; j++)
1141 {
1142 for (unsigned i=0; i<width; i++)
1143 {
1144 // Compute the nodes' index
1145 unsigned global_node_indices[8];
1146 unsigned local_node_index = 0;
1147
1148 for (unsigned z = 0; z < 2; z++)
1149 {
1150 for (unsigned y = 0; y < 2; y++)
1151 {
1152 for (unsigned x = 0; x < 2; x++)
1153 {
1154 global_node_indices[local_node_index] = i+x+(width+1)*(j+y+(height+1)*(k+z));
1155
1156 local_node_index++;
1157 }
1158 }
1159 }
1160
1161 for (unsigned m = 0; m < 6; m++)
1162 {
1163 // Tetrahedra #m
1164
1165 tetrahedra_nodes.clear();
1166
1167 for (unsigned n = 0; n < 4; n++)
1168 {
1169 tetrahedra_nodes.push_back(GetNodeOrHaloNode( global_node_indices[element_nodes[m][n]] ));
1170 }
1171 unsigned elem_index = 6 * ((k*height+j)*width+i)+m;
1172 RegisterElement(elem_index);
1173 this->mElements.push_back(new Element<ELEMENT_DIM,SPACE_DIM>(elem_index, tetrahedra_nodes));
1174 }
1175
1176 //Are we at a boundary?
1177 std::vector<Node<SPACE_DIM>*> triangle_nodes;
1178
1179 if (i == 0) //low face at x==0
1180 {
1181 triangle_nodes.clear();
1182 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1183 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
1184 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
1185 RegisterBoundaryElement(belem_index);
1186 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1187 triangle_nodes.clear();
1188 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1189 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
1190 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
1191 RegisterBoundaryElement(belem_index);
1192 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1193 }
1194 if (i == width-1) //high face at x=width
1195 {
1196 triangle_nodes.clear();
1197 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
1198 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
1199 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1200 RegisterBoundaryElement(belem_index);
1201 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1202 triangle_nodes.clear();
1203 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
1204 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1205 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
1206 RegisterBoundaryElement(belem_index);
1207 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1208 }
1209 if (j == 0) //low face at y==0
1210 {
1211 triangle_nodes.clear();
1212 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1213 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
1214 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
1215 RegisterBoundaryElement(belem_index);
1216 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1217 triangle_nodes.clear();
1218 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1219 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
1220 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
1221 RegisterBoundaryElement(belem_index);
1222 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1223 }
1224 if (j == height-1) //high face at y=height
1225 {
1226 triangle_nodes.clear();
1227 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
1228 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
1229 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1230 RegisterBoundaryElement(belem_index);
1231 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1232 triangle_nodes.clear();
1233 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
1234 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1235 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
1236 RegisterBoundaryElement(belem_index);
1237 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1238 }
1239 if (k == 0) //low face at z==0
1240 {
1241 triangle_nodes.clear();
1242 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1243 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
1244 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[2] ));
1245 RegisterBoundaryElement(belem_index);
1246 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1247 triangle_nodes.clear();
1248 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[0] ));
1249 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[1] ));
1250 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[3] ));
1251 RegisterBoundaryElement(belem_index);
1252 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1253 }
1254 if (k == depth-1) //high face at z=depth
1255 {
1256 triangle_nodes.clear();
1257 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
1258 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1259 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[5] ));
1260 RegisterBoundaryElement(belem_index);
1261 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1262 triangle_nodes.clear();
1263 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[4] ));
1264 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[6] ));
1265 triangle_nodes.push_back(GetNodeOrHaloNode( global_node_indices[7] ));
1266 RegisterBoundaryElement(belem_index);
1267 this->mBoundaryElements.push_back(new BoundaryElement<ELEMENT_DIM-1,SPACE_DIM>(belem_index++,triangle_nodes));
1268 }
1269 }//i
1270 }//j
1271 }//k
1272 }
1273}
1274
1275template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1276void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Scale(const double xFactor, const double yFactor, const double zFactor)
1277{
1278 //Base class scale (scales node positions)
1280 //Scales halos
1281 for (unsigned i=0; i<mHaloNodes.size(); i++)
1282 {
1283 c_vector<double, SPACE_DIM>& r_location = mHaloNodes[i]->rGetModifiableLocation();
1284 if (SPACE_DIM>=3)
1285 {
1286 r_location[2] *= zFactor;
1287 }
1288 if (SPACE_DIM>=2)
1289 {
1290 r_location[1] *= yFactor;
1291 }
1292 r_location[0] *= xFactor;
1293 }
1294
1295}
1296
1297
1298template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1301 std::set<unsigned>& rElementsOwned,
1302 std::set<unsigned>& rNodesOwned,
1303 std::set<unsigned>& rHaloNodesOwned,
1304 std::vector<unsigned>& rProcessorsOffset)
1305{
1306 assert(PetscTools::IsParallel());
1307 assert(ELEMENT_DIM==2 || ELEMENT_DIM==3); // LCOV_EXCL_LINE // Partitioning works with triangles and tetras
1308
1309 const unsigned num_elements = rMeshReader.GetNumElements();
1310 const unsigned num_nodes = rMeshReader.GetNumNodes();
1311 const unsigned num_procs = PetscTools::GetNumProcs();
1312 const unsigned local_proc_index = PetscTools::GetMyRank();
1313
1314 /*
1315 * Work out initial element distribution
1316 */
1317 boost::scoped_array<idx_t> element_distribution(new idx_t[num_procs+1]);
1318 boost::scoped_array<int> element_counts(new int[num_procs]);
1319
1320 element_distribution[0] = 0;
1321
1322 for (unsigned proc_index=1; proc_index<num_procs; proc_index++)
1323 {
1324 element_distribution[proc_index] = element_distribution[proc_index-1] + num_elements/num_procs;
1325 element_counts[proc_index-1] = element_distribution[proc_index] - element_distribution[proc_index-1];
1326 }
1327
1328 element_distribution[num_procs] = num_elements;
1329 element_counts[num_procs-1] = element_distribution[num_procs] - element_distribution[num_procs-1];
1330
1331 /*
1332 * Create distributed mesh data structure
1333 */
1334 idx_t first_local_element = element_distribution[local_proc_index];
1335 idx_t last_plus_one_element = element_distribution[local_proc_index+1];
1336 idx_t num_local_elements = last_plus_one_element - first_local_element;
1337
1338 boost::scoped_array<idx_t> eind(new idx_t[num_local_elements*(ELEMENT_DIM+1)]);
1339 boost::scoped_array<idx_t> eptr(new idx_t[num_local_elements+1]);
1340
1341 if (rMeshReader.IsFileFormatBinary() && first_local_element > 0)
1342 {
1343 // Advance the file pointer to the first element before the ones I own.
1344 rMeshReader.GetElementData(first_local_element - 1);
1345 }
1346 else
1347 {
1348 // Advance the file pointer to the first element before the ones I own.
1349 for (idx_t element_index = 0; element_index < first_local_element; element_index++)
1350 {
1351 rMeshReader.GetNextElementData();
1352 }
1353 }
1354
1355#ifdef CHASTE_HOMEMADE_MESH_TO_DUAL
1356 // element_node_matrix is an encoding of the .ele file. Each row is an element with the
1357 // 3 or 4 adjacent nodes indicated by a 1 in the approciate column
1358 Mat element_node_matrix;
1359 PetscTools::SetupMat(element_node_matrix, num_elements, num_nodes, ELEMENT_DIM+1, num_local_elements);
1360#endif
1361
1362 unsigned counter = 0;
1363 for (idx_t element_index = 0; element_index < num_local_elements; element_index++)
1364 {
1365 ElementData element_data;
1366
1367 element_data = rMeshReader.GetNextElementData();
1368
1369 eptr[element_index] = counter;
1370 for (unsigned i=0; i<ELEMENT_DIM+1; i++)
1371 {
1372#ifdef CHASTE_HOMEMADE_MESH_TO_DUAL
1373 PetscMatTools::SetElement(element_node_matrix, element_index+first_local_element, element_data.NodeIndices[i], 1.0);
1374#else
1375 eind[counter++] = element_data.NodeIndices[i];
1376#endif
1377 }
1378 }
1379 eptr[num_local_elements] = counter;
1380
1381 rMeshReader.Reset();
1382 idx_t numflag = 0; // ParMETIS speak for C-style numbering
1383
1384 MPI_Comm communicator = PETSC_COMM_WORLD;
1385
1386 idx_t* xadj;
1387 idx_t* adjncy;
1388
1389 Timer::Reset();
1390#ifdef CHASTE_HOMEMADE_MESH_TO_DUAL
1391 PetscMatTools::Finalise(element_node_matrix);
1392 std::vector<idx_t> my_xadj;
1393 std::vector<idx_t> my_adjncy;
1394 /* The goal is for my_adjncy to contain, for each local element, a list of the
1395 * elements that it is adjacent to. These are contiguous but my_xadj will contain the
1396 * start index for the data of each local element.
1397 * The element_node_matrix contains data on which nodes support each element.
1398 * The dot-product of two rows of this matrix is an indication of how many nodes two elements share:
1399 * ELEMENT_DIM+1 if they are the same row and ELEMENT_DIM if they are neighbours.
1400 * These dot-products are achieved (slowly) with
1401 * element_element_matrix = element_node_matrix * transpose(element_node_matrix)
1402 *
1403 * Each entry of element_element_matrix shows how many nodes a pair of elements share.
1404 */
1405
1406 Mat node_element_matrix;
1407 MatTranspose(element_node_matrix, MAT_INITIAL_MATRIX, &node_element_matrix);
1408 Mat element_element_matrix;
1409 MatMatMult(element_node_matrix, node_element_matrix, MAT_INITIAL_MATRIX, PETSC_DETERMINE, &element_element_matrix);
1410 my_xadj.push_back(0);
1411 PetscInt ncols;
1412 const PetscInt *cols;
1413 const PetscScalar *vals;
1414 for (PetscInt el_index=first_local_element; el_index<last_plus_one_element; el_index++)
1415 {
1416 MatGetRow(element_element_matrix, el_index, &ncols, &cols, &vals);
1417 for (PetscInt i=0;i<ncols;i++)
1418 {
1419 if (std::lround(vals[i])==ELEMENT_DIM)
1420 {
1421 // Shared edge/face between two elements
1422 my_adjncy.push_back(cols[i]);
1423 }
1424 }
1425 MatRestoreRow(element_element_matrix, el_index, &ncols, &cols, NULL);
1426 // Mark where next local element starts
1427 my_xadj.push_back(my_adjncy.size());
1428 }
1429 MatDestroy(&element_node_matrix);
1430 MatDestroy(&node_element_matrix);
1431 MatDestroy(&element_element_matrix);
1432 xadj = my_xadj.data();
1433 adjncy = my_adjncy.data();
1434
1435#else
1436 // The default behaviour is to use a ParMETIS (or possible Scotch) function to get the dual
1437 /* Connectivity degree.
1438 * GRAPH EDGE is placed between any two elements if and only if they share at least this many nodes.
1439 */
1440 idx_t ncommonnodes = 3; //Linear tetrahedra
1441 if (ELEMENT_DIM == 2)
1442 {
1443 ncommonnodes = 2;
1444 }
1445 ParMETIS_V3_Mesh2Dual(element_distribution.get(), eptr.get(), eind.get(),
1446 &numflag, &ncommonnodes, &xadj, &adjncy, &communicator);
1447#endif
1448 //Timer::Print("ParMETIS Mesh2Dual");
1449 // Be more memory efficient, and get rid of (maybe large) arrays as soon as they're no longer needed, rather than at end of scope
1450 eind.reset();
1451 eptr.reset();
1452
1453 idx_t weight_flag = 0; // unweighted graph
1454 idx_t n_constraints = 1; // number of weights that each vertex has (number of balance constraints)
1455 idx_t n_subdomains = PetscTools::GetNumProcs();
1456 idx_t options[3]; // extra options
1457 options[0] = 0; // ignore extra options
1458 idx_t edgecut;
1459 boost::scoped_array<real_t> tpwgts(new real_t[n_subdomains]);
1460 real_t ubvec_value = (real_t)1.05;
1461 for (unsigned proc=0; proc<PetscTools::GetNumProcs(); proc++)
1462 {
1463 tpwgts[proc] = ((real_t)1.0)/n_subdomains;
1464 }
1465 boost::scoped_array<idx_t> local_partition(new idx_t[num_local_elements]);
1466
1467/*
1468 * In order to use ParMETIS_V3_PartGeomKway, we need to sort out how to compute the coordinates of the
1469 * centers of each element efficiently.
1470 *
1471 * In the meantime use ParMETIS_V3_PartKway.
1472 */
1473// int n_dimensions = ELEMENT_DIM;
1474// float node_coordinates[num_local_elements*SPACE_DIM];
1475//
1476// ParMETIS_V3_PartGeomKway(element_distribution, xadj, adjncy, NULL, NULL, &weight_flag, &numflag,
1477// &n_dimensions, node_coordinates, &n_constraints, &n_subdomains, NULL, NULL,
1478// options, &edgecut, local_partition, &communicator);
1479
1480 Timer::Reset();
1481 ParMETIS_V3_PartKway(element_distribution.get(), xadj, adjncy, nullptr, nullptr, &weight_flag, &numflag,
1482 &n_constraints, &n_subdomains, tpwgts.get(), &ubvec_value,
1483 options, &edgecut, local_partition.get(), &communicator);
1484 //Timer::Print("ParMETIS PartKway");
1485 tpwgts.reset();
1486
1487 boost::scoped_array<idx_t> global_element_partition(new idx_t[num_elements]);
1488
1489 //idx_t is normally int (see metis-4.0/Lib/struct.h 17-22) but is 64bit on Windows
1490 MPI_Datatype mpi_idx_t = MPI_LONG_LONG_INT;
1491 if (sizeof(idx_t) == sizeof(int))
1492 {
1493 mpi_idx_t = MPI_INT;
1494 }
1495 boost::scoped_array<int> int_element_distribution(new int[num_procs+1]);
1496 for (unsigned i=0; i<num_procs+1; ++i)
1497 {
1498 int_element_distribution[i] = element_distribution[i];
1499 }
1500 MPI_Allgatherv(local_partition.get(), num_local_elements, mpi_idx_t,
1501 global_element_partition.get(), element_counts.get(), int_element_distribution.get(), mpi_idx_t, PETSC_COMM_WORLD);
1502
1503 local_partition.reset();
1504
1505 for (unsigned elem_index=0; elem_index<num_elements; elem_index++)
1506 {
1507 if ((unsigned) global_element_partition[elem_index] == local_proc_index)
1508 {
1509 rElementsOwned.insert(elem_index);
1510 }
1511 }
1512
1513 rMeshReader.Reset();
1514#ifdef CHASTE_HOMEMADE_MESH_TO_DUAL
1515 // These are contained in std::vectors that are automatically freed
1516 xadj = NULL;
1517 adjncy = NULL;
1518#endif
1519 free(xadj);
1520 free(adjncy);
1521 //unsigned num_nodes = rMeshReader.GetNumNodes();
1522
1523 // Initialise with no nodes known
1524 std::vector<unsigned> global_node_partition(num_nodes, UNASSIGNED_NODE);
1525
1526 assert(rProcessorsOffset.size() == 0); // Making sure the vector is empty. After calling resize() only newly created memory will be initialised to 0.
1527 rProcessorsOffset.resize(PetscTools::GetNumProcs(), 0);
1528
1529 /*
1530 * Work out node distribution based on initial element distribution returned by ParMETIS
1531 *
1532 * In this loop we handle 4 different data structures:
1533 * global_node_partition and rProcessorsOffset are global,
1534 * rNodesOwned and rHaloNodesOwned are local.
1535 */
1536
1537 /*
1538 * Note that at this point each process has to read the entire element file in order to compute
1539 * the node partition form the initial element distribution.
1540 * * Previously we randomly permuted the BIN file element access order on each process so that the processes
1541 * weren't reading the same file sectors at the same time
1542 * * We noted that with large files (above about 0.5 GigaByte) on HECToR the random access file reader
1543 * was spending all its time in fseekg. This is presumably because each fseekg from the start of the file
1544 * involves multiple levels of indirect file block pointers.
1545 * * Hence the use of random element reading is only useful for the niche of moderately large meshes with
1546 * process counts in the thousands.
1547 * Hence BIN file element permuting is deprecated - we just read the file in order.
1548 * See
1549 * https://github.com/Chaste/Old-Chaste-svn-mirror/blob/554dbbf5cb7e95105aa8a6f48ee57551edea2a8a/mesh/src/common/DistributedTetrahedralMesh.cpp#L1459
1550 */
1551
1552 for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
1553 {
1554 unsigned element_owner = global_element_partition[element_number];
1555
1556 ElementData element_data;
1557
1558 element_data = rMeshReader.GetNextElementData();
1559
1560 for (std::vector<unsigned>::const_iterator node_it = element_data.NodeIndices.begin();
1561 node_it != element_data.NodeIndices.end();
1562 ++node_it)
1563 {
1564 /*
1565 * For each node in this element, check whether it hasn't been assigned to another processor yet.
1566 * If so, assign it to the owner of the element. Otherwise, consider it halo.
1567 */
1568 if (global_node_partition[*node_it] == UNASSIGNED_NODE)
1569 {
1570 if (element_owner == local_proc_index)
1571 {
1572 rNodesOwned.insert(*node_it);
1573 }
1574
1575 global_node_partition[*node_it] = element_owner;
1576
1577 // Offset is defined as the first node owned by a processor. We compute it incrementally.
1578 // i.e. if node_index belongs to proc 3 (of 6) we have to shift the processors 4, 5, and 6
1579 // offset a position.
1580 for (unsigned proc=element_owner+1; proc<PetscTools::GetNumProcs(); proc++)
1581 {
1582 rProcessorsOffset[proc]++;
1583 }
1584 }
1585 else
1586 {
1587 if (element_owner == local_proc_index)
1588 {
1589 //if (rNodesOwned.find(*node_it) == rNodesOwned.end())
1590 if (global_node_partition[*node_it] != local_proc_index)
1591 {
1592 rHaloNodesOwned.insert(*node_it);
1593 }
1594 }
1595 }
1596 }
1597 }
1598
1599
1600 /*
1601 * Refine element distribution. Add extra elements that parMETIS didn't consider initially but
1602 * include any node owned by the processor. This ensures that all the system matrix rows are
1603 * assembled locally.
1604 * It may be that some of these elements are in the set of owned nodes erroneously.
1605 * The original set of owned elements (from the k-way partition) informed a
1606 * node partition. It may be that an element near the edge of this new node
1607 * partition may no longer be needed.
1608 *
1609 * Note that rather than clearing the set we could remove elements to the original element partition set
1610 * with erase(), if (!element_owned) below.
1611 */
1612 rElementsOwned.clear();
1613 rMeshReader.Reset();
1614 for (unsigned element_number = 0; element_number < mTotalNumElements; element_number++)
1615 {
1616 ElementData element_data = rMeshReader.GetNextElementData();
1617
1618 bool element_owned = false;
1619 std::set<unsigned> temp_halo_nodes;
1620
1621 for (std::vector<unsigned>::const_iterator node_it = element_data.NodeIndices.begin();
1622 node_it != element_data.NodeIndices.end();
1623 ++node_it)
1624 {
1625 if (rNodesOwned.find(*node_it) != rNodesOwned.end())
1626 {
1627 element_owned = true;
1628 rElementsOwned.insert(element_number);
1629 }
1630 else
1631 {
1632 temp_halo_nodes.insert(*node_it);
1633 }
1634 }
1635
1636 if (element_owned)
1637 {
1638 rHaloNodesOwned.insert(temp_halo_nodes.begin(), temp_halo_nodes.end());
1639 }
1640 }
1641
1642 rMeshReader.Reset();
1643
1644 /*
1645 * Once we know the offsets we can compute the permutation vector
1646 */
1647 std::vector<unsigned> local_index(PetscTools::GetNumProcs(), 0);
1648
1649 this->mNodePermutation.resize(this->GetNumNodes());
1650
1651 for (unsigned node_index=0; node_index<this->GetNumNodes(); node_index++)
1652 {
1653 unsigned partition = global_node_partition[node_index];
1654 assert(partition != UNASSIGNED_NODE);
1655
1656 this->mNodePermutation[node_index] = rProcessorsOffset[partition] + local_index[partition];
1657
1658 local_index[partition]++;
1659 }
1660}
1661
1662template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1664{
1665 ChastePoint<SPACE_DIM> my_minimum_point {};
1666 ChastePoint<SPACE_DIM> my_maximum_point {};
1667
1668 try
1669 {
1671 my_minimum_point=my_box.rGetLowerCorner();
1672 my_maximum_point=my_box.rGetUpperCorner();
1673 }
1674 // LCOV_EXCL_START
1675 catch (Exception& e)
1676 {
1678 throw e;
1679
1680 }
1681 // LCOV_EXCL_STOP
1682
1684
1685 c_vector<double, SPACE_DIM> global_minimum_point;
1686 c_vector<double, SPACE_DIM> global_maximum_point;
1687 MPI_Allreduce(&my_minimum_point.rGetLocation()[0], &global_minimum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
1688 MPI_Allreduce(&my_maximum_point.rGetLocation()[0], &global_maximum_point[0], SPACE_DIM, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
1689
1690 ChastePoint<SPACE_DIM> min(global_minimum_point);
1691 ChastePoint<SPACE_DIM> max(global_maximum_point);
1692
1693 return ChasteCuboid<SPACE_DIM>(min, max);
1694}
1695
1696template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1698{
1699 // Call base method to find closest on local processor
1700 unsigned best_node_index = AbstractMesh<ELEMENT_DIM, SPACE_DIM>::GetNearestNodeIndex(rTestPoint);
1701
1702 // Recalculate the distance to the best node (if this process has one)
1703 double best_node_point_distance = DBL_MAX;
1704 if (best_node_index != UINT_MAX)
1705 {
1706 best_node_point_distance = norm_2(this->GetNode(best_node_index)->rGetLocation() - rTestPoint.rGetLocation());
1707 }
1708
1709
1710 // This is a handy data structure that will work with MPI_DOUBLE_INT data type.
1711 // There is no MPI_DOUBLE_UNSIGNED
1712 struct
1713 {
1714 double distance;
1715 int node_index;
1716 } value, minval;
1717
1718 value.node_index = best_node_index;
1719 value.distance = best_node_point_distance;
1720
1721 MPI_Allreduce( &value, &minval, 1, MPI_DOUBLE_INT, MPI_MINLOC, MPI_COMM_WORLD );
1722
1723 return minval.node_index;
1724}
1725
1726template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1728{
1730 c_vector<double, 2> global_min_max;
1731
1732 MPI_Allreduce(&local_min_max[0], &global_min_max[0], 1, MPI_DOUBLE, MPI_MIN, PETSC_COMM_WORLD);
1733 MPI_Allreduce(&local_min_max[1], &global_min_max[1], 1, MPI_DOUBLE, MPI_MAX, PETSC_COMM_WORLD);
1734
1735 return global_min_max;
1736}
1737
1738template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1743
1744template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1745void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Rotate(c_matrix<double, SPACE_DIM, SPACE_DIM> rotationMatrix)
1746{
1747 // First do the extras
1748 for (unsigned i=0; i<this->mHaloNodes.size(); i++)
1749 {
1750 c_vector<double, SPACE_DIM>& r_location = this->mHaloNodes[i]->rGetModifiableLocation();
1751 r_location = prod(rotationMatrix, r_location);
1752 }
1753 // Now a copy of the base class implementation
1754 for (unsigned i=0; i<this->mNodes.size(); i++)
1755 {
1756 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
1757 r_location = prod(rotationMatrix, r_location);
1758 }
1759}
1760
1761template<unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1762void DistributedTetrahedralMesh<ELEMENT_DIM, SPACE_DIM>::Translate(const c_vector<double, SPACE_DIM>& rDisplacement)
1763{
1764 // First do the extras
1765 for (unsigned i=0; i<this->mHaloNodes.size(); i++)
1766 {
1767 c_vector<double, SPACE_DIM>& r_location = this->mHaloNodes[i]->rGetModifiableLocation();
1768 r_location += rDisplacement;
1769 }
1770 // Now a copy of the base class implementation
1771 for (unsigned i=0; i<this->mNodes.size(); i++)
1772 {
1773 c_vector<double, SPACE_DIM>& r_location = this->mNodes[i]->rGetModifiableLocation();
1774 r_location += rDisplacement;
1775 }
1776}
1777
1778template <unsigned ELEMENT_DIM, unsigned SPACE_DIM>
1783
1784// Explicit instantiation
1785template class DistributedTetrahedralMesh<1,1>;
1786template class DistributedTetrahedralMesh<1,2>;
1787template class DistributedTetrahedralMesh<1,3>;
1788template class DistributedTetrahedralMesh<2,2>;
1789template class DistributedTetrahedralMesh<2,3>;
1790template class DistributedTetrahedralMesh<3,3>;
1791
1792// Serialization for Boost >= 1.36
#define EXCEPTION(message)
#define EXPORT_TEMPLATE_CLASS_ALL_DIMS(CLASS)
void SetAttribute(double attribute)
ElementIterator GetElementIteratorBegin()
virtual void Reset()=0
virtual unsigned GetNumElements() const =0
virtual std::vector< unsigned > GetContainingElementIndices(unsigned index)
virtual ElementData GetNextElementData()=0
virtual std::string GetMeshFileBaseName()
virtual unsigned GetNumFaces() const =0
NodeIterator GetNodeIteratorEnd()
virtual unsigned GetNumElementAttributes() const
virtual std::vector< double > GetNextNode()=0
virtual const std::vector< unsigned > & rGetNodePermutation()
virtual std::vector< double > GetNodeAttributes()
virtual unsigned GetNumFaceAttributes() const
virtual bool HasNodePermutation()
virtual bool IsFileFormatBinary()
virtual unsigned GetNumNodes() const =0
virtual ElementData GetNextFaceData()=0
NodeIterator GetNodeIteratorBegin()
virtual ElementData GetElementData(unsigned index)
ElementIterator GetElementIteratorEnd()
virtual void SetDistributedVectorFactory(DistributedVectorFactory *pFactory)
virtual ChasteCuboid< SPACE_DIM > CalculateBoundingBox() const
virtual unsigned GetNearestNodeIndex(const ChastePoint< SPACE_DIM > &rTestPoint)
virtual void Scale(const double xFactor=1.0, const double yFactor=1.0, const double zFactor=1.0)
virtual void WriteFilesUsingMesh(AbstractTetrahedralMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, bool keepOriginalElementIndexing=true)
virtual c_vector< double, 2 > CalculateMinMaxEdgeLengths()
virtual void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
virtual void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
virtual void ConstructLinearMesh(unsigned width)
const ChastePoint< SPACE_DIM > & rGetUpperCorner() const
const ChastePoint< SPACE_DIM > & rGetLowerCorner() const
c_vector< double, DIM > & rGetLocation()
bool CalculateDesignatedOwnershipOfElement(unsigned elementIndex)
virtual void ConstructFromMeshReader(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader)
ChasteCuboid< SPACE_DIM > * GetProcessRegion()
virtual c_vector< double, 2 > CalculateMinMaxEdgeLengths()
Node< SPACE_DIM > * GetNodeOrHaloNode(unsigned index) const
void ComputeMeshPartitioning(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader, std::set< unsigned > &rNodesOwned, std::set< unsigned > &rHaloNodesOwned, std::set< unsigned > &rElementsOwned, std::vector< unsigned > &rProcessorsOffset)
virtual unsigned GetNearestNodeIndex(const ChastePoint< SPACE_DIM > &rTestPoint)
HaloNodeIterator GetHaloNodeIteratorEnd() const
unsigned SolveElementMapping(unsigned index) const
void ConstructRectangularMesh(unsigned width, unsigned height, bool stagger=true)
void Translate(const c_vector< double, SPACE_DIM > &rDisplacement)
DistributedTetrahedralMeshPartitionType::type GetPartitionType() const
unsigned SolveBoundaryElementMapping(unsigned index) const
void SetDistributedVectorFactory(DistributedVectorFactory *pFactory)
bool CalculateDesignatedOwnershipOfBoundaryElement(unsigned faceIndex)
HaloNodeIterator GetHaloNodeIteratorBegin() const
DistributedTetrahedralMesh(DistributedTetrahedralMeshPartitionType::type partitioningMethod=DistributedTetrahedralMeshPartitionType::PARMETIS_LIBRARY)
unsigned SolveNodeMapping(unsigned index) const
void ParMetisLibraryNodeAndElementPartitioning(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader, std::set< unsigned > &rElementsOwned, std::set< unsigned > &rNodesOwned, std::set< unsigned > &rHaloNodesOwned, std::vector< unsigned > &rProcessorsOffset)
virtual void Scale(const double xFactor=1.0, const double yFactor=1.0, const double zFactor=1.0)
void ConstructCuboid(unsigned width, unsigned height, unsigned depth)
void Rotate(c_matrix< double, SPACE_DIM, SPACE_DIM > rotationMatrix)
std::vector< Node< SPACE_DIM > * >::const_iterator HaloNodeIterator
void SetProcessRegion(ChasteCuboid< SPACE_DIM > *pRegion)
void GetHaloNodeIndices(std::vector< unsigned > &rHaloIndices) const
virtual ChasteCuboid< SPACE_DIM > CalculateBoundingBox() const
DistributedTetrahedralMeshPartitionType::type mPartitioning
static void DumbPartitioning(AbstractMesh< ELEMENT_DIM, SPACE_DIM > &rMesh, std::set< unsigned > &rNodesOwned)
static void PetscMatrixPartitioning(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader, std::vector< unsigned > &rNodePermutation, std::set< unsigned > &rNodesOwned, std::vector< unsigned > &rProcessorsOffset)
static void GeometricPartitioning(AbstractMeshReader< ELEMENT_DIM, SPACE_DIM > &rMeshReader, std::vector< unsigned > &rNodePermutation, std::set< unsigned > &rNodesOwned, std::vector< unsigned > &rProcessorsOffset, ChasteCuboid< SPACE_DIM > *pRegion)
Definition Node.hpp:59
void AddNodeAttribute(double attribute)
Definition Node.cpp:170
std::string GetOutputDirectoryFullPath() const
static void Finalise(Mat matrix)
static void SetElement(Mat matrix, PetscInt row, PetscInt col, double value)
static bool AmMaster()
static bool AmTopMost()
static void Barrier(const std::string callerId="")
static bool IsParallel()
static unsigned GetMyRank()
static void ReplicateException(bool flag)
static void SetupMat(Mat &rMat, int numRows, int numColumns, unsigned rowPreallocation, int numLocalRows=PETSC_DECIDE, int numLocalColumns=PETSC_DECIDE, bool ignoreOffProcEntries=true, bool newAllocationError=true)
static unsigned GetNumProcs()
static bool HasParMetis()
static void PrintAndReset(std::string message)
Definition Timer.cpp:70
static void Reset()
Definition Timer.cpp:44
std::vector< unsigned > NodeIndices