1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced 2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files 3 // LICENSE and NOTICE for details. LLNL-CODE-806117. 4 // 5 // This file is part of the MFEM library. For more information and source code 6 // availability visit https://mfem.org. 7 // 8 // MFEM is free software; you can redistribute it and/or modify it under the 9 // terms of the BSD-3 license. We welcome feedback and contributions, see file 10 // CONTRIBUTING.md for details. 11 12 #ifndef MFEM_PMESH 13 #define MFEM_PMESH 14 15 #include "../config/config.hpp" 16 17 #ifdef MFEM_USE_MPI 18 19 #include "../general/communication.hpp" 20 #include "../general/globals.hpp" 21 #include "mesh.hpp" 22 #include "pncmesh.hpp" 23 #include <iostream> 24 25 namespace mfem 26 { 27 #ifdef MFEM_USE_PUMI 28 class ParPumiMesh; 29 #endif 30 31 /// Class for parallel meshes 32 class ParMesh : public Mesh 33 { 34 protected: 35 MPI_Comm MyComm; 36 int NRanks, MyRank; 37 38 struct Vert3 39 { 40 int v[3]; 41 Vert3() = default; Vert3mfem::ParMesh::Vert342 Vert3(int v0, int v1, int v2) { v[0] = v0; v[1] = v1; v[2] = v2; } Setmfem::ParMesh::Vert343 void Set(int v0, int v1, int v2) { v[0] = v0; v[1] = v1; v[2] = v2; } Setmfem::ParMesh::Vert344 void Set(const int *w) { v[0] = w[0]; v[1] = w[1]; v[2] = w[2]; } 45 }; 46 47 struct Vert4 48 { 49 int v[4]; 50 Vert4() = default; Vert4mfem::ParMesh::Vert451 Vert4(int v0, int v1, int v2, int v3) 52 { v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3; } Setmfem::ParMesh::Vert453 void Set(int v0, int v1, int v2, int v3) 54 { v[0] = v0; v[1] = v1; v[2] = v2; v[3] = v3; } Setmfem::ParMesh::Vert455 void Set(const int *w) 56 { v[0] = w[0]; v[1] = w[1]; v[2] = w[2]; v[3] = w[3]; } 57 }; 58 59 Array<Element *> shared_edges; 60 // shared face id 'i' is: 61 // * triangle id 'i', if i < shared_trias.Size() 62 // * quad id 'i-shared_trias.Size()', otherwise 63 Array<Vert3> shared_trias; 64 Array<Vert4> shared_quads; 65 66 /// Shared objects in each group. 67 Table group_svert; 68 Table group_sedge; 69 Table group_stria; // contains shared triangle indices 70 Table group_squad; // contains shared quadrilateral indices 71 72 /// Shared to local index mapping. 73 Array<int> svert_lvert; 74 Array<int> sedge_ledge; 75 // sface ids: all triangles first, then all quads 76 Array<int> sface_lface; 77 78 IsoparametricTransformation FaceNbrTransformation; 79 80 // glob_elem_offset + local element number defines a global element numbering 81 mutable long glob_elem_offset, glob_offset_sequence; 82 void ComputeGlobalElementOffset() const; 83 84 /// Create from a nonconforming mesh. 85 ParMesh(const ParNCMesh &pncmesh); 86 87 // Convert the local 'meshgen' to a global one. 88 void ReduceMeshGen(); 89 90 // Determine sedge_ledge and sface_lface. 91 void FinalizeParTopo(); 92 93 // Mark all tets to ensure consistency across MPI tasks; also mark the 94 // shared and boundary triangle faces using the consistently marked tets. 95 virtual void MarkTetMeshForRefinement(DSTable &v_to_v); 96 97 /// Return a number(0-1) identifying how the given edge has been split 98 int GetEdgeSplittings(Element *edge, const DSTable &v_to_v, int *middle); 99 /// Append codes identifying how the given face has been split to @a codes 100 void GetFaceSplittings(const int *fv, const HashTable<Hashed2> &v_to_v, 101 Array<unsigned> &codes); 102 103 bool DecodeFaceSplittings(HashTable<Hashed2> &v_to_v, const int *v, 104 const Array<unsigned> &codes, int &pos); 105 106 void GetFaceNbrElementTransformation( 107 int i, IsoparametricTransformation *ElTr); 108 109 void GetGhostFaceTransformation( 110 FaceElementTransformations* FETr, Element::Type face_type, 111 Geometry::Type face_geom); 112 113 /// Update the groups after triangle refinement 114 void RefineGroups(const DSTable &v_to_v, int *middle); 115 116 /// Update the groups after tetrahedron refinement 117 void RefineGroups(int old_nv, const HashTable<Hashed2> &v_to_v); 118 119 void UniformRefineGroups2D(int old_nv); 120 121 // f2qf can be NULL if all faces are quads or there are no quad faces 122 void UniformRefineGroups3D(int old_nv, int old_nedges, 123 const DSTable &old_v_to_v, 124 const STable3D &old_faces, 125 Array<int> *f2qf); 126 127 void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face); 128 129 /// Refine a mixed 2D mesh uniformly. 130 virtual void UniformRefinement2D(); 131 132 /// Refine a mixed 3D mesh uniformly. 133 virtual void UniformRefinement3D(); 134 135 virtual void NURBSUniformRefinement(); 136 137 /// This function is not public anymore. Use GeneralRefinement instead. 138 virtual void LocalRefinement(const Array<int> &marked_el, int type = 3); 139 140 /// This function is not public anymore. Use GeneralRefinement instead. 141 virtual void NonconformingRefinement(const Array<Refinement> &refinements, 142 int nc_limit = 0); 143 144 virtual bool NonconformingDerefinement(Array<double> &elem_error, 145 double threshold, int nc_limit = 0, 146 int op = 1); 147 148 void RebalanceImpl(const Array<int> *partition); 149 150 void DeleteFaceNbrData(); 151 152 bool WantSkipSharedMaster(const NCMesh::Master &master) const; 153 154 /// Fills out partitioned Mesh::vertices 155 int BuildLocalVertices(const Mesh& global_mesh, const int *partitioning, 156 Array<int> &vert_global_local); 157 158 /// Fills out partitioned Mesh::elements 159 int BuildLocalElements(const Mesh& global_mesh, const int *partitioning, 160 const Array<int> &vert_global_local); 161 162 /// Fills out partitioned Mesh::boundary 163 int BuildLocalBoundary(const Mesh& global_mesh, const int *partitioning, 164 const Array<int> &vert_global_local, 165 Array<bool>& activeBdrElem, 166 Table* &edge_element); 167 168 void FindSharedFaces(const Mesh &mesh, const int* partition, 169 Array<int>& face_group, 170 ListOfIntegerSets& groups); 171 172 int FindSharedEdges(const Mesh &mesh, const int* partition, 173 Table* &edge_element, ListOfIntegerSets& groups); 174 175 int FindSharedVertices(const int *partition, Table* vertex_element, 176 ListOfIntegerSets& groups); 177 178 void BuildFaceGroup(int ngroups, const Mesh &mesh, 179 const Array<int>& face_group, 180 int &nstria, int &nsquad); 181 182 void BuildEdgeGroup(int ngroups, const Table& edge_element); 183 184 void BuildVertexGroup(int ngroups, const Table& vert_element); 185 186 void BuildSharedFaceElems(int ntri_faces, int nquad_faces, 187 const Mesh &mesh, int *partitioning, 188 const STable3D *faces_tbl, 189 const Array<int> &face_group, 190 const Array<int> &vert_global_local); 191 192 void BuildSharedEdgeElems(int nedges, Mesh &mesh, 193 const Array<int> &vert_global_local, 194 const Table *edge_element); 195 196 void BuildSharedVertMapping(int nvert, const Table* vert_element, 197 const Array<int> &vert_global_local); 198 199 /// Ensure that bdr_attributes and attributes agree across processors 200 void DistributeAttributes(Array<int> &attr); 201 202 void LoadSharedEntities(std::istream &input); 203 204 /// If the mesh is curved, make sure 'Nodes' is ParGridFunction. 205 /** Note that this method is not related to the public 'Mesh::EnsureNodes`.*/ 206 void EnsureParNodes(); 207 208 /// Internal function used in ParMesh::MakeRefined (and related constructor) 209 void MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type); 210 211 // Mark Mesh::Swap as protected, should use ParMesh::Swap to swap @a ParMesh 212 // objects. 213 using Mesh::Swap; 214 215 void Destroy(); 216 217 public: 218 /// Default constructor. Create an empty @a ParMesh. ParMesh()219 ParMesh() : MyComm(0), NRanks(0), MyRank(-1), glob_elem_offset(-1), 220 glob_offset_sequence(-1), have_face_nbr_data(false), pncmesh(NULL) { } 221 222 /// Create a parallel mesh by partitioning a serial Mesh. 223 /** The mesh is partitioned automatically or using external partitioning 224 data (the optional parameter 'partitioning_[i]' contains the desired MPI 225 rank for element 'i'). Automatic partitioning uses METIS for conforming 226 meshes and quick space-filling curve equipartitioning for nonconforming 227 meshes (elements of nonconforming meshes should ideally be ordered as a 228 sequence of face-neighbors). */ 229 ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_ = NULL, 230 int part_method = 1); 231 232 /** Copy constructor. Performs a deep copy of (almost) all data, so that the 233 source mesh can be modified (e.g. deleted, refined) without affecting the 234 new mesh. If 'copy_nodes' is false, use a shallow (pointer) copy for the 235 nodes, if present. */ 236 explicit ParMesh(const ParMesh &pmesh, bool copy_nodes = true); 237 238 /// Read a parallel mesh, each MPI rank from its own file/stream. 239 /** The @a refine parameter is passed to the method Mesh::Finalize(). */ 240 ParMesh(MPI_Comm comm, std::istream &input, bool refine = true); 241 242 /// Deprecated: see @a ParMesh::MakeRefined 243 MFEM_DEPRECATED 244 ParMesh(ParMesh *orig_mesh, int ref_factor, int ref_type); 245 246 /// Move constructor. Used for named constructors. 247 ParMesh(ParMesh &&mesh); 248 249 /// Move assignment operator. 250 ParMesh& operator=(ParMesh &&mesh); 251 252 /// Explicitly delete the copy assignment operator. 253 ParMesh& operator=(ParMesh &mesh) = delete; 254 255 /// Create a uniformly refined (by any factor) version of @a orig_mesh. 256 /** @param[in] orig_mesh The starting coarse mesh. 257 @param[in] ref_factor The refinement factor, an integer > 1. 258 @param[in] ref_type Specify the positions of the new vertices. The 259 options are BasisType::ClosedUniform or 260 BasisType::GaussLobatto. 261 262 The refinement data which can be accessed with GetRefinementTransforms() 263 is set to reflect the performed refinements. 264 265 @note The constructed ParMesh is linear, i.e. it does not have nodes. */ 266 static ParMesh MakeRefined(ParMesh &orig_mesh, int ref_factor, int ref_type); 267 268 /** Create a mesh by splitting each element of @a orig_mesh into simplices. 269 See @a Mesh::MakeSimplicial for more details. */ 270 static ParMesh MakeSimplicial(ParMesh &orig_mesh); 271 272 virtual void Finalize(bool refine = false, bool fix_orientation = false); 273 274 virtual void SetAttributes(); 275 GetComm() const276 MPI_Comm GetComm() const { return MyComm; } GetNRanks() const277 int GetNRanks() const { return NRanks; } GetMyRank() const278 int GetMyRank() const { return MyRank; } 279 280 /** Map a global element number to a local element number. If the global 281 element is not on this processor, return -1. */ 282 int GetLocalElementNum(long global_element_num) const; 283 284 /// Map a local element number to a global element number. 285 long GetGlobalElementNum(int local_element_num) const; 286 287 /** The following functions define global indices for all local vertices, 288 edges, faces, or elements. The global indices have no meaning or 289 significance for ParMesh, but can be used for purposes beyond this class. 290 */ 291 /// AMR meshes are not supported. 292 void GetGlobalVertexIndices(Array<HYPRE_Int> &gi) const; 293 /// AMR meshes are not supported. 294 void GetGlobalEdgeIndices(Array<HYPRE_Int> &gi) const; 295 /// AMR meshes are not supported. 296 void GetGlobalFaceIndices(Array<HYPRE_Int> &gi) const; 297 /// AMR meshes are supported. 298 void GetGlobalElementIndices(Array<HYPRE_Int> &gi) const; 299 300 GroupTopology gtopo; 301 302 // Face-neighbor elements and vertices 303 bool have_face_nbr_data; 304 Array<int> face_nbr_group; 305 Array<int> face_nbr_elements_offset; 306 Array<int> face_nbr_vertices_offset; 307 Array<Element *> face_nbr_elements; 308 Array<Vertex> face_nbr_vertices; 309 // Local face-neighbor elements and vertices ordered by face-neighbor 310 Table send_face_nbr_elements; 311 Table send_face_nbr_vertices; 312 313 ParNCMesh* pncmesh; 314 GetNGroups() const315 int GetNGroups() const { return gtopo.NGroups(); } 316 317 ///@{ @name These methods require group > 0 GroupNVertices(int group)318 int GroupNVertices(int group) { return group_svert.RowSize(group-1); } GroupNEdges(int group)319 int GroupNEdges(int group) { return group_sedge.RowSize(group-1); } GroupNTriangles(int group)320 int GroupNTriangles(int group) { return group_stria.RowSize(group-1); } GroupNQuadrilaterals(int group)321 int GroupNQuadrilaterals(int group) { return group_squad.RowSize(group-1); } 322 GroupVertex(int group,int i)323 int GroupVertex(int group, int i) 324 { return svert_lvert[group_svert.GetRow(group-1)[i]]; } 325 void GroupEdge(int group, int i, int &edge, int &o); 326 void GroupTriangle(int group, int i, int &face, int &o); 327 void GroupQuadrilateral(int group, int i, int &face, int &o); 328 ///@} 329 330 void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], 331 Array<HYPRE_BigInt> *offsets[]) const; 332 333 void ExchangeFaceNbrData(); 334 void ExchangeFaceNbrNodes(); 335 336 virtual void SetCurvature(int order, bool discont = false, int space_dim = -1, 337 int ordering = 1); 338 GetNFaceNeighbors() const339 int GetNFaceNeighbors() const { return face_nbr_group.Size(); } GetNFaceNeighborElements() const340 int GetNFaceNeighborElements() const { return face_nbr_elements.Size(); } GetFaceNbrGroup(int fn) const341 int GetFaceNbrGroup(int fn) const { return face_nbr_group[fn]; } 342 int GetFaceNbrRank(int fn) const; 343 344 /** Similar to Mesh::GetFaceToElementTable with added face-neighbor elements 345 with indices offset by the local number of elements. */ 346 Table *GetFaceToAllElementTable() const; 347 348 /** Get the FaceElementTransformations for the given shared face (edge 2D). 349 In the returned object, 1 and 2 refer to the local and the neighbor 350 elements, respectively. */ 351 FaceElementTransformations * 352 GetSharedFaceTransformations(int sf, bool fill2 = true); 353 354 ElementTransformation * GetFaceNbrElementTransformation(int i)355 GetFaceNbrElementTransformation(int i) 356 { 357 GetFaceNbrElementTransformation(i, &FaceNbrTransformation); 358 359 return &FaceNbrTransformation; 360 } 361 362 /// Get the size of the i-th face neighbor element relative to the reference 363 /// element. 364 double GetFaceNbrElementSize(int i, int type=0); 365 366 /// Return the number of shared faces (3D), edges (2D), vertices (1D) 367 int GetNSharedFaces() const; 368 369 /// Return the local face index for the given shared face. 370 int GetSharedFace(int sface) const; 371 372 /// See the remarks for the serial version in mesh.hpp 373 virtual void ReorientTetMesh(); 374 375 /// Utility function: sum integers from all processors (Allreduce). 376 virtual long ReduceInt(int value) const; 377 378 /** Load balance the mesh by equipartitioning the global space-filling 379 sequence of elements. Works for nonconforming meshes only. */ 380 void Rebalance(); 381 382 /** Load balance a nonconforming mesh using a user-defined partition. 383 Each local element 'i' is migrated to processor rank 'partition[i]', 384 for 0 <= i < GetNE(). */ 385 void Rebalance(const Array<int> &partition); 386 387 /// Save the mesh in a parallel mesh format. 388 void ParPrint(std::ostream &out) const; 389 390 /** Print the part of the mesh in the calling processor adding the interface 391 as boundary (for visualization purposes) using the mfem v1.0 format. */ 392 virtual void Print(std::ostream &out = mfem::out) const; 393 394 /// Save the ParMesh to files (one for each MPI rank). The files will be 395 /// given suffixes according to the MPI rank. The mesh will be written to the 396 /// files using ParMesh::Print. The given @a precision will be used for ASCII 397 /// output. 398 virtual void Save(const char *fname, int precision=16) const; 399 400 #ifdef MFEM_USE_ADIOS2 401 /** Print the part of the mesh in the calling processor using adios2 bp 402 format. */ 403 virtual void Print(adios2stream &out) const; 404 #endif 405 406 /** Print the part of the mesh in the calling processor adding the interface 407 as boundary (for visualization purposes) using Netgen/Truegrid format .*/ 408 virtual void PrintXG(std::ostream &out = mfem::out) const; 409 410 /** Write the mesh to the stream 'out' on Process 0 in a form suitable for 411 visualization: the mesh is written as a disjoint mesh and the shared 412 boundary is added to the actual boundary; both the element and boundary 413 attributes are set to the processor number. */ 414 void PrintAsOne(std::ostream &out = mfem::out) const; 415 416 /// Save the mesh as a single file (using ParMesh::PrintAsOne). The given 417 /// @a precision is used for ASCII output. 418 void SaveAsOne(const char *fname, int precision=16) const; 419 420 /// Old mesh format (Netgen/Truegrid) version of 'PrintAsOne' 421 void PrintAsOneXG(std::ostream &out = mfem::out); 422 423 /** Print the mesh in parallel PVTU format. The PVTU and VTU files will be 424 stored in the directory specified by @a pathname. If the directory does 425 not exist, it will be created. */ 426 virtual void PrintVTU(std::string pathname, 427 VTKFormat format=VTKFormat::ASCII, 428 bool high_order_output=false, 429 int compression_level=0, 430 bool bdr=false); 431 432 /// Parallel version of Mesh::Load(). 433 virtual void Load(std::istream &input, int generate_edges = 0, 434 int refine = 1, bool fix_orientation = true); 435 436 /// Returns the minimum and maximum corners of the mesh bounding box. For 437 /// high-order meshes, the geometry is refined first "ref" times. 438 void GetBoundingBox(Vector &p_min, Vector &p_max, int ref = 2); 439 440 void GetCharacteristics(double &h_min, double &h_max, 441 double &kappa_min, double &kappa_max); 442 443 /// Swaps internal data with another ParMesh, including non-geometry members. 444 /// See @a Mesh::Swap 445 void Swap(ParMesh &other); 446 447 /// Print various parallel mesh stats 448 virtual void PrintInfo(std::ostream &out = mfem::out); 449 450 virtual int FindPoints(DenseMatrix& point_mat, Array<int>& elem_ids, 451 Array<IntegrationPoint>& ips, bool warn = true, 452 InverseElementTransformation *inv_trans = NULL); 453 454 /// Debugging method 455 void PrintSharedEntities(const char *fname_prefix) const; 456 457 virtual ~ParMesh(); 458 459 friend class ParNCMesh; 460 #ifdef MFEM_USE_PUMI 461 friend class ParPumiMesh; 462 #endif 463 #ifdef MFEM_USE_ADIOS2 464 friend class adios2stream; 465 #endif 466 }; 467 468 } 469 470 #endif // MFEM_USE_MPI 471 472 #endif 473