Merge branch 'master' into hypre-runtime-compute-policy
This commit is contained in:
+1
-1
@@ -232,7 +232,7 @@ miniapps/meshing/mobius-strip.mesh
|
||||
miniapps/meshing/klein-bottle.mesh
|
||||
miniapps/meshing/toroid-*.mesh
|
||||
miniapps/meshing/twist-*.mesh
|
||||
miniapps/meshing/mesh-explorer.mesh
|
||||
miniapps/meshing/mesh-explorer.mesh*
|
||||
miniapps/meshing/partitioning.txt
|
||||
miniapps/meshing/mesh-explorer-visit*
|
||||
miniapps/meshing/mesh-explorer-paraview/
|
||||
|
||||
@@ -17,6 +17,10 @@ Meshing improvements
|
||||
arbitrary integer factors is also enabled, e.g. in the mesh-explorer miniapp.
|
||||
NURBS coarsening and knot removal are also introduced.
|
||||
|
||||
- Added the capability to partition (big) serial meshes in serial code, see the
|
||||
new classes MeshPartitioner and MeshPart. This capability is also exposed as a
|
||||
menu option in the mesh-explorer miniapp in miniapps/meshing.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Introduced support for higher order non conformal Nedelec elements on
|
||||
|
||||
+1
-1
@@ -388,7 +388,7 @@ GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LINK_LIB_DIR) -L$(GINKGO_LINK_LIB_DIR)\
|
||||
# AmgX library configuration
|
||||
AMGX_DIR = @MFEM_DIR@/../amgx
|
||||
AMGX_OPT = -I$(AMGX_DIR)/include
|
||||
AMGX_LIB = -lcusparse -lcusolver -lcublas -lnvToolsExt -L$(AMGX_DIR)/lib -lamgx
|
||||
AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
|
||||
@@ -92,4 +92,5 @@ vertices
|
||||
-0.70710678 -0.70710678
|
||||
0 -1
|
||||
0.70710678 -0.70710678
|
||||
|
||||
mfem_mesh_end
|
||||
|
||||
+27
-8
@@ -74,10 +74,14 @@ public:
|
||||
inline Array(int asize, MemoryType mt)
|
||||
: size(asize) { asize > 0 ? data.New(asize, mt) : data.Reset(mt); }
|
||||
|
||||
/** @brief Creates array using an externally allocated pointer @a data_ to
|
||||
@a asize elements. The data pointer will not be deleted by Array. */
|
||||
inline Array(T *data_, int asize)
|
||||
{ data.Wrap(data_, asize, false); size = asize; }
|
||||
/** @brief Creates array using an externally allocated host pointer @a data_
|
||||
to @a asize elements. If @a own_data is true, the array takes ownership
|
||||
of the pointer.
|
||||
|
||||
When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by MemoryManager::GetHostMemoryType(). */
|
||||
inline Array(T *data_, int asize, bool own_data = false)
|
||||
{ data.Wrap(data_, asize, own_data); size = asize; }
|
||||
|
||||
/// Copy constructor: deep copy from @a src
|
||||
/** This method supports source arrays using any MemoryType. */
|
||||
@@ -205,7 +209,14 @@ public:
|
||||
inline void Copy(Array ©) const;
|
||||
|
||||
/// Make this Array a reference to a pointer.
|
||||
inline void MakeRef(T *, int);
|
||||
/** When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by MemoryManager::GetHostMemoryType(). */
|
||||
inline void MakeRef(T *data_, int size_, bool own_data = false);
|
||||
|
||||
/// Make this Array a reference to a pointer.
|
||||
/** When @a own_data is true, the pointer @a data_ must be allocated with
|
||||
MemoryType given by @a mt. */
|
||||
inline void MakeRef(T *data_, int size, MemoryType mt, bool own_data);
|
||||
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
@@ -868,11 +879,19 @@ inline void Array<T>::Copy(Array ©) const
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(T *p, int s)
|
||||
inline void Array<T>::MakeRef(T *data_, int size_, bool own_data)
|
||||
{
|
||||
data.Delete();
|
||||
data.Wrap(p, s, false);
|
||||
size = s;
|
||||
data.Wrap(data_, size_, own_data);
|
||||
size = size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void Array<T>::MakeRef(T *data_, int size_, MemoryType mt, bool own_data)
|
||||
{
|
||||
data.Delete();
|
||||
data.Wrap(data_, size_, mt, own_data);
|
||||
size = size_;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
||||
@@ -275,7 +275,7 @@ void GroupTopology::Save(ostream &os) const
|
||||
os << "\ncommunication_groups\n";
|
||||
os << "number_of_groups " << NGroups() << "\n\n";
|
||||
|
||||
os << "# number of entities in each group, followed by group ids in group\n";
|
||||
os << "# number of entities in each group, followed by ranks in group\n";
|
||||
for (int group_id = 0; group_id < NGroups(); ++group_id)
|
||||
{
|
||||
int group_size = GetGroupSize(group_id);
|
||||
|
||||
+6
-1
@@ -207,7 +207,12 @@ template <> inline void Swap<Table>(Table &a, Table &b)
|
||||
void Transpose (const Table &A, Table &At, int ncols_A_ = -1);
|
||||
Table * Transpose (const Table &A);
|
||||
|
||||
/// Transpose an Array<int>
|
||||
/// @brief Transpose an Array<int>.
|
||||
///
|
||||
/// The array @a A represents a table where each row @a i has exactly one
|
||||
/// connection to the column (TYPE II) index specified by @a A[i].
|
||||
///
|
||||
/// @note The column (TYPE II) indices in each row of @a At will be sorted.
|
||||
void Transpose(const Array<int> &A, Table &At, int ncols_A_ = -1);
|
||||
|
||||
/// C = A * B (as boolean matrices)
|
||||
|
||||
+884
-3
@@ -20,9 +20,10 @@
|
||||
#include "../general/tic_toc.hpp"
|
||||
#include "../general/gecko.hpp"
|
||||
#include "../general/kdtree.hpp"
|
||||
#include "../general/sets.hpp"
|
||||
#include "../fem/quadinterpolator.hpp"
|
||||
|
||||
#include <iostream>
|
||||
// headers already included by mesh.hpp: <iostream>, <array>, <map>, <memory>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
@@ -1338,7 +1339,7 @@ Mesh::FaceInformation::operator Mesh::FaceInfo() const
|
||||
return res;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Mesh::FaceInformation& info)
|
||||
std::ostream &operator<<(std::ostream &os, const Mesh::FaceInformation& info)
|
||||
{
|
||||
os << "face topology=";
|
||||
switch (info.topology)
|
||||
@@ -6209,6 +6210,12 @@ const FiniteElementSpace *Mesh::GetNodalFESpace() const
|
||||
|
||||
void Mesh::SetCurvature(int order, bool discont, int space_dim, int ordering)
|
||||
{
|
||||
if (order <= 0)
|
||||
{
|
||||
delete Nodes;
|
||||
Nodes = nullptr;
|
||||
return;
|
||||
}
|
||||
space_dim = (space_dim == -1) ? spaceDim : space_dim;
|
||||
FiniteElementCollection* nfec;
|
||||
if (discont)
|
||||
@@ -11419,7 +11426,8 @@ void Mesh::Printer(std::ostream &os, std::string section_delimiter,
|
||||
|
||||
if (!section_delimiter.empty())
|
||||
{
|
||||
os << section_delimiter << endl; // only with formats v1.2 and above
|
||||
os << '\n'
|
||||
<< section_delimiter << endl; // only with formats v1.2 and above
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13299,6 +13307,879 @@ void Mesh::GetGeometricParametersFromJacobian(const DenseMatrix &J,
|
||||
}
|
||||
|
||||
|
||||
MeshPart::EntityHelper::EntityHelper(
|
||||
int dim_, const Array<int> (&entity_to_vertex_)[Geometry::NumGeom])
|
||||
: dim(dim_),
|
||||
entity_to_vertex(entity_to_vertex_)
|
||||
{
|
||||
int geom_offset = 0;
|
||||
for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
|
||||
{
|
||||
geom_offsets[g] = geom_offset;
|
||||
geom_offset += entity_to_vertex[g].Size()/Geometry::NumVerts[g];
|
||||
}
|
||||
geom_offsets[Geometry::DimStart[dim+1]] = geom_offset;
|
||||
num_entities = geom_offset;
|
||||
}
|
||||
|
||||
MeshPart::Entity MeshPart::EntityHelper::FindEntity(int bytype_entity_id)
|
||||
{
|
||||
// Find the 'geom' that corresponds to 'bytype_entity_id'
|
||||
int geom = Geometry::DimStart[dim];
|
||||
while (geom_offsets[geom+1] <= bytype_entity_id) { geom++; }
|
||||
MFEM_ASSERT(geom < Geometry::NumGeom, "internal error");
|
||||
MFEM_ASSERT(Geometry::Dimension[geom] == dim, "internal error");
|
||||
const int nv = Geometry::NumVerts[geom];
|
||||
const int geom_elem_id = bytype_entity_id - geom_offsets[geom];
|
||||
const int *v = &entity_to_vertex[geom][nv*geom_elem_id];
|
||||
return { geom, nv, v };
|
||||
}
|
||||
|
||||
void MeshPart::Print(std::ostream &os) const
|
||||
{
|
||||
os << "MFEM mesh v1.2\n";
|
||||
|
||||
// optional
|
||||
os <<
|
||||
"\n#\n# MFEM Geometry Types (see mesh/geom.hpp):\n#\n"
|
||||
"# POINT = 0\n"
|
||||
"# SEGMENT = 1\n"
|
||||
"# TRIANGLE = 2\n"
|
||||
"# SQUARE = 3\n"
|
||||
"# TETRAHEDRON = 4\n"
|
||||
"# CUBE = 5\n"
|
||||
"# PRISM = 6\n"
|
||||
"# PYRAMID = 7\n"
|
||||
"#\n";
|
||||
|
||||
const int dim = dimension;
|
||||
os << "\ndimension\n" << dim;
|
||||
|
||||
os << "\n\nelements\n" << num_elements << '\n';
|
||||
{
|
||||
const bool have_element_map = (element_map.Size() == num_elements);
|
||||
MFEM_ASSERT(have_element_map || element_map.Size() == 0,
|
||||
"invalid MeshPart state");
|
||||
EntityHelper elem_helper(dim, entity_to_vertex);
|
||||
MFEM_ASSERT(elem_helper.num_entities == num_elements,
|
||||
"invalid MeshPart state");
|
||||
for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
|
||||
{
|
||||
const int bytype_elem_id = have_element_map ?
|
||||
element_map[nat_elem_id] : nat_elem_id;
|
||||
const Entity ent = elem_helper.FindEntity(bytype_elem_id);
|
||||
// Print the element
|
||||
os << attributes[nat_elem_id] << ' ' << ent.geom;
|
||||
for (int i = 0; i < ent.num_verts; i++)
|
||||
{
|
||||
os << ' ' << ent.verts[i];
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
os << "\nboundary\n" << num_bdr_elements << '\n';
|
||||
{
|
||||
const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
|
||||
MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
|
||||
"invalid MeshPart state");
|
||||
EntityHelper bdr_helper(dim-1, entity_to_vertex);
|
||||
MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
|
||||
"invalid MeshPart state");
|
||||
for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
|
||||
{
|
||||
const int bytype_bdr_id = have_boundary_map ?
|
||||
boundary_map[nat_bdr_id] : nat_bdr_id;
|
||||
const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
|
||||
// Print the boundary element
|
||||
os << bdr_attributes[nat_bdr_id] << ' ' << ent.geom;
|
||||
for (int i = 0; i < ent.num_verts; i++)
|
||||
{
|
||||
os << ' ' << ent.verts[i];
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
os << "\nvertices\n" << num_vertices << '\n';
|
||||
if (!nodes)
|
||||
{
|
||||
const int sdim = space_dimension;
|
||||
os << sdim << '\n';
|
||||
for (int i = 0; i < num_vertices; i++)
|
||||
{
|
||||
os << vertex_coordinates[i*sdim];
|
||||
for (int d = 1; d < sdim; d++)
|
||||
{
|
||||
os << ' ' << vertex_coordinates[i*sdim+d];
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
os << "\nnodes\n";
|
||||
nodes->Save(os);
|
||||
}
|
||||
|
||||
os << "\nmfem_serial_mesh_end\n";
|
||||
|
||||
// Start: GroupTopology::Save
|
||||
const int num_groups = my_groups.Size();
|
||||
os << "\ncommunication_groups\n";
|
||||
os << "number_of_groups " << num_groups << "\n\n";
|
||||
|
||||
os << "# number of entities in each group, followed by ranks in group\n";
|
||||
for (int group_id = 0; group_id < num_groups; ++group_id)
|
||||
{
|
||||
const int group_size = my_groups.RowSize(group_id);
|
||||
const int *group_ptr = my_groups.GetRow(group_id);
|
||||
os << group_size;
|
||||
for (int group_member_index = 0; group_member_index < group_size;
|
||||
++group_member_index)
|
||||
{
|
||||
os << ' ' << group_ptr[group_member_index];
|
||||
}
|
||||
os << '\n';
|
||||
}
|
||||
// End: GroupTopology::Save
|
||||
|
||||
const Table &g2v = group_shared_entity_to_vertex[Geometry::POINT];
|
||||
const Table &g2ev = group_shared_entity_to_vertex[Geometry::SEGMENT];
|
||||
const Table &g2tv = group_shared_entity_to_vertex[Geometry::TRIANGLE];
|
||||
const Table &g2qv = group_shared_entity_to_vertex[Geometry::SQUARE];
|
||||
|
||||
MFEM_VERIFY(g2v.RowSize(0) == 0, "internal erroor");
|
||||
os << "\ntotal_shared_vertices " << g2v.Size_of_connections() << '\n';
|
||||
if (dimension >= 2)
|
||||
{
|
||||
MFEM_VERIFY(g2ev.RowSize(0) == 0, "internal erroor");
|
||||
os << "total_shared_edges " << g2ev.Size_of_connections()/2 << '\n';
|
||||
}
|
||||
if (dimension >= 3)
|
||||
{
|
||||
MFEM_VERIFY(g2tv.RowSize(0) == 0, "internal erroor");
|
||||
MFEM_VERIFY(g2qv.RowSize(0) == 0, "internal erroor");
|
||||
const int total_shared_faces =
|
||||
g2tv.Size_of_connections()/3 + g2qv.Size_of_connections()/4;
|
||||
os << "total_shared_faces " << total_shared_faces << '\n';
|
||||
}
|
||||
os << "\n# group 0 has no shared entities\n";
|
||||
for (int gr = 1; gr < num_groups; gr++)
|
||||
{
|
||||
{
|
||||
const int nv = g2v.RowSize(gr);
|
||||
const int *sv = g2v.GetRow(gr);
|
||||
os << "\n# group " << gr << "\nshared_vertices " << nv << '\n';
|
||||
for (int i = 0; i < nv; i++)
|
||||
{
|
||||
os << sv[i] << '\n';
|
||||
}
|
||||
}
|
||||
if (dimension >= 2)
|
||||
{
|
||||
const int ne = g2ev.RowSize(gr)/2;
|
||||
const int *se = g2ev.GetRow(gr);
|
||||
os << "\nshared_edges " << ne << '\n';
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
const int *v = se + 2*i;
|
||||
os << v[0] << ' ' << v[1] << '\n';
|
||||
}
|
||||
}
|
||||
if (dimension >= 3)
|
||||
{
|
||||
const int nt = g2tv.RowSize(gr)/3;
|
||||
const int *st = g2tv.GetRow(gr);
|
||||
const int nq = g2qv.RowSize(gr)/4;
|
||||
const int *sq = g2qv.GetRow(gr);
|
||||
os << "\nshared_faces " << nt+nq << '\n';
|
||||
for (int i = 0; i < nt; i++)
|
||||
{
|
||||
os << Geometry::TRIANGLE;
|
||||
const int *v = st + 3*i;
|
||||
for (int j = 0; j < 3; j++) { os << ' ' << v[j]; }
|
||||
os << '\n';
|
||||
}
|
||||
for (int i = 0; i < nq; i++)
|
||||
{
|
||||
os << Geometry::SQUARE;
|
||||
const int *v = sq + 4*i;
|
||||
for (int j = 0; j < 4; j++) { os << ' ' << v[j]; }
|
||||
os << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write out section end tag for mesh.
|
||||
os << "\nmfem_mesh_end" << endl;
|
||||
}
|
||||
|
||||
Mesh &MeshPart::GetMesh()
|
||||
{
|
||||
if (mesh) { return *mesh; }
|
||||
|
||||
mesh.reset(new Mesh(dimension,
|
||||
num_vertices,
|
||||
num_elements,
|
||||
num_bdr_elements,
|
||||
space_dimension));
|
||||
|
||||
// Add elements
|
||||
{
|
||||
const bool have_element_map = (element_map.Size() == num_elements);
|
||||
MFEM_ASSERT(have_element_map || element_map.Size() == 0,
|
||||
"invalid MeshPart state");
|
||||
EntityHelper elem_helper(dimension, entity_to_vertex);
|
||||
MFEM_ASSERT(elem_helper.num_entities == num_elements,
|
||||
"invalid MeshPart state");
|
||||
const bool have_tet_refine_flags = (tet_refine_flags.Size() > 0);
|
||||
for (int nat_elem_id = 0; nat_elem_id < num_elements; nat_elem_id++)
|
||||
{
|
||||
const int bytype_elem_id = have_element_map ?
|
||||
element_map[nat_elem_id] : nat_elem_id;
|
||||
const Entity ent = elem_helper.FindEntity(bytype_elem_id);
|
||||
Element *el = mesh->NewElement(ent.geom);
|
||||
el->SetVertices(ent.verts);
|
||||
el->SetAttribute(attributes[nat_elem_id]);
|
||||
if (ent.geom == Geometry::TETRAHEDRON && have_tet_refine_flags)
|
||||
{
|
||||
constexpr int geom_tet = Geometry::TETRAHEDRON;
|
||||
const int tet_id = (ent.verts - entity_to_vertex[geom_tet])/4;
|
||||
const int ref_flag = tet_refine_flags[tet_id];
|
||||
static_cast<Tetrahedron*>(el)->SetRefinementFlag(ref_flag);
|
||||
}
|
||||
mesh->AddElement(el);
|
||||
}
|
||||
}
|
||||
|
||||
// Add boundary elements
|
||||
{
|
||||
const bool have_boundary_map = (boundary_map.Size() == num_bdr_elements);
|
||||
MFEM_ASSERT(have_boundary_map || boundary_map.Size() == 0,
|
||||
"invalid MeshPart state");
|
||||
EntityHelper bdr_helper(dimension-1, entity_to_vertex);
|
||||
MFEM_ASSERT(bdr_helper.num_entities == num_bdr_elements,
|
||||
"invalid MeshPart state");
|
||||
for (int nat_bdr_id = 0; nat_bdr_id < num_bdr_elements; nat_bdr_id++)
|
||||
{
|
||||
const int bytype_bdr_id = have_boundary_map ?
|
||||
boundary_map[nat_bdr_id] : nat_bdr_id;
|
||||
const Entity ent = bdr_helper.FindEntity(bytype_bdr_id);
|
||||
Element *bdr = mesh->NewElement(ent.geom);
|
||||
bdr->SetVertices(ent.verts);
|
||||
bdr->SetAttribute(bdr_attributes[nat_bdr_id]);
|
||||
mesh->AddBdrElement(bdr);
|
||||
}
|
||||
}
|
||||
|
||||
// Add vertices
|
||||
if (vertex_coordinates.Size() == space_dimension*num_vertices)
|
||||
{
|
||||
MFEM_ASSERT(!nodes, "invalid MeshPart state");
|
||||
for (int vert_id = 0; vert_id < num_vertices; vert_id++)
|
||||
{
|
||||
mesh->AddVertex(vertex_coordinates + space_dimension*vert_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(vertex_coordinates.Size() == 0, "invalid MeshPart state");
|
||||
for (int vert_id = 0; vert_id < num_vertices; vert_id++)
|
||||
{
|
||||
mesh->AddVertex(0., 0., 0.);
|
||||
}
|
||||
// 'mesh.Nodes' cannot be set here -- they can be set later, if needed
|
||||
}
|
||||
|
||||
mesh->FinalizeTopology(/* generate_bdr: */ false);
|
||||
|
||||
return *mesh;
|
||||
}
|
||||
|
||||
|
||||
MeshPartitioner::MeshPartitioner(Mesh &mesh_,
|
||||
int num_parts_,
|
||||
int *partitioning_,
|
||||
int part_method)
|
||||
: mesh(mesh_)
|
||||
{
|
||||
if (partitioning_)
|
||||
{
|
||||
partitioning.MakeRef(partitioning_, mesh.GetNE(), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
partitioning_ = mesh.GeneratePartitioning(num_parts_, part_method);
|
||||
// Mesh::GeneratePartitioning always uses new[] to allocate the,
|
||||
// partitioning, so we need to tell the memory manager to free it with
|
||||
// delete[] (even if a different host memory type has been selected).
|
||||
const MemoryType mt = MemoryType::HOST;
|
||||
partitioning.MakeRef(partitioning_, mesh.GetNE(), mt, true);
|
||||
}
|
||||
|
||||
Transpose(partitioning, part_to_element, num_parts_);
|
||||
// Note: the element ids in each row of 'part_to_element' are sorted.
|
||||
|
||||
const int dim = mesh.Dimension();
|
||||
if (dim >= 2)
|
||||
{
|
||||
Transpose(mesh.ElementToEdgeTable(), edge_to_element, mesh.GetNEdges());
|
||||
}
|
||||
|
||||
Array<int> boundary_to_part(mesh.GetNBE());
|
||||
// Same logic as in ParMesh::BuildLocalBoundary
|
||||
if (dim >= 3)
|
||||
{
|
||||
for (int i = 0; i < boundary_to_part.Size(); i++)
|
||||
{
|
||||
int face, o, el1, el2;
|
||||
mesh.GetBdrElementFace(i, &face, &o);
|
||||
mesh.GetFaceElements(face, &el1, &el2);
|
||||
boundary_to_part[i] =
|
||||
partitioning[(o % 2 == 0 || el2 < 0) ? el1 : el2];
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
for (int i = 0; i < boundary_to_part.Size(); i++)
|
||||
{
|
||||
int edge = mesh.GetBdrElementFaceIndex(i);
|
||||
int el1 = edge_to_element.GetRow(edge)[0];
|
||||
boundary_to_part[i] = partitioning[el1];
|
||||
}
|
||||
}
|
||||
else if (dim == 1)
|
||||
{
|
||||
for (int i = 0; i < boundary_to_part.Size(); i++)
|
||||
{
|
||||
int vert = mesh.GetBdrElementFaceIndex(i);
|
||||
int el1, el2;
|
||||
mesh.GetFaceElements(vert, &el1, &el2);
|
||||
boundary_to_part[i] = partitioning[el1];
|
||||
}
|
||||
}
|
||||
Transpose(boundary_to_part, part_to_boundary, num_parts_);
|
||||
// Note: the boundary element ids in each row of 'part_to_boundary' are
|
||||
// sorted.
|
||||
boundary_to_part.DeleteAll();
|
||||
|
||||
Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
|
||||
vertex_to_element.Swap(*vert_element);
|
||||
delete vert_element;
|
||||
}
|
||||
|
||||
void MeshPartitioner::ExtractPart(int part_id, MeshPart &mesh_part) const
|
||||
{
|
||||
const int num_parts = part_to_element.Size();
|
||||
|
||||
MFEM_VERIFY(0 <= part_id && part_id < num_parts,
|
||||
"invalid part_id = " << part_id
|
||||
<< ", num_parts = " << num_parts);
|
||||
|
||||
const int dim = mesh.Dimension();
|
||||
const int sdim = mesh.SpaceDimension();
|
||||
const int num_elems = part_to_element.RowSize(part_id);
|
||||
const int *elem_list = part_to_element.GetRow(part_id); // sorted
|
||||
const int num_bdr_elems = part_to_boundary.RowSize(part_id);
|
||||
const int *bdr_elem_list = part_to_boundary.GetRow(part_id); // sorted
|
||||
|
||||
// Initialize 'mesh_part'
|
||||
mesh_part.dimension = dim;
|
||||
mesh_part.space_dimension = sdim;
|
||||
mesh_part.num_vertices = 0;
|
||||
mesh_part.num_elements = num_elems;
|
||||
mesh_part.num_bdr_elements = num_bdr_elems;
|
||||
for (int g = 0; g < Geometry::NumGeom; g++)
|
||||
{
|
||||
mesh_part.entity_to_vertex[g].SetSize(0); // can reuse Array allocation
|
||||
}
|
||||
mesh_part.tet_refine_flags.SetSize(0);
|
||||
mesh_part.element_map.SetSize(0); // 0 or 'num_elements', if needed
|
||||
mesh_part.boundary_map.SetSize(0); // 0 or 'num_bdr_elements', if needed
|
||||
mesh_part.attributes.SetSize(num_elems);
|
||||
mesh_part.bdr_attributes.SetSize(num_bdr_elems);
|
||||
mesh_part.vertex_coordinates.SetSize(0);
|
||||
|
||||
mesh_part.num_parts = num_parts;
|
||||
mesh_part.my_part_id = part_id;
|
||||
mesh_part.my_groups.Clear();
|
||||
for (int g = 0; g < Geometry::NumGeom; g++)
|
||||
{
|
||||
mesh_part.group_shared_entity_to_vertex[g].Clear();
|
||||
}
|
||||
mesh_part.nodes.reset(nullptr);
|
||||
mesh_part.nodal_fes.reset(nullptr);
|
||||
mesh_part.mesh.reset(nullptr);
|
||||
|
||||
// Initialize:
|
||||
// - 'mesh_part.entity_to_vertex' for the elements (boundary elements are
|
||||
// set later); vertex ids are global at this point - they will be mapped to
|
||||
// local ids later
|
||||
// - 'mesh_part.attributes'
|
||||
// - 'mesh_part.tet_refine_flags' if needed
|
||||
int geom_marker = 0, num_geom = 0;
|
||||
for (int i = 0; i < num_elems; i++)
|
||||
{
|
||||
const Element *elem = mesh.GetElement(elem_list[i]);
|
||||
const int geom = elem->GetGeometryType();
|
||||
const int nv = Geometry::NumVerts[geom];
|
||||
const int *v = elem->GetVertices();
|
||||
MFEM_VERIFY(numeric_limits<int>::max() - nv >=
|
||||
mesh_part.entity_to_vertex[geom].Size(),
|
||||
"overflow in 'entity_to_vertex[geom]', geom: "
|
||||
<< Geometry::Name[geom]);
|
||||
mesh_part.entity_to_vertex[geom].Append(v, nv);
|
||||
mesh_part.attributes[i] = elem->GetAttribute();
|
||||
if (geom == Geometry::TETRAHEDRON)
|
||||
{
|
||||
// Create 'mesh_part.tet_refine_flags' but only if we find at least one
|
||||
// non-zero flag in a tetrahedron.
|
||||
const Tetrahedron *tet = static_cast<const Tetrahedron*>(elem);
|
||||
const int ref_flag = tet->GetRefinementFlag();
|
||||
if (mesh_part.tet_refine_flags.Size() == 0)
|
||||
{
|
||||
if (ref_flag)
|
||||
{
|
||||
// This is the first time we encounter non-zero 'ref_flag'
|
||||
const int num_tets = mesh_part.entity_to_vertex[geom].Size()/nv;
|
||||
mesh_part.tet_refine_flags.SetSize(num_tets, 0);
|
||||
mesh_part.tet_refine_flags.Last() = ref_flag;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh_part.tet_refine_flags.Append(ref_flag);
|
||||
}
|
||||
}
|
||||
if ((geom_marker & (1 << geom)) == 0)
|
||||
{
|
||||
geom_marker |= (1 << geom);
|
||||
num_geom++;
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(mesh_part.tet_refine_flags.Size() == 0 ||
|
||||
mesh_part.tet_refine_flags.Size() ==
|
||||
mesh_part.entity_to_vertex[Geometry::TETRAHEDRON].Size()/4,
|
||||
"internal error");
|
||||
// Initialize 'mesh_part.element_map' if needed
|
||||
if (num_geom > 1)
|
||||
{
|
||||
int offsets[Geometry::NumGeom];
|
||||
int offset = 0;
|
||||
for (int g = Geometry::DimStart[dim]; g < Geometry::DimStart[dim+1]; g++)
|
||||
{
|
||||
offsets[g] = offset;
|
||||
offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
|
||||
}
|
||||
mesh_part.element_map.SetSize(num_elems);
|
||||
for (int i = 0; i < num_elems; i++)
|
||||
{
|
||||
const int geom = mesh.GetElementGeometry(elem_list[i]);
|
||||
mesh_part.element_map[i] = offsets[geom]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize:
|
||||
// - 'mesh_part.entity_to_vertex' for the boundary elements; vertex ids are
|
||||
// global at this point - they will be mapped to local ids later
|
||||
// - 'mesh_part.bdr_attributes'
|
||||
geom_marker = 0; num_geom = 0;
|
||||
for (int i = 0; i < num_bdr_elems; i++)
|
||||
{
|
||||
const Element *bdr_elem = mesh.GetBdrElement(bdr_elem_list[i]);
|
||||
const int geom = bdr_elem->GetGeometryType();
|
||||
const int nv = Geometry::NumVerts[geom];
|
||||
const int *v = bdr_elem->GetVertices();
|
||||
MFEM_VERIFY(numeric_limits<int>::max() - nv >=
|
||||
mesh_part.entity_to_vertex[geom].Size(),
|
||||
"overflow in 'entity_to_vertex[geom]', geom: "
|
||||
<< Geometry::Name[geom]);
|
||||
mesh_part.entity_to_vertex[geom].Append(v, nv);
|
||||
mesh_part.bdr_attributes[i] = bdr_elem->GetAttribute();
|
||||
if ((geom_marker & (1 << geom)) == 0)
|
||||
{
|
||||
geom_marker |= (1 << geom);
|
||||
num_geom++;
|
||||
}
|
||||
}
|
||||
// Initialize 'mesh_part.boundary_map' if needed
|
||||
if (num_geom > 1)
|
||||
{
|
||||
int offsets[Geometry::NumGeom];
|
||||
int offset = 0;
|
||||
for (int g = Geometry::DimStart[dim-1]; g < Geometry::DimStart[dim]; g++)
|
||||
{
|
||||
offsets[g] = offset;
|
||||
offset += mesh_part.entity_to_vertex[g].Size()/Geometry::NumVerts[g];
|
||||
}
|
||||
mesh_part.boundary_map.SetSize(num_bdr_elems);
|
||||
for (int i = 0; i < num_bdr_elems; i++)
|
||||
{
|
||||
const int geom = mesh.GetBdrElementGeometry(bdr_elem_list[i]);
|
||||
mesh_part.boundary_map[i] = offsets[geom]++;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the vertex id map, 'vertex_loc_to_glob', which maps local ids to
|
||||
// global ones; the map is sorted, preserving the global ordering.
|
||||
Array<int> vertex_loc_to_glob;
|
||||
{
|
||||
std::unordered_set<int> vertex_set;
|
||||
for (int i = 0; i < num_elems; i++)
|
||||
{
|
||||
const Element *elem = mesh.GetElement(elem_list[i]);
|
||||
const int geom = elem->GetGeometryType();
|
||||
const int nv = Geometry::NumVerts[geom];
|
||||
const int *v = elem->GetVertices();
|
||||
vertex_set.insert(v, v + nv);
|
||||
}
|
||||
vertex_loc_to_glob.SetSize(vertex_set.size());
|
||||
std::copy(vertex_set.begin(), vertex_set.end(), // src
|
||||
vertex_loc_to_glob.begin()); // dest
|
||||
}
|
||||
vertex_loc_to_glob.Sort();
|
||||
|
||||
// Initialize 'mesh_part.num_vertices'
|
||||
mesh_part.num_vertices = vertex_loc_to_glob.Size();
|
||||
|
||||
// Update the vertex ids in the arrays 'mesh_part.entity_to_vertex' from
|
||||
// global to local.
|
||||
for (int g = 0; g < Geometry::NumGeom; g++)
|
||||
{
|
||||
Array<int> &vert_array = mesh_part.entity_to_vertex[g];
|
||||
for (int i = 0; i < vert_array.Size(); i++)
|
||||
{
|
||||
const int glob_id = vert_array[i];
|
||||
const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
|
||||
MFEM_ASSERT(loc_id >= 0, "internal error: global vertex id not found");
|
||||
vert_array[i] = loc_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize one of 'mesh_part.vertex_coordinates' or 'mesh_part.nodes'
|
||||
if (!mesh.GetNodes())
|
||||
{
|
||||
MFEM_VERIFY(numeric_limits<int>::max()/sdim >= vertex_loc_to_glob.Size(),
|
||||
"overflow in 'vertex_coordinates', num_vertices = "
|
||||
<< vertex_loc_to_glob.Size() << ", sdim = " << sdim);
|
||||
mesh_part.vertex_coordinates.SetSize(sdim*vertex_loc_to_glob.Size());
|
||||
for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
|
||||
{
|
||||
const real_t *coord = mesh.GetVertex(vertex_loc_to_glob[i]);
|
||||
for (int d = 0; d < sdim; d++)
|
||||
{
|
||||
mesh_part.vertex_coordinates[i*sdim+d] = coord[d];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const GridFunction &glob_nodes = *mesh.GetNodes();
|
||||
mesh_part.nodal_fes = ExtractFESpace(mesh_part, *glob_nodes.FESpace());
|
||||
// Initialized 'mesh_part.mesh'.
|
||||
// Note: the nodes of 'mesh_part.mesh' are not set.
|
||||
|
||||
mesh_part.nodes = ExtractGridFunction(mesh_part, glob_nodes,
|
||||
*mesh_part.nodal_fes);
|
||||
|
||||
// Attach the 'mesh_part.nodes' to the 'mesh_part.mesh'.
|
||||
mesh_part.mesh->NewNodes(*mesh_part.nodes, /* make_owner: */ false);
|
||||
// Note: the vertices of 'mesh_part.mesh' are not set.
|
||||
}
|
||||
|
||||
// Begin constructing the "neighbor" groups, i.e. the groups that contain
|
||||
// 'part_id'.
|
||||
ListOfIntegerSets groups;
|
||||
{
|
||||
// the first group is the local one
|
||||
IntegerSet group;
|
||||
group.Recreate(1, &part_id);
|
||||
groups.Insert(group);
|
||||
}
|
||||
|
||||
// 'shared_faces' : shared face id -> (global_face_id, group_id)
|
||||
// Note: 'shared_faces' will be sorted by 'global_face_id'.
|
||||
Array<Pair<int,int>> shared_faces;
|
||||
|
||||
// Add "neighbor" groups defined by faces
|
||||
// Construct 'shared_faces'.
|
||||
if (dim >= 3)
|
||||
{
|
||||
std::unordered_set<int> face_set;
|
||||
// Construct 'face_set'
|
||||
const Table &elem_to_face = mesh.ElementToFaceTable();
|
||||
for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
|
||||
{
|
||||
const int glob_elem_id = elem_list[loc_elem_id];
|
||||
const int nfaces = elem_to_face.RowSize(glob_elem_id);
|
||||
const int *faces = elem_to_face.GetRow(glob_elem_id);
|
||||
face_set.insert(faces, faces + nfaces);
|
||||
}
|
||||
// Construct 'shared_faces'; add "neighbor" groups defined by faces.
|
||||
IntegerSet group;
|
||||
for (int glob_face_id : face_set)
|
||||
{
|
||||
int el[2];
|
||||
mesh.GetFaceElements(glob_face_id, &el[0], &el[1]);
|
||||
if (el[1] < 0) { continue; }
|
||||
el[0] = partitioning[el[0]];
|
||||
el[1] = partitioning[el[1]];
|
||||
MFEM_ASSERT(el[0] == part_id || el[1] == part_id, "internal error");
|
||||
if (el[0] != part_id || el[1] != part_id)
|
||||
{
|
||||
group.Recreate(2, el);
|
||||
const int group_id = groups.Insert(group);
|
||||
shared_faces.Append(Pair<int,int>(glob_face_id, group_id));
|
||||
}
|
||||
}
|
||||
shared_faces.Sort(); // sort the shared faces by 'glob_face_id'
|
||||
}
|
||||
|
||||
// 'shared_edges' : shared edge id -> (global_edge_id, group_id)
|
||||
// Note: 'shared_edges' will be sorted by 'global_edge_id'.
|
||||
Array<Pair<int,int>> shared_edges;
|
||||
|
||||
// Add "neighbor" groups defined by edges.
|
||||
// Construct 'shared_edges'.
|
||||
if (dim >= 2)
|
||||
{
|
||||
std::unordered_set<int> edge_set;
|
||||
// Construct 'edge_set'
|
||||
const Table &elem_to_edge = mesh.ElementToEdgeTable();
|
||||
for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
|
||||
{
|
||||
const int glob_elem_id = elem_list[loc_elem_id];
|
||||
const int nedges = elem_to_edge.RowSize(glob_elem_id);
|
||||
const int *edges = elem_to_edge.GetRow(glob_elem_id);
|
||||
edge_set.insert(edges, edges + nedges);
|
||||
}
|
||||
// Construct 'shared_edges'; add "neighbor" groups defined by edges.
|
||||
IntegerSet group;
|
||||
for (int glob_edge_id : edge_set)
|
||||
{
|
||||
const int nelem = edge_to_element.RowSize(glob_edge_id);
|
||||
const int *elem = edge_to_element.GetRow(glob_edge_id);
|
||||
Array<int> &gr = group; // reference to the 'group' internal Array
|
||||
gr.SetSize(nelem);
|
||||
for (int j = 0; j < nelem; j++)
|
||||
{
|
||||
gr[j] = partitioning[elem[j]];
|
||||
}
|
||||
gr.Sort();
|
||||
gr.Unique();
|
||||
MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
|
||||
if (group.Size() > 1)
|
||||
{
|
||||
const int group_id = groups.Insert(group);
|
||||
shared_edges.Append(Pair<int,int>(glob_edge_id, group_id));
|
||||
}
|
||||
}
|
||||
shared_edges.Sort(); // sort the shared edges by 'glob_edge_id'
|
||||
}
|
||||
|
||||
// 'shared_verts' : shared vertex id -> (global_vertex_id, group_id)
|
||||
// Note: 'shared_verts' will be sorted by 'global_vertex_id'.
|
||||
Array<Pair<int,int>> shared_verts;
|
||||
|
||||
// Add "neighbor" groups defined by vertices.
|
||||
// Construct 'shared_verts'.
|
||||
{
|
||||
IntegerSet group;
|
||||
for (int i = 0; i < vertex_loc_to_glob.Size(); i++)
|
||||
{
|
||||
// 'vertex_to_element' maps global vertex ids to global element ids
|
||||
const int glob_vertex_id = vertex_loc_to_glob[i];
|
||||
const int nelem = vertex_to_element.RowSize(glob_vertex_id);
|
||||
const int *elem = vertex_to_element.GetRow(glob_vertex_id);
|
||||
Array<int> &gr = group; // reference to the 'group' internal Array
|
||||
gr.SetSize(nelem);
|
||||
for (int j = 0; j < nelem; j++)
|
||||
{
|
||||
gr[j] = partitioning[elem[j]];
|
||||
}
|
||||
gr.Sort();
|
||||
gr.Unique();
|
||||
MFEM_ASSERT(gr.FindSorted(part_id) >= 0, "internal error");
|
||||
if (group.Size() > 1)
|
||||
{
|
||||
const int group_id = groups.Insert(group);
|
||||
shared_verts.Append(Pair<int,int>(glob_vertex_id, group_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Done constructing the "neighbor" groups in 'groups'.
|
||||
const int num_groups = groups.Size();
|
||||
|
||||
// Define 'mesh_part.my_groups'
|
||||
groups.AsTable(mesh_part.my_groups);
|
||||
|
||||
// Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::POINT]'
|
||||
Table &group__shared_vertex_to_vertex =
|
||||
mesh_part.group_shared_entity_to_vertex[Geometry::POINT];
|
||||
group__shared_vertex_to_vertex.MakeI(num_groups);
|
||||
for (int sv = 0; sv < shared_verts.Size(); sv++)
|
||||
{
|
||||
const int group_id = shared_verts[sv].two;
|
||||
group__shared_vertex_to_vertex.AddAColumnInRow(group_id);
|
||||
}
|
||||
group__shared_vertex_to_vertex.MakeJ();
|
||||
for (int sv = 0; sv < shared_verts.Size(); sv++)
|
||||
{
|
||||
const int glob_vertex_id = shared_verts[sv].one;
|
||||
const int group_id = shared_verts[sv].two;
|
||||
const int loc_vertex_id = vertex_loc_to_glob.FindSorted(glob_vertex_id);
|
||||
MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
|
||||
group__shared_vertex_to_vertex.AddConnection(group_id, loc_vertex_id);
|
||||
}
|
||||
group__shared_vertex_to_vertex.ShiftUpI();
|
||||
|
||||
// Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::SEGMENT]'
|
||||
if (dim >= 2)
|
||||
{
|
||||
Table &group__shared_edge_to_vertex =
|
||||
mesh_part.group_shared_entity_to_vertex[Geometry::SEGMENT];
|
||||
group__shared_edge_to_vertex.MakeI(num_groups);
|
||||
for (int se = 0; se < shared_edges.Size(); se++)
|
||||
{
|
||||
const int group_id = shared_edges[se].two;
|
||||
group__shared_edge_to_vertex.AddColumnsInRow(group_id, 2);
|
||||
}
|
||||
group__shared_edge_to_vertex.MakeJ();
|
||||
const Table &edge_to_vertex = *mesh.GetEdgeVertexTable();
|
||||
for (int se = 0; se < shared_edges.Size(); se++)
|
||||
{
|
||||
const int glob_edge_id = shared_edges[se].one;
|
||||
const int group_id = shared_edges[se].two;
|
||||
const int *v = edge_to_vertex.GetRow(glob_edge_id);
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
const int loc_vertex_id = vertex_loc_to_glob.FindSorted(v[i]);
|
||||
MFEM_ASSERT(loc_vertex_id >= 0, "internal error");
|
||||
group__shared_edge_to_vertex.AddConnection(group_id, loc_vertex_id);
|
||||
}
|
||||
}
|
||||
group__shared_edge_to_vertex.ShiftUpI();
|
||||
}
|
||||
|
||||
// Construct 'mesh_part.group_shared_entity_to_vertex[Geometry::TRIANGLE]'
|
||||
// and 'mesh_part.group_shared_entity_to_vertex[Geometry::SQUARE]'.
|
||||
if (dim >= 3)
|
||||
{
|
||||
Table &group__shared_tria_to_vertex =
|
||||
mesh_part.group_shared_entity_to_vertex[Geometry::TRIANGLE];
|
||||
Table &group__shared_quad_to_vertex =
|
||||
mesh_part.group_shared_entity_to_vertex[Geometry::SQUARE];
|
||||
Array<int> vertex_ids;
|
||||
group__shared_tria_to_vertex.MakeI(num_groups);
|
||||
group__shared_quad_to_vertex.MakeI(num_groups);
|
||||
for (int sf = 0; sf < shared_faces.Size(); sf++)
|
||||
{
|
||||
const int glob_face_id = shared_faces[sf].one;
|
||||
const int group_id = shared_faces[sf].two;
|
||||
const int geom = mesh.GetFaceGeometry(glob_face_id);
|
||||
mesh_part.group_shared_entity_to_vertex[geom].
|
||||
AddColumnsInRow(group_id, Geometry::NumVerts[geom]);
|
||||
}
|
||||
group__shared_tria_to_vertex.MakeJ();
|
||||
group__shared_quad_to_vertex.MakeJ();
|
||||
for (int sf = 0; sf < shared_faces.Size(); sf++)
|
||||
{
|
||||
const int glob_face_id = shared_faces[sf].one;
|
||||
const int group_id = shared_faces[sf].two;
|
||||
const int geom = mesh.GetFaceGeometry(glob_face_id);
|
||||
mesh.GetFaceVertices(glob_face_id, vertex_ids);
|
||||
// Rotate shared triangles that have an adjacent tetrahedron with a
|
||||
// nonzero refinement flag.
|
||||
// See also ParMesh::BuildSharedFaceElems.
|
||||
if (geom == Geometry::TRIANGLE)
|
||||
{
|
||||
int glob_el_id[2];
|
||||
mesh.GetFaceElements(glob_face_id, &glob_el_id[0], &glob_el_id[1]);
|
||||
int side = 0;
|
||||
const Element *el = mesh.GetElement(glob_el_id[0]);
|
||||
const Tetrahedron *tet = nullptr;
|
||||
if (el->GetGeometryType() == Geometry::TETRAHEDRON)
|
||||
{
|
||||
tet = static_cast<const Tetrahedron*>(el);
|
||||
}
|
||||
else
|
||||
{
|
||||
side = 1;
|
||||
el = mesh.GetElement(glob_el_id[1]);
|
||||
if (el->GetGeometryType() == Geometry::TETRAHEDRON)
|
||||
{
|
||||
tet = static_cast<const Tetrahedron*>(el);
|
||||
}
|
||||
}
|
||||
if (tet && tet->GetRefinementFlag())
|
||||
{
|
||||
// mark the shared face for refinement by reorienting
|
||||
// it according to the refinement flag in the tetrahedron
|
||||
// to which this shared face belongs to.
|
||||
int info[2];
|
||||
mesh.GetFaceInfos(glob_face_id, &info[0], &info[1]);
|
||||
tet->GetMarkedFace(info[side]/64, &vertex_ids[0]);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < vertex_ids.Size(); i++)
|
||||
{
|
||||
const int glob_id = vertex_ids[i];
|
||||
const int loc_id = vertex_loc_to_glob.FindSorted(glob_id);
|
||||
MFEM_ASSERT(loc_id >= 0, "internal error");
|
||||
vertex_ids[i] = loc_id;
|
||||
}
|
||||
mesh_part.group_shared_entity_to_vertex[geom].
|
||||
AddConnections(group_id, vertex_ids, vertex_ids.Size());
|
||||
}
|
||||
group__shared_tria_to_vertex.ShiftUpI();
|
||||
group__shared_quad_to_vertex.ShiftUpI();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<FiniteElementSpace>
|
||||
MeshPartitioner::ExtractFESpace(MeshPart &mesh_part,
|
||||
const FiniteElementSpace &global_fespace) const
|
||||
{
|
||||
mesh_part.GetMesh(); // initialize 'mesh_part.mesh'
|
||||
// Note: the nodes of 'mesh_part.mesh' are not set by GetMesh() unless they
|
||||
// were already constructed, e.g. by ExtractPart().
|
||||
|
||||
return std::unique_ptr<FiniteElementSpace>(
|
||||
new FiniteElementSpace(mesh_part.mesh.get(),
|
||||
global_fespace.FEColl(),
|
||||
global_fespace.GetVDim(),
|
||||
global_fespace.GetOrdering()));
|
||||
}
|
||||
|
||||
std::unique_ptr<GridFunction>
|
||||
MeshPartitioner::ExtractGridFunction(const MeshPart &mesh_part,
|
||||
const GridFunction &global_gf,
|
||||
FiniteElementSpace &local_fespace) const
|
||||
{
|
||||
std::unique_ptr<GridFunction> local_gf(new GridFunction(&local_fespace));
|
||||
|
||||
// Transfer data from 'global_gf' to 'local_gf'.
|
||||
Array<int> gvdofs, lvdofs;
|
||||
Vector loc_vals;
|
||||
const int part_id = mesh_part.my_part_id;
|
||||
const int num_elems = part_to_element.RowSize(part_id);
|
||||
const int *elem_list = part_to_element.GetRow(part_id); // sorted
|
||||
for (int loc_elem_id = 0; loc_elem_id < num_elems; loc_elem_id++)
|
||||
{
|
||||
const int glob_elem_id = elem_list[loc_elem_id];
|
||||
auto glob_dt = global_gf.FESpace()->GetElementVDofs(glob_elem_id, gvdofs);
|
||||
global_gf.GetSubVector(gvdofs, loc_vals);
|
||||
if (glob_dt) { glob_dt->InvTransformPrimal(loc_vals); }
|
||||
auto local_dt = local_fespace.GetElementVDofs(loc_elem_id, lvdofs);
|
||||
if (local_dt) { local_dt->TransformPrimal(loc_vals); }
|
||||
local_gf->SetSubVector(lvdofs, loc_vals);
|
||||
}
|
||||
return local_gf;
|
||||
}
|
||||
|
||||
|
||||
GeometricFactors::GeometricFactors(const Mesh *mesh, const IntegrationRule &ir,
|
||||
int flags, MemoryType d_mt)
|
||||
{
|
||||
|
||||
+349
-17
@@ -30,6 +30,7 @@
|
||||
#include <iostream>
|
||||
#include <array>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -75,8 +76,10 @@ protected:
|
||||
visualization purpose in GLVis. */
|
||||
mutable int nbInteriorFaces, nbBoundaryFaces;
|
||||
|
||||
int meshgen; // see MeshGenerator()
|
||||
int mesh_geoms; // sum of (1 << geom) for all geom of all dimensions
|
||||
// see MeshGenerator(); global in parallel
|
||||
int meshgen;
|
||||
// sum of (1 << geom) for all geom of all dimensions; local in parallel
|
||||
int mesh_geoms;
|
||||
|
||||
// Counter for Mesh transformations: refinement, derefinement, rebalancing.
|
||||
// Used for checking during Update operations on objects depending on the
|
||||
@@ -307,11 +310,11 @@ protected:
|
||||
void Destroy(); // Delete all owned data.
|
||||
void ResetLazyData();
|
||||
|
||||
Element *ReadElementWithoutAttr(std::istream &);
|
||||
static void PrintElementWithoutAttr(const Element *, std::ostream &);
|
||||
Element *ReadElementWithoutAttr(std::istream &input);
|
||||
static void PrintElementWithoutAttr(const Element *el, std::ostream &os);
|
||||
|
||||
Element *ReadElement(std::istream &);
|
||||
static void PrintElement(const Element *, std::ostream &);
|
||||
Element *ReadElement(std::istream &input);
|
||||
static void PrintElement(const Element *el, std::ostream &os);
|
||||
|
||||
// Readers for different mesh formats, used in the Load() method.
|
||||
// The implementations of these methods are in mesh_readers.cpp.
|
||||
@@ -558,7 +561,7 @@ protected:
|
||||
mfem v1.2 format with the given section_delimiter at the end.
|
||||
If @a comments is non-empty, it will be printed after the first line of
|
||||
the file, and each line should begin with '#'. */
|
||||
void Printer(std::ostream &out = mfem::out,
|
||||
void Printer(std::ostream &os = mfem::out,
|
||||
std::string section_delimiter = "",
|
||||
const std::string &comments = "") const;
|
||||
|
||||
@@ -2124,7 +2127,11 @@ public:
|
||||
/// Set the curvature of the mesh nodes using the given polynomial degree.
|
||||
/** Creates a nodal GridFunction if one doesn't already exist.
|
||||
|
||||
@param[in] order Polynomial degree of the nodal FE space.
|
||||
@param[in] order Polynomial degree of the nodal FE space. If this
|
||||
value is <= 0 then the method will remove the
|
||||
nodal GridFunction and the Mesh will use the
|
||||
vertices array instead; the other arguments are
|
||||
ignored in this case.
|
||||
@param[in] discont Whether to use a discontinuous or continuous
|
||||
finite element space (continuous is default).
|
||||
@param[in] space_dim The space dimension (optional).
|
||||
@@ -2330,7 +2337,7 @@ public:
|
||||
std::ostream &os, int elem_attr = 0) const;
|
||||
|
||||
void PrintElementsWithPartitioning (int *partitioning,
|
||||
std::ostream &out,
|
||||
std::ostream &os,
|
||||
int interior_faces = 0);
|
||||
|
||||
/// Print set of disjoint surfaces:
|
||||
@@ -2338,13 +2345,13 @@ public:
|
||||
* If Aface_face(i,j) != 0, print face j as a boundary
|
||||
* element with attribute i+1.
|
||||
*/
|
||||
void PrintSurfaces(const Table &Aface_face, std::ostream &out) const;
|
||||
void PrintSurfaces(const Table &Aface_face, std::ostream &os) const;
|
||||
|
||||
/// Auxiliary method used by PrintCharacteristics().
|
||||
/** It is also used in the `mesh-explorer` miniapp. */
|
||||
static void PrintElementsByGeometry(int dim,
|
||||
const Array<int> &num_elems_by_geom,
|
||||
std::ostream &out);
|
||||
std::ostream &os);
|
||||
|
||||
/** @brief Compute and print mesh characteristics such as number of vertices,
|
||||
number of elements, number of boundary elements, minimal and maximal
|
||||
@@ -2364,7 +2371,7 @@ public:
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
/// Output an NCMesh-compatible debug dump.
|
||||
void DebugDump(std::ostream &out) const;
|
||||
void DebugDump(std::ostream &os) const;
|
||||
#endif
|
||||
|
||||
/// @}
|
||||
@@ -2445,7 +2452,334 @@ public:
|
||||
|
||||
/** Overload operator<< for std::ostream and Mesh; valid also for the derived
|
||||
class ParMesh */
|
||||
std::ostream &operator<<(std::ostream &out, const Mesh &mesh);
|
||||
std::ostream &operator<<(std::ostream &os, const Mesh &mesh);
|
||||
|
||||
/// @brief Print function for Mesh::FaceInformation.
|
||||
std::ostream& operator<<(std::ostream &os, const Mesh::FaceInformation& info);
|
||||
|
||||
|
||||
/** @brief Class containing a minimal description of a part (a subset of the
|
||||
elements) of a Mesh and its connectivity to other parts.
|
||||
|
||||
The main purpose of this class is to facilitate the partitioning of serial
|
||||
meshes (in serial, i.e. on one processor) and save the parts in parallel
|
||||
MFEM mesh format.
|
||||
|
||||
Another potential futrure purpose of this class could be to facilitate
|
||||
exchange of MeshParts between MPI ranks for repartitioning purposes. It can
|
||||
also potentially be used to implement parallel mesh I/O functions with
|
||||
partitionings that have number of parts different from the number of MPI
|
||||
tasks.
|
||||
|
||||
@note Parts of NURBS or non-conforming meshes cannot be fully described by
|
||||
this class alone with its current data members. Such extensions may be added
|
||||
in the future.
|
||||
*/
|
||||
class MeshPart
|
||||
{
|
||||
protected:
|
||||
struct Entity { int geom; int num_verts; const int *verts; };
|
||||
struct EntityHelper
|
||||
{
|
||||
int dim, num_entities;
|
||||
int geom_offsets[Geometry::NumGeom+1];
|
||||
typedef const Array<int> entity_to_vertex_type[Geometry::NumGeom];
|
||||
entity_to_vertex_type &entity_to_vertex;
|
||||
|
||||
EntityHelper(int dim_,
|
||||
const Array<int> (&entity_to_vertex_)[Geometry::NumGeom]);
|
||||
Entity FindEntity(int bytype_entity_id);
|
||||
};
|
||||
|
||||
public:
|
||||
/// Reference space dimension of the elements
|
||||
int dimension;
|
||||
|
||||
/// Dimension of the physical space into which the MeshPart is embedded.
|
||||
int space_dimension;
|
||||
|
||||
/// Number of vertices
|
||||
int num_vertices;
|
||||
|
||||
/// Number of elements with reference space dimension equal to 'dimension'.
|
||||
int num_elements;
|
||||
|
||||
/** @brief Number of boundary elements with reference space dimension equal
|
||||
to 'dimension'-1. */
|
||||
int num_bdr_elements;
|
||||
|
||||
/**
|
||||
Each 'entity_to_vertex[geom]' describes the entities of Geometry::Type
|
||||
'geom' in terms of their vertices. The number of entities of type 'geom'
|
||||
is:
|
||||
|
||||
num_entities[geom] = size('entity_to_vertex[geom]')/num_vertices[geom]
|
||||
|
||||
The number of all elements, 'num_elements', is:
|
||||
|
||||
'num_elements' = sum_{dim[geom]=='dimension'} num_entities[geom]
|
||||
|
||||
and the number of all boundary elements, 'num_bdr_elements' is:
|
||||
|
||||
'num_bdr_elements' = sum_{dim[geom]=='dimension'-1} num_entities[geom]
|
||||
|
||||
Note that 'entity_to_vertex' does NOT describe all "faces" in the mesh
|
||||
part (i.e. all 'dimension'-1 entities) but only the boundary elements.
|
||||
Also, note that lower dimesional entities ('dimension'-2 and lower) are
|
||||
NOT described by the respective array, i.e. the array will be empty.
|
||||
*/
|
||||
Array<int> entity_to_vertex[Geometry::NumGeom];
|
||||
|
||||
/** @brief Store the refinement flags for tetraheral elements. If all tets
|
||||
have zero refinement flags then this array is empty, i.e. has size 0. */
|
||||
Array<int> tet_refine_flags;
|
||||
|
||||
/**
|
||||
Terminology: "by-type" element/boundary ordering: ordered by
|
||||
Geometry::Type and within each Geometry::Type 'geom' ordered as in
|
||||
'entity_to_vertex[geom]'.
|
||||
|
||||
Optional re-ordering of the elements that will be used by (Par)Mesh
|
||||
objects constructed from this MeshPart. This array maps "natural" element
|
||||
ids (used by the Mesh/ParMesh objects) to "by-type" element ids (see
|
||||
above):
|
||||
|
||||
"by-type" element id = element_map["natural" element id]
|
||||
|
||||
The size of the array is either 'num_elements' or 0 when no re-ordering is
|
||||
needed (then "by-type" id == "natural" id).
|
||||
*/
|
||||
Array<int> element_map;
|
||||
|
||||
/// Optional re-ordering for the boundary elements, similar to 'element_map'.
|
||||
Array<int> boundary_map;
|
||||
|
||||
/**
|
||||
Element attributes. Ordered using the "natural" element ordering defined
|
||||
by the array 'element_map'. The size of this array is 'num_elements'.
|
||||
*/
|
||||
Array<int> attributes;
|
||||
|
||||
/**
|
||||
Boundary element attributes. Ordered using the "natural" boundary element
|
||||
ordering defined by the array 'boundary_map'. The size of this array is
|
||||
'num_bdr_elements'.
|
||||
*/
|
||||
Array<int> bdr_attributes;
|
||||
|
||||
/**
|
||||
Optional vertex coordinates. The size of the array is either
|
||||
|
||||
size = 'space_dimension' * 'num_vertices'
|
||||
|
||||
or 0 when the vertex coordinates are not used, i.e. when the MeshPart uses
|
||||
a nodal GridFunction to describe its location in physical space. This
|
||||
array uses Ordering::byVDIM: "X0,Y0,Z0, X1,Y1,Z1, ...".
|
||||
*/
|
||||
Array<real_t> vertex_coordinates;
|
||||
|
||||
/**
|
||||
Optional serial Mesh object constructed on demand using the method
|
||||
GetMesh(). One use case for it is when one wants to construct FE spaces
|
||||
and GridFunction%s on the MeshPart for saving or MPI communication.
|
||||
*/
|
||||
std::unique_ptr<Mesh> mesh;
|
||||
|
||||
/**
|
||||
Nodal FE space defined on 'mesh' used by the GridFunction 'nodes'. Uses
|
||||
the FE collection from the global nodal FE space.
|
||||
*/
|
||||
std::unique_ptr<FiniteElementSpace> nodal_fes;
|
||||
|
||||
/**
|
||||
'nodes': pointer to a GridFunction describing the physical location of the
|
||||
MeshPart. Used for describing high-order and periodic meshes. This
|
||||
GridFunction is defined on the FE space 'nodal_fes' which, in turn, is
|
||||
defined on the Mesh 'mesh'.
|
||||
*/
|
||||
std::unique_ptr<GridFunction> nodes;
|
||||
|
||||
/** @name Connectivity to other MeshPart objects */
|
||||
///@{
|
||||
|
||||
/// Total number of MeshParts
|
||||
int num_parts;
|
||||
|
||||
/** @brief Index of the part described by this MeshPart:
|
||||
0 <= 'my_part_id' < 'num_parts' */
|
||||
int my_part_id;
|
||||
|
||||
/**
|
||||
A group G is a subset of the set { 0, 1, ..., 'num_parts'-1 } for which
|
||||
there is a mesh entity E (of any dimension) in the global mesh such that
|
||||
G is the set of the parts assigned (by the partitioning array) to the
|
||||
elements adjacent to E. The MeshPart describes only the "neighbor" groups,
|
||||
i.e. the groups that contain 'my_part_id'. The Table 'my_groups' defines
|
||||
the "neighbor" groups in terms of their part ids. In other words, it maps
|
||||
"neighbor" group ids to a (sorted) list of part ids. In particular, the
|
||||
number of "neighbor" groups is given by 'my_groups.Size()'. The "local"
|
||||
group { 'my_part_id' } has index 0 in 'my_groups'.
|
||||
*/
|
||||
Table my_groups;
|
||||
|
||||
/**
|
||||
Shared entities for this MeshPart are mesh entities of all dimensions less
|
||||
than 'dimension' that are generated by the elements of this MeshPart and
|
||||
at least one other MeshPart.
|
||||
|
||||
The Table 'group_shared_entity_to_vertex[geom]' defines, for each group,
|
||||
the shared entities of Geometry::Type 'geom'. Each row (corresponding to a
|
||||
"neighbor" group, as defined by 'my_groups') in the Table defines the
|
||||
shared entities in a way similar to the arrays 'entity_to_vertex[geom]'.
|
||||
The "local" group (with index 0) does not have any shared entities, so the
|
||||
0-th row in the Table is always empty.
|
||||
|
||||
IMPORTANT: the descriptions of the groups in this MeshPart must match
|
||||
their descriptions in all neighboring MeshParts. This includes the
|
||||
ordering of the shared entities within the group, as well as the vertex
|
||||
ordering of each shared entity.
|
||||
*/
|
||||
Table group_shared_entity_to_vertex[Geometry::NumGeom];
|
||||
|
||||
///@}
|
||||
|
||||
/** @brief Write the MeshPart to a stream using the parallel format
|
||||
"MFEM mesh v1.2". */
|
||||
void Print(std::ostream &os) const;
|
||||
|
||||
/** @brief Construct a serial Mesh object from the MeshPart.
|
||||
|
||||
The nodes of 'mesh' are NOT initialized by this method, however, the
|
||||
nodal FE space and nodal GridFunction can be created and then attached to
|
||||
the 'mesh'. The Mesh is constructed only if 'mesh' is empty, otherwise
|
||||
the method simply returns the object held by 'mesh'.
|
||||
*/
|
||||
Mesh &GetMesh();
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class that allows serial meshes to be partitioned into MeshPart
|
||||
objects, typically one MeshPart at a time, which can then be used to write
|
||||
the local mesh in parallel MFEM mesh format.
|
||||
|
||||
Sample usage of this class: partition a serial mesh and save it in parallel
|
||||
MFEM format:
|
||||
\code
|
||||
// The array 'partitioning' can be obtained e.g. from
|
||||
// mesh->GeneratePartitioning():
|
||||
void usage1(Mesh *mesh, int num_parts, int *partitioning)
|
||||
{
|
||||
MeshPartitioner partitioner(*mesh, num_parts, partitioning);
|
||||
MeshPart mesh_part;
|
||||
for (int i = 0; i < num_parts; i++)
|
||||
{
|
||||
partitioner.ExtractPart(i, mesh_part);
|
||||
ofstream omesh(MakeParFilename("my-mesh.", i));
|
||||
mesh_part.Print(omesh);
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
|
||||
This class can also be used to partition a mesh and GridFunction(s) and save
|
||||
them in parallel:
|
||||
\code
|
||||
// The array 'partitioning' can be obtained e.g. from
|
||||
// mesh->GeneratePartitioning():
|
||||
void usage2(Mesh *mesh, int num_parts, int *partitioning,
|
||||
GridFunction *gf)
|
||||
{
|
||||
MeshPartitioner partitioner(*mesh, num_parts, partitioning);
|
||||
MeshPart mesh_part;
|
||||
for (int i = 0; i < num_parts; i++)
|
||||
{
|
||||
partitioner.ExtractPart(i, mesh_part);
|
||||
ofstream omesh(MakeParFilename("my-mesh.", i));
|
||||
mesh_part.Print(omesh);
|
||||
auto lfes = partitioner.ExtractFESpace(mesh_part, *gf->FESpace());
|
||||
auto lgf = partitioner.ExtractGridFunction(mesh_part, *gf, *lfes);
|
||||
ofstream ofield(MakeParFilename("my-field.", i));
|
||||
lgf->Save(ofield);
|
||||
}
|
||||
}
|
||||
\endcode
|
||||
*/
|
||||
class MeshPartitioner
|
||||
{
|
||||
protected:
|
||||
Mesh &mesh;
|
||||
Array<int> partitioning;
|
||||
Table part_to_element;
|
||||
Table part_to_boundary;
|
||||
Table edge_to_element;
|
||||
Table vertex_to_element;
|
||||
|
||||
public:
|
||||
/** @brief Construct a MeshPartitioner.
|
||||
|
||||
@param[in] mesh_ Mesh to be partitioned into MeshPart%s.
|
||||
@param[in] num_parts_ Number of parts to partition the mesh into.
|
||||
@param[in] partitioning_ Partitioning array: for every element in the
|
||||
mesh gives the partition it belongs to; if NULL,
|
||||
partitioning will be generated internally by
|
||||
calling Mesh::GeneratePartitioning().
|
||||
@param[in] part_method Partitioning method to be used in the call to
|
||||
Mesh::GeneratePartitioning() when the provided
|
||||
input partitioning is NULL.
|
||||
*/
|
||||
MeshPartitioner(Mesh &mesh_, int num_parts_, int *partitioning_ = NULL,
|
||||
int part_method = 1);
|
||||
|
||||
/** @brief Construct a MeshPart corresponding to the given @a part_id.
|
||||
|
||||
@param[in] part_id Partition index to extract; valid values are in
|
||||
the range [0, num_parts).
|
||||
@param[out] mesh_part Output MeshPart object; its contents is
|
||||
overwritten, while potentially reusing existing
|
||||
dynamic memory allocations.
|
||||
*/
|
||||
void ExtractPart(int part_id, MeshPart &mesh_part) const;
|
||||
|
||||
/** @brief Construct a local version of the given FiniteElementSpace
|
||||
@a global_fespace corresponding to the given @a mesh_part.
|
||||
|
||||
@param[in,out] mesh_part MeshPart on which to construct the local
|
||||
FiniteElementSpace; this object is
|
||||
generally modified by this call since it
|
||||
calls mesh_part.GetMesh() to ensure the
|
||||
local mesh is constructed.
|
||||
@param[in] global_fespace The global FiniteElementSpace that should
|
||||
be restricted to the @a mesh_part.
|
||||
|
||||
@returns A FiniteElementSpace pointer stored in a unique_ptr. The
|
||||
returned local FiniteElementSpace is built on the Mesh object
|
||||
contained in @a mesh_part (MeshPart::mesh) and it reuses the
|
||||
FiniteElementCollection of the @a global_fespace.
|
||||
*/
|
||||
std::unique_ptr<FiniteElementSpace>
|
||||
ExtractFESpace(MeshPart &mesh_part,
|
||||
const FiniteElementSpace &global_fespace) const;
|
||||
|
||||
/** @brief Construct a local version of the given GridFunction, @a global_gf,
|
||||
corresponding to the given @a mesh_part. The respective data is copied
|
||||
from @a global_gf to the returned local GridFunction.
|
||||
|
||||
@param[in] mesh_part MeshPart on which to construct the local
|
||||
GridFunction.
|
||||
@param[in] global_gf The global GridFunction that should be
|
||||
restricted to the @a mesh_part.
|
||||
@param[in,out] local_fespace The local FiniteElementSpace corresponding
|
||||
to @a mesh_part, e.g. constructed by the
|
||||
method ExtractFESpace().
|
||||
|
||||
@returns A GridFunction pointer stored in a unique_ptr. The returned
|
||||
local GridFunction is initialized with data appropriately copied
|
||||
from @a global_gf.
|
||||
*/
|
||||
std::unique_ptr<GridFunction>
|
||||
ExtractGridFunction(const MeshPart &mesh_part,
|
||||
const GridFunction &global_gf,
|
||||
FiniteElementSpace &local_fespace) const;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Structure for storing mesh geometric factors: coordinates, Jacobians,
|
||||
@@ -2454,7 +2788,6 @@ std::ostream &operator<<(std::ostream &out, const Mesh &mesh);
|
||||
Mesh. See Mesh::GetGeometricFactors(). */
|
||||
class GeometricFactors
|
||||
{
|
||||
|
||||
private:
|
||||
void Compute(const GridFunction &nodes,
|
||||
MemoryType d_mt = MemoryType::DEFAULT);
|
||||
@@ -2502,6 +2835,7 @@ public:
|
||||
Vector detJ;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Structure for storing face geometric factors: coordinates, Jacobians,
|
||||
determinants of the Jacobians, and normal vectors. */
|
||||
/** Typically objects of this type are constructed and owned by objects of class
|
||||
@@ -2556,6 +2890,7 @@ public:
|
||||
Vector normal;
|
||||
};
|
||||
|
||||
|
||||
/// Class used to extrude the nodes of a mesh
|
||||
class NodeExtrudeCoefficient : public VectorCoefficient
|
||||
{
|
||||
@@ -2587,9 +2922,6 @@ inline void ShiftRight(int &a, int &b, int &c)
|
||||
a = c; c = b; b = t;
|
||||
}
|
||||
|
||||
/// @brief Print function for Mesh::FaceInformation.
|
||||
std::ostream& operator<<(std::ostream& os, const Mesh::FaceInformation& info);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+7
-8
@@ -256,9 +256,6 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
// build svert_lvert mapping
|
||||
BuildSharedVertMapping(nsvert, vert_element, vert_global_local);
|
||||
delete vert_element;
|
||||
|
||||
SetMeshGen();
|
||||
meshgen = mesh.meshgen; // copy the global 'meshgen'
|
||||
}
|
||||
|
||||
if (mesh.NURBSext)
|
||||
@@ -1527,6 +1524,7 @@ ParMesh ParMesh::MakeSimplicial(ParMesh &orig_mesh)
|
||||
void ParMesh::Finalize(bool refine, bool fix_orientation)
|
||||
{
|
||||
const int meshgen_save = meshgen; // Mesh::Finalize() may call SetMeshGen()
|
||||
// 'mesh_geoms' is local, so there's no need to save and restore it.
|
||||
|
||||
Mesh::Finalize(refine, fix_orientation);
|
||||
|
||||
@@ -4807,7 +4805,7 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
|
||||
if (NURBSext)
|
||||
{
|
||||
Printer(os, comments); // does not print shared boundary
|
||||
Printer(os, "", comments); // does not print shared boundary
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4935,7 +4933,7 @@ void ParMesh::Print(std::ostream &os, const std::string &comments) const
|
||||
|
||||
if (set_names)
|
||||
{
|
||||
os << "mfem_mesh_end\n";
|
||||
os << "\nmfem_mesh_end" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5286,7 +5284,7 @@ void ParMesh::PrintAsSerial(std::ostream &os, const std::string &comments) const
|
||||
Mesh serialmesh = GetSerialMesh(save_rank);
|
||||
if (MyRank == save_rank)
|
||||
{
|
||||
serialmesh.Printer(os, comments);
|
||||
serialmesh.Printer(os, "", comments);
|
||||
}
|
||||
MPI_Barrier(MyComm);
|
||||
}
|
||||
@@ -6325,11 +6323,11 @@ void ParMesh::ParPrint(ostream &os, const std::string &comments) const
|
||||
if (Nonconforming())
|
||||
{
|
||||
// the NC mesh format works both in serial and in parallel
|
||||
Printer(os, comments);
|
||||
Printer(os, "", comments);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write out serial mesh. Tell serial mesh to deliniate the end of it's
|
||||
// Write out serial mesh. Tell serial mesh to delineate the end of its
|
||||
// output with 'mfem_serial_mesh_end' instead of 'mfem_mesh_end', as we will
|
||||
// be adding additional parallel mesh information.
|
||||
Printer(os, "mfem_serial_mesh_end", comments);
|
||||
@@ -6346,6 +6344,7 @@ void ParMesh::ParPrint(ostream &os, const std::string &comments) const
|
||||
{
|
||||
os << "total_shared_faces " << sface_lface.Size() << '\n';
|
||||
}
|
||||
os << "\n# group 0 has no shared entities\n";
|
||||
for (int gr = 1; gr < GetNGroups(); gr++)
|
||||
{
|
||||
{
|
||||
|
||||
@@ -53,7 +53,7 @@ void Tetrahedron::Init(int ind1, int ind2, int ind3, int ind4, int attr,
|
||||
}
|
||||
|
||||
void Tetrahedron::ParseRefinementFlag(int refinement_edges[2], int &type,
|
||||
int &flag)
|
||||
int &flag) const
|
||||
{
|
||||
int i, f = refinement_flag;
|
||||
|
||||
@@ -134,9 +134,10 @@ void Tetrahedron::CreateRefinementFlag(int refinement_edges[2], int type,
|
||||
refinement_flag |= refinement_edges[0];
|
||||
}
|
||||
|
||||
void Tetrahedron::GetMarkedFace(const int face, int *fv)
|
||||
void Tetrahedron::GetMarkedFace(const int face, int *fv) const
|
||||
{
|
||||
int re[2], type, flag, *tv = this->indices;
|
||||
int re[2], type, flag;
|
||||
const int *tv = this->indices;
|
||||
ParseRefinementFlag(re, type, flag);
|
||||
switch (face)
|
||||
{
|
||||
|
||||
@@ -58,12 +58,13 @@ public:
|
||||
/// Return element's type.
|
||||
Type GetType() const override { return Element::TETRAHEDRON; }
|
||||
|
||||
void ParseRefinementFlag(int refinement_edges[2], int &type, int &flag);
|
||||
void ParseRefinementFlag(int refinement_edges[2], int &type,
|
||||
int &flag) const;
|
||||
void CreateRefinementFlag(int refinement_edges[2], int type, int flag = 0);
|
||||
|
||||
void GetMarkedFace(const int face, int *fv);
|
||||
void GetMarkedFace(const int face, int *fv) const;
|
||||
|
||||
int GetRefinementFlag() { return refinement_flag; }
|
||||
int GetRefinementFlag() const { return refinement_flag; }
|
||||
|
||||
void SetRefinementFlag(int rf) { refinement_flag = rf; }
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ clean-build:
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f mobius-strip.mesh klein-bottle.mesh mesh-explorer.mesh
|
||||
@rm -f mobius-strip.mesh klein-bottle.mesh mesh-explorer.mesh*
|
||||
@rm -f toroid-*.mesh twist-*.mesh trimmer.mesh reflected.mesh
|
||||
@rm -f partitioning.txt shaper.mesh extruder.mesh
|
||||
@rm -f optimized* perturbed* polar-nc.mesh
|
||||
|
||||
@@ -308,6 +308,7 @@ int main (int argc, char *argv[])
|
||||
partitioning = 0;
|
||||
bdr_partitioning.SetSize(mesh->GetNBE());
|
||||
bdr_partitioning = 0;
|
||||
np = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -382,7 +383,8 @@ int main (int argc, char *argv[])
|
||||
"f) Find physical point in reference space\n"
|
||||
"p) Generate a partitioning\n"
|
||||
"o) Reorder elements\n"
|
||||
"S) Save in MFEM format\n"
|
||||
"S) Save in MFEM serial format\n"
|
||||
"T) Save in MFEM parallel format using the current partitioning\n"
|
||||
"V) Save in VTK format (only linear and quadratic meshes)\n"
|
||||
"D) Save as a DataCollection\n"
|
||||
"q) Quit\n"
|
||||
@@ -1037,7 +1039,7 @@ int main (int argc, char *argv[])
|
||||
partitioning.SetSize(mesh->GetNE());
|
||||
for (int i = 0; i < mesh->GetNE(); i++)
|
||||
{
|
||||
partitioning[i] = i * np / mesh->GetNE();
|
||||
partitioning[i] = (long long)i * np / mesh->GetNE();
|
||||
}
|
||||
recover_bdr_partitioning(mesh, partitioning, bdr_partitioning);
|
||||
}
|
||||
@@ -1250,6 +1252,33 @@ int main (int argc, char *argv[])
|
||||
cout << "New mesh file: " << omesh_file << endl;
|
||||
}
|
||||
|
||||
if (mk == 'T')
|
||||
{
|
||||
string mesh_prefix("mesh-explorer.mesh."), line;
|
||||
MeshPartitioner partitioner(*mesh, np, partitioning);
|
||||
MeshPart mesh_part;
|
||||
cout << "Enter mesh file prefix or press <enter> to use \""
|
||||
<< mesh_prefix << "\": " << flush;
|
||||
// extract and ignore all characters after 'T' up to and including the
|
||||
// new line:
|
||||
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||
getline(cin, line);
|
||||
if (!line.empty()) { mesh_prefix = line; }
|
||||
int precision;
|
||||
cout << "Enter floating point output precision (num. digits): "
|
||||
<< flush;
|
||||
cin >> precision;
|
||||
for (int i = 0; i < np; i++)
|
||||
{
|
||||
partitioner.ExtractPart(i, mesh_part);
|
||||
|
||||
ofstream omesh(MakeParFilename(mesh_prefix, i));
|
||||
omesh.precision(precision);
|
||||
mesh_part.Print(omesh);
|
||||
}
|
||||
cout << "New parallel mesh files: " << mesh_prefix << "<rank>" << endl;
|
||||
}
|
||||
|
||||
if (mk == 'V')
|
||||
{
|
||||
const char omesh_file[] = "mesh-explorer.vtk";
|
||||
|
||||
Reference in New Issue
Block a user