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