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