2504 lines
80 KiB
C++
2504 lines
80 KiB
C++
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
|
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
|
// reserved. See file COPYRIGHT for details.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability see http://mfem.googlecode.com.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU Lesser General Public License (as published by the Free
|
|
// Software Foundation) version 2.1 dated February 1999.
|
|
|
|
#ifdef MFEM_USE_MPI
|
|
|
|
#include "mesh_headers.hpp"
|
|
#include "../fem/fem.hpp"
|
|
#include "../general/sets.hpp"
|
|
|
|
ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
|
int part_method)
|
|
{
|
|
int i, j;
|
|
int *partitioning;
|
|
|
|
MyComm = comm;
|
|
MPI_Comm_size(MyComm, &NRanks);
|
|
MPI_Comm_rank(MyComm, &MyRank);
|
|
|
|
Dim = mesh.Dim;
|
|
|
|
if (partitioning_)
|
|
partitioning = partitioning_;
|
|
else
|
|
partitioning = mesh.GeneratePartitioning(NRanks, part_method);
|
|
|
|
// re-enumerate the partitions to better map to actual processor
|
|
// interconnect topology !?
|
|
|
|
Array<int> vert;
|
|
Array<int> vert_global_local(mesh.GetNV());
|
|
int vert_counter, element_counter, bdrelem_counter;
|
|
|
|
// build vert_global_local
|
|
for (i = 0; i < vert_global_local.Size(); i++)
|
|
vert_global_local[i] = -1;
|
|
|
|
element_counter = 0;
|
|
vert_counter = 0;
|
|
for (i = 0; i < mesh.GetNE(); i++)
|
|
if (partitioning[i] == MyRank)
|
|
{
|
|
mesh.GetElementVertices(i, vert);
|
|
element_counter++;
|
|
for (j = 0; j < vert.Size(); j++)
|
|
if (vert_global_local[vert[j]] < 0)
|
|
vert_global_local[vert[j]] = vert_counter++;
|
|
}
|
|
|
|
NumOfVertices = vert_counter;
|
|
NumOfElements = element_counter;
|
|
vertices.SetSize(NumOfVertices);
|
|
|
|
// preserve ordering when running in serial
|
|
if (NRanks == 1)
|
|
for (i = 0; i < vert_global_local.Size(); i++)
|
|
vert_global_local[i] = i;
|
|
|
|
// determine vertices
|
|
for (i = 0; i < vert_global_local.Size(); i++)
|
|
if (vert_global_local[i] >= 0)
|
|
vertices[vert_global_local[i]].SetCoords(mesh.GetVertex(i));
|
|
|
|
// determine elements
|
|
element_counter = 0;
|
|
elements.SetSize(NumOfElements);
|
|
for (i = 0; i < mesh.GetNE(); i++)
|
|
if (partitioning[i] == MyRank)
|
|
{
|
|
elements[element_counter] = mesh.GetElement(i)->Duplicate();
|
|
int *v = elements[element_counter]->GetVertices();
|
|
int nv = elements[element_counter]->GetNVertices();
|
|
for (j = 0; j < nv; j++)
|
|
v[j] = vert_global_local[v[j]];
|
|
element_counter++;
|
|
}
|
|
|
|
Table *edge_element = NULL;
|
|
|
|
// build boundary elements
|
|
if (Dim == 3)
|
|
{
|
|
NumOfBdrElements = 0;
|
|
for (i = 0; i < mesh.GetNBE(); i++)
|
|
{
|
|
int face = mesh.GetBdrElementEdgeIndex(i);
|
|
int el1, el2;
|
|
mesh.GetFaceElements(face, &el1, &el2);
|
|
if (partitioning[el1] == MyRank ||
|
|
(el2 >= 0 && partitioning[el2] == MyRank))
|
|
NumOfBdrElements++;
|
|
}
|
|
|
|
bdrelem_counter = 0;
|
|
boundary.SetSize(NumOfBdrElements);
|
|
for (i = 0; i < mesh.GetNBE(); i++)
|
|
{
|
|
int face = mesh.GetBdrElementEdgeIndex(i);
|
|
int el1, el2;
|
|
mesh.GetFaceElements(face, &el1, &el2);
|
|
if (partitioning[el1] == MyRank ||
|
|
(el2 >= 0 && partitioning[el2] == MyRank))
|
|
{
|
|
boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate();
|
|
int *v = boundary[bdrelem_counter]->GetVertices();
|
|
int nv = boundary[bdrelem_counter]->GetNVertices();
|
|
for (j = 0; j < nv; j++)
|
|
v[j] = vert_global_local[v[j]];
|
|
bdrelem_counter++;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if (Dim == 2)
|
|
{
|
|
edge_element = new Table;
|
|
Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
|
|
|
|
NumOfBdrElements = 0;
|
|
for (i = 0; i < mesh.GetNBE(); i++)
|
|
{
|
|
int edge = mesh.GetBdrElementEdgeIndex(i);
|
|
int el1, el2 = -1;
|
|
el1 = edge_element->GetRow(edge)[0];
|
|
if (edge_element->RowSize(edge) == 2)
|
|
el2 = edge_element->GetRow(edge)[1];
|
|
if (partitioning[el1] == MyRank ||
|
|
(el2 >= 0 && partitioning[el2] == MyRank))
|
|
NumOfBdrElements++;
|
|
}
|
|
|
|
bdrelem_counter = 0;
|
|
boundary.SetSize(NumOfBdrElements);
|
|
for (i = 0; i < mesh.GetNBE(); i++)
|
|
{
|
|
int edge = mesh.GetBdrElementEdgeIndex(i);
|
|
int el1, el2 = -1;
|
|
el1 = edge_element->GetRow(edge)[0];
|
|
if (edge_element->RowSize(edge) == 2)
|
|
el2 = edge_element->GetRow(edge)[1];
|
|
if (partitioning[el1] == MyRank ||
|
|
(el2 >= 0 && partitioning[el2] == MyRank))
|
|
{
|
|
boundary[bdrelem_counter] = mesh.GetBdrElement(i)->Duplicate();
|
|
int *v = boundary[bdrelem_counter]->GetVertices();
|
|
int nv = boundary[bdrelem_counter]->GetNVertices();
|
|
for (j = 0; j < nv; j++)
|
|
v[j] = vert_global_local[v[j]];
|
|
bdrelem_counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
meshgen = mesh.MeshGenerator();
|
|
|
|
attributes.SetSize(mesh.attributes.Size());
|
|
for (i = 0; i < attributes.Size(); i++)
|
|
attributes[i] = mesh.attributes[i];
|
|
bdr_attributes.SetSize(mesh.bdr_attributes.Size());
|
|
for (i = 0; i < bdr_attributes.Size(); i++)
|
|
bdr_attributes[i] = mesh.bdr_attributes[i];
|
|
|
|
// this is called by the default Mesh constructor
|
|
// InitTables();
|
|
|
|
el_to_edge = new Table;
|
|
NumOfEdges = Mesh::GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
|
|
|
STable3D *faces_tbl = NULL;
|
|
if (Dim == 3)
|
|
faces_tbl = GetElementToFaceTable(1);
|
|
else
|
|
NumOfFaces = 0;
|
|
GenerateFaces();
|
|
|
|
c_el_to_edge = NULL;
|
|
|
|
ListOfIntegerSets groups;
|
|
IntegerSet group;
|
|
|
|
// the first group is the local one
|
|
group.Recreate(1, &MyRank);
|
|
groups.Insert(group);
|
|
|
|
#ifdef MFEM_DEBUG
|
|
if (Dim < 3 && mesh.GetNFaces() != 0)
|
|
{
|
|
cerr << "ParMesh::ParMesh (proc " << MyRank << ") : "
|
|
"(Dim < 3 && mesh.GetNFaces() != 0) is true!" << endl;
|
|
mfem_error();
|
|
}
|
|
#endif
|
|
// determine shared faces
|
|
int sface_counter = 0;
|
|
Array<int> face_group(mesh.GetNFaces());
|
|
for (i = 0; i < face_group.Size(); i++)
|
|
{
|
|
int el[2];
|
|
face_group[i] = -1;
|
|
mesh.GetFaceElements(i, &el[0], &el[1]);
|
|
if (el[1] >= 0)
|
|
{
|
|
el[0] = partitioning[el[0]];
|
|
el[1] = partitioning[el[1]];
|
|
if ((el[0] == MyRank && el[1] != MyRank) ||
|
|
(el[0] != MyRank && el[1] == MyRank))
|
|
{
|
|
group.Recreate(2, el);
|
|
face_group[i] = groups.Insert(group) - 1;
|
|
sface_counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// determine shared edges
|
|
int sedge_counter = 0;
|
|
if (!edge_element)
|
|
{
|
|
edge_element = new Table;
|
|
Transpose(mesh.ElementToEdgeTable(), *edge_element, mesh.GetNEdges());
|
|
}
|
|
for (i = 0; i < edge_element->Size(); i++)
|
|
{
|
|
int me = 0, others = 0;
|
|
for (j = edge_element->GetI()[i]; j < edge_element->GetI()[i+1]; j++)
|
|
{
|
|
edge_element->GetJ()[j] = partitioning[edge_element->GetJ()[j]];
|
|
if (edge_element->GetJ()[j] == MyRank)
|
|
me = 1;
|
|
else
|
|
others = 1;
|
|
}
|
|
|
|
if (me && others)
|
|
{
|
|
sedge_counter++;
|
|
group.Recreate(edge_element->RowSize(i), edge_element->GetRow(i));
|
|
edge_element->GetRow(i)[0] = groups.Insert(group) - 1;
|
|
}
|
|
else
|
|
edge_element->GetRow(i)[0] = -1;
|
|
}
|
|
|
|
// determine shared vertices
|
|
int svert_counter = 0;
|
|
Table *vert_element = mesh.GetVertexToElementTable(); // we must delete this
|
|
|
|
for (i = 0; i < vert_element->Size(); i++)
|
|
{
|
|
int me = 0, others = 0;
|
|
for (j = vert_element->GetI()[i]; j < vert_element->GetI()[i+1]; j++)
|
|
{
|
|
vert_element->GetJ()[j] = partitioning[vert_element->GetJ()[j]];
|
|
if (vert_element->GetJ()[j] == MyRank)
|
|
me = 1;
|
|
else
|
|
others = 1;
|
|
}
|
|
|
|
if (me && others)
|
|
{
|
|
svert_counter++;
|
|
group.Recreate(vert_element->RowSize(i), vert_element->GetRow(i));
|
|
vert_element->GetRow(i)[0] = groups.Insert(group) - 1;
|
|
}
|
|
else
|
|
vert_element->GetRow(i)[0] = -1;
|
|
}
|
|
|
|
// build group_sface
|
|
group_sface.MakeI(groups.Size()-1);
|
|
|
|
for (i = 0; i < face_group.Size(); i++)
|
|
if (face_group[i] >= 0)
|
|
group_sface.AddAColumnInRow(face_group[i]);
|
|
|
|
group_sface.MakeJ();
|
|
|
|
sface_counter = 0;
|
|
for (i = 0; i < face_group.Size(); i++)
|
|
if (face_group[i] >= 0)
|
|
group_sface.AddConnection(face_group[i], sface_counter++);
|
|
|
|
group_sface.ShiftUpI();
|
|
|
|
// build group_sedge
|
|
group_sedge.MakeI(groups.Size()-1);
|
|
|
|
for (i = 0; i < edge_element->Size(); i++)
|
|
if (edge_element->GetRow(i)[0] >= 0)
|
|
group_sedge.AddAColumnInRow(edge_element->GetRow(i)[0]);
|
|
|
|
group_sedge.MakeJ();
|
|
|
|
sedge_counter = 0;
|
|
for (i = 0; i < edge_element->Size(); i++)
|
|
if (edge_element->GetRow(i)[0] >= 0)
|
|
group_sedge.AddConnection(edge_element->GetRow(i)[0],
|
|
sedge_counter++);
|
|
|
|
group_sedge.ShiftUpI();
|
|
|
|
// build group_svert
|
|
group_svert.MakeI(groups.Size()-1);
|
|
|
|
for (i = 0; i < vert_element->Size(); i++)
|
|
if (vert_element->GetRow(i)[0] >= 0)
|
|
group_svert.AddAColumnInRow(vert_element->GetRow(i)[0]);
|
|
|
|
group_svert.MakeJ();
|
|
|
|
svert_counter = 0;
|
|
for (i = 0; i < vert_element->Size(); i++)
|
|
if (vert_element->GetRow(i)[0] >= 0)
|
|
group_svert.AddConnection(vert_element->GetRow(i)[0],
|
|
svert_counter++);
|
|
|
|
group_svert.ShiftUpI();
|
|
|
|
// build shared_faces and sface_lface
|
|
shared_faces.SetSize(sface_counter);
|
|
sface_lface. SetSize(sface_counter);
|
|
|
|
if (Dim == 3)
|
|
{
|
|
sface_counter = 0;
|
|
for (i = 0; i < face_group.Size(); i++)
|
|
if (face_group[i] >= 0)
|
|
{
|
|
shared_faces[sface_counter] = mesh.GetFace(i)->Duplicate();
|
|
int *v = shared_faces[sface_counter]->GetVertices();
|
|
int nv = shared_faces[sface_counter]->GetNVertices();
|
|
for (j = 0; j < nv; j++)
|
|
v[j] = vert_global_local[v[j]];
|
|
switch (shared_faces[sface_counter]->GetType())
|
|
{
|
|
case Element::TRIANGLE:
|
|
sface_lface[sface_counter] = (*faces_tbl)(v[0], v[1], v[2]);
|
|
// mark the shared face for refinement by reorienting
|
|
// it according to the refinement flag in the tetradron
|
|
// to which this shared face belongs to.
|
|
{
|
|
int lface = sface_lface[sface_counter];
|
|
Tetrahedron *tet =
|
|
(Tetrahedron *)(elements[faces_info[lface].Elem1No]);
|
|
int re[2], type, flag, *tv;
|
|
tet->ParseRefinementFlag(re, type, flag);
|
|
tv = tet->GetVertices();
|
|
switch (faces_info[lface].Elem1Inf/64)
|
|
{
|
|
case 0:
|
|
switch (re[1])
|
|
{
|
|
case 1: v[0] = tv[1]; v[1] = tv[2]; v[2] = tv[3]; break;
|
|
case 4: v[0] = tv[3]; v[1] = tv[1]; v[2] = tv[2]; break;
|
|
case 5: v[0] = tv[2]; v[1] = tv[3]; v[2] = tv[1]; break;
|
|
}
|
|
break;
|
|
case 1:
|
|
switch (re[0])
|
|
{
|
|
case 2: v[0] = tv[2]; v[1] = tv[0]; v[2] = tv[3]; break;
|
|
case 3: v[0] = tv[0]; v[1] = tv[3]; v[2] = tv[2]; break;
|
|
case 5: v[0] = tv[3]; v[1] = tv[2]; v[2] = tv[0]; break;
|
|
}
|
|
break;
|
|
case 2:
|
|
v[0] = tv[0]; v[1] = tv[1]; v[2] = tv[3];
|
|
break;
|
|
case 3:
|
|
v[0] = tv[1]; v[1] = tv[0]; v[2] = tv[2];
|
|
break;
|
|
}
|
|
// flip the shared face in the processor that owns the
|
|
// second element (in 'mesh')
|
|
{
|
|
int gl_el1, gl_el2;
|
|
mesh.GetFaceElements(i, &gl_el1, &gl_el2);
|
|
if (MyRank == partitioning[gl_el2])
|
|
{
|
|
const int t = v[0]; v[0] = v[1]; v[1] = t;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Element::QUADRILATERAL:
|
|
sface_lface[sface_counter] =
|
|
(*faces_tbl)(v[0], v[1], v[2], v[3]);
|
|
break;
|
|
}
|
|
sface_counter++;
|
|
}
|
|
|
|
delete faces_tbl;
|
|
}
|
|
|
|
// build shared_edges and sedge_ledge
|
|
shared_edges.SetSize(sedge_counter);
|
|
sedge_ledge. SetSize(sedge_counter);
|
|
|
|
{
|
|
DSTable v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(v_to_v);
|
|
|
|
sedge_counter = 0;
|
|
for (i = 0; i < edge_element->Size(); i++)
|
|
if (edge_element->GetRow(i)[0] >= 0)
|
|
{
|
|
mesh.GetEdgeVertices(i, vert);
|
|
|
|
shared_edges[sedge_counter] =
|
|
new Segment(vert_global_local[vert[0]],
|
|
vert_global_local[vert[1]], 1);
|
|
|
|
if ((sedge_ledge[sedge_counter] =
|
|
v_to_v(vert_global_local[vert[0]],
|
|
vert_global_local[vert[1]])) < 0)
|
|
{
|
|
cerr << "\n\n\n" << MyRank << ": ParMesh::ParMesh: "
|
|
<< "ERROR in v_to_v\n\n" << endl;
|
|
mfem_error();
|
|
}
|
|
|
|
sedge_counter++;
|
|
}
|
|
}
|
|
|
|
delete edge_element;
|
|
|
|
// build svert_lvert
|
|
svert_lvert.SetSize(svert_counter);
|
|
|
|
svert_counter = 0;
|
|
for (i = 0; i < vert_element->Size(); i++)
|
|
if (vert_element->GetRow(i)[0] >= 0)
|
|
svert_lvert[svert_counter++] = vert_global_local[i];
|
|
|
|
delete vert_element;
|
|
|
|
// build group_lproc, group_mgroupandproc and lproc_proc
|
|
groups.AsTable(group_lproc); // group_lproc = group_proc
|
|
|
|
Table group_mgroupandproc;
|
|
group_mgroupandproc.SetDims(group_lproc.Size(),
|
|
group_lproc.Size_of_connections() +
|
|
group_lproc.Size());
|
|
for (i = 0; i < group_mgroupandproc.Size(); i++)
|
|
{
|
|
j = group_mgroupandproc.GetI()[i];
|
|
group_mgroupandproc.GetI()[i+1] = j + group_lproc.RowSize(i) + 1;
|
|
group_mgroupandproc.GetJ()[j] = i;
|
|
j++;
|
|
for (int k = group_lproc.GetI()[i];
|
|
j < group_mgroupandproc.GetI()[i+1]; j++, k++)
|
|
group_mgroupandproc.GetJ()[j] = group_lproc.GetJ()[k];
|
|
}
|
|
|
|
Array<int> proc_lproc(NRanks); // array of size number of processors!
|
|
proc_lproc = -1;
|
|
|
|
int lproc_counter = 0;
|
|
for (i = 0; i < group_lproc.Size_of_connections(); i++)
|
|
if (proc_lproc[group_lproc.GetJ()[i]] < 0)
|
|
proc_lproc[group_lproc.GetJ()[i]] = lproc_counter++;
|
|
|
|
lproc_proc.SetSize(lproc_counter);
|
|
for (i = 0; i < NRanks; i++)
|
|
if (proc_lproc[i] >= 0)
|
|
lproc_proc[proc_lproc[i]] = i;
|
|
|
|
for (i = 0; i < group_lproc.Size_of_connections(); i++)
|
|
group_lproc.GetJ()[i] = proc_lproc[group_lproc.GetJ()[i]];
|
|
|
|
// build groupmaster_lproc
|
|
groupmaster_lproc.SetSize(groups.Size());
|
|
|
|
// simplest choice of the group owner
|
|
for (i = 0; i < groups.Size(); i++)
|
|
groupmaster_lproc[i] = proc_lproc[groups.PickElementInSet(i)];
|
|
|
|
// load-balanced choice of the group owner, which however can lead to
|
|
// isolated dofs
|
|
// for (i = 0; i < groups.Size(); i++)
|
|
// groupmaster_lproc[i] = proc_lproc[groups.PickRandomElementInSet(i)];
|
|
proc_lproc.DeleteAll();
|
|
|
|
// build group_mgroup
|
|
group_mgroup.SetSize(groups.Size());
|
|
|
|
int send_counter = 0;
|
|
int recv_counter = 0;
|
|
for (i = 1; i < groups.Size(); i++)
|
|
if (groupmaster_lproc[i] != 0) // we are not the master
|
|
recv_counter++;
|
|
else
|
|
send_counter += group_lproc.RowSize(i)-1;
|
|
|
|
MPI_Request *requests = new MPI_Request[send_counter];
|
|
MPI_Status *statuses = new MPI_Status[send_counter];
|
|
|
|
int max_recv_size = 0;
|
|
send_counter = 0;
|
|
for (i = 1; i < groups.Size(); i++)
|
|
{
|
|
if (groupmaster_lproc[i] == 0) // we are the master
|
|
{
|
|
group_mgroup[i] = i;
|
|
|
|
for (j = group_lproc.GetI()[i];
|
|
j < group_lproc.GetI()[i+1]; j++)
|
|
{
|
|
if (group_lproc.GetJ()[j] != 0)
|
|
{
|
|
MPI_Isend(group_mgroupandproc.GetRow (i),
|
|
group_mgroupandproc.RowSize (i),
|
|
MPI_INT,
|
|
lproc_proc[group_lproc.GetJ()[j]],
|
|
822,
|
|
MyComm,
|
|
&requests[send_counter]);
|
|
send_counter++;
|
|
}
|
|
}
|
|
}
|
|
else // we are not the master
|
|
if (max_recv_size < group_lproc.RowSize(i))
|
|
max_recv_size = group_lproc.RowSize(i);
|
|
}
|
|
max_recv_size++;
|
|
|
|
if (recv_counter > 0)
|
|
{
|
|
int count;
|
|
MPI_Status status;
|
|
int *recv_buf = new int[max_recv_size];
|
|
for ( ; recv_counter > 0; recv_counter--)
|
|
{
|
|
MPI_Recv(recv_buf, max_recv_size, MPI_INT,
|
|
MPI_ANY_SOURCE, 822, MyComm, &status);
|
|
|
|
MPI_Get_count(&status, MPI_INT, &count);
|
|
|
|
group.Recreate(count-1, recv_buf+1);
|
|
group_mgroup[i=groups.Lookup(group)] = recv_buf[0];
|
|
|
|
if (lproc_proc[groupmaster_lproc[i]] != status.MPI_SOURCE)
|
|
{
|
|
cerr << "\n\n\nParMesh::ParMesh: " << MyRank
|
|
<< ": ERROR\n\n\n" << endl;
|
|
mfem_error();
|
|
}
|
|
}
|
|
delete [] recv_buf;
|
|
}
|
|
|
|
MPI_Waitall(send_counter, requests, statuses);
|
|
|
|
delete [] statuses;
|
|
delete [] requests;
|
|
|
|
if (mesh.GetNodes()) // curved mesh
|
|
{
|
|
Nodes = new ParGridFunction(this, mesh.GetNodes());
|
|
own_nodes = 1;
|
|
|
|
Array<int> gvdofs, lvdofs;
|
|
Vector lnodes;
|
|
element_counter = 0;
|
|
for (i = 0; i < mesh.GetNE(); i++)
|
|
if (partitioning[i] == MyRank)
|
|
{
|
|
Nodes->FESpace()->GetElementVDofs(element_counter, lvdofs);
|
|
mesh.GetNodes()->FESpace()->GetElementVDofs(i, gvdofs);
|
|
mesh.GetNodes()->GetSubVector(gvdofs, lnodes);
|
|
Nodes->SetSubVector(lvdofs, lnodes);
|
|
element_counter++;
|
|
}
|
|
}
|
|
|
|
if (partitioning_ == NULL)
|
|
delete [] partitioning;
|
|
}
|
|
|
|
void ParMesh::GroupEdge(int group, int i, int &edge, int &o)
|
|
{
|
|
int sedge = group_sedge.GetJ()[group_sedge.GetI()[group-1]+i];
|
|
edge = sedge_ledge[sedge];
|
|
int *v = shared_edges[sedge]->GetVertices();
|
|
o = (v[0] < v[1]) ? (+1) : (-1);
|
|
}
|
|
|
|
void ParMesh::GroupFace(int group, int i, int &face, int &o)
|
|
{
|
|
int sface = group_sface.GetJ()[group_sface.GetI()[group-1]+i];
|
|
face = sface_lface[sface];
|
|
// face gives the base orientation
|
|
if (faces[face]->GetType() == Element::TRIANGLE)
|
|
o = GetTriOrientation(faces[face]->GetVertices(),
|
|
shared_faces[sface]->GetVertices());
|
|
if (faces[face]->GetType() == Element::QUADRILATERAL)
|
|
o = GetQuadOrientation(faces[face]->GetVertices(),
|
|
shared_faces[sface]->GetVertices());
|
|
}
|
|
|
|
|
|
// For a line segment with vertices v[0] and v[1], return a number with
|
|
// the following meaning:
|
|
// 0 - the edge was not refined
|
|
// 1 - the edge e was refined once by splitting v[0],v[1]
|
|
int ParMesh::GetEdgeSplittings(Element *edge, const DSTable &v_to_v,
|
|
int *middle)
|
|
{
|
|
int m, *v = edge->GetVertices();
|
|
|
|
if ((m = v_to_v(v[0], v[1])) != -1 && middle[m] != -1)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
// For a triangular face with (correctly ordered) vertices v[0], v[1], v[2]
|
|
// return a number with the following meaning:
|
|
// 0 - the face was not refined
|
|
// 1 - the face was refined once by splitting v[0],v[1]
|
|
// 2 - the face was refined twice by splitting v[0],v[1] and then v[1],v[2]
|
|
// 3 - the face was refined twice by splitting v[0],v[1] and then v[0],v[2]
|
|
// 4 - the face was refined three times (as in 2+3)
|
|
int ParMesh::GetFaceSplittings(Element *face, const DSTable &v_to_v,
|
|
int *middle)
|
|
{
|
|
int m, right = 0;
|
|
int number_of_splittings = 0;
|
|
int *v = face->GetVertices();
|
|
|
|
if ((m = v_to_v(v[0], v[1])) != -1 && middle[m] != -1)
|
|
{
|
|
number_of_splittings++;
|
|
if ((m = v_to_v(v[1], v[2])) != -1 && middle[m] != -1)
|
|
{
|
|
right = 1;
|
|
number_of_splittings++;
|
|
}
|
|
if ((m = v_to_v(v[2], v[0])) != -1 && middle[m] != -1)
|
|
number_of_splittings++;
|
|
|
|
switch (number_of_splittings)
|
|
{
|
|
case 2:
|
|
if (right == 0)
|
|
number_of_splittings++;
|
|
break;
|
|
case 3:
|
|
number_of_splittings++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return number_of_splittings;
|
|
}
|
|
|
|
void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
|
|
{
|
|
int i, j, wtls = WantTwoLevelState;
|
|
|
|
if (Nodes) // curved mesh
|
|
{
|
|
UseTwoLevelState(1);
|
|
}
|
|
|
|
SetState(Mesh::NORMAL);
|
|
DeleteCoarseTables();
|
|
|
|
if (Dim == 3)
|
|
{
|
|
if (WantTwoLevelState)
|
|
{
|
|
c_NumOfVertices = NumOfVertices;
|
|
c_NumOfEdges = NumOfEdges;
|
|
c_NumOfFaces = NumOfFaces;
|
|
c_NumOfElements = NumOfElements;
|
|
c_NumOfBdrElements = NumOfBdrElements;
|
|
}
|
|
|
|
int uniform_refinement = 0;
|
|
if (type < 0)
|
|
{
|
|
type = -type;
|
|
uniform_refinement = 1;
|
|
}
|
|
|
|
// 1. Get table of vertex to vertex connections.
|
|
DSTable v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(v_to_v);
|
|
|
|
// 2. Get edge to element connections in arrays edge1 and edge2
|
|
Array<int> middle(v_to_v.NumberOfEntries());
|
|
middle = -1;
|
|
|
|
// 3. Do the red refinement.
|
|
switch (type)
|
|
{
|
|
case 1:
|
|
for (i = 0; i < marked_el.Size(); i++)
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
break;
|
|
case 2:
|
|
for (i = 0; i < marked_el.Size(); i++)
|
|
{
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
|
|
Bisection(NumOfElements - 1, v_to_v, NULL, NULL, middle);
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
}
|
|
break;
|
|
case 3:
|
|
for (i = 0; i < marked_el.Size(); i++)
|
|
{
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
|
|
j = NumOfElements - 1;
|
|
Bisection(j, v_to_v, NULL, NULL, middle);
|
|
Bisection(NumOfElements - 1, v_to_v, NULL, NULL, middle);
|
|
Bisection(j, v_to_v, NULL, NULL, middle);
|
|
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
Bisection(NumOfElements-1, v_to_v, NULL, NULL, middle);
|
|
Bisection(marked_el[i], v_to_v, NULL, NULL, middle);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (WantTwoLevelState)
|
|
{
|
|
RefinedElement::State = RefinedElement::FINE;
|
|
State = Mesh::TWO_LEVEL_FINE;
|
|
}
|
|
|
|
// 4. Do the green refinement (to get conforming mesh).
|
|
int need_refinement;
|
|
int refined_edge[5][3] = {{0, 0, 0},
|
|
{1, 0, 0},
|
|
{1, 1, 0},
|
|
{1, 0, 1},
|
|
{1, 1, 1}};
|
|
int faces_in_group, max_faces_in_group = 0;
|
|
// face_splittings identify how the shared faces have been split
|
|
int **face_splittings = new int*[GetNGroups()-1];
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
faces_in_group = GroupNFaces(i+1);
|
|
face_splittings[i] = new int[faces_in_group];
|
|
if (faces_in_group > max_faces_in_group)
|
|
max_faces_in_group = faces_in_group;
|
|
}
|
|
int neighbor, *iBuf = new int[max_faces_in_group];
|
|
|
|
Array<int> group_faces;
|
|
Vertex V;
|
|
|
|
MPI_Request request;
|
|
MPI_Status status;
|
|
|
|
#ifdef MFEM_DEBUG
|
|
int ref_loops_all = 0, ref_loops_par = 0;
|
|
#endif
|
|
do
|
|
{
|
|
need_refinement = 0;
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
if (elements[i]->NeedRefinement(v_to_v, middle))
|
|
{
|
|
need_refinement = 1;
|
|
Bisection(i, v_to_v, NULL, NULL, middle);
|
|
}
|
|
}
|
|
#ifdef MFEM_DEBUG
|
|
ref_loops_all++;
|
|
#endif
|
|
|
|
if (uniform_refinement)
|
|
continue;
|
|
|
|
// if the mesh is locally conforming start making it globally
|
|
// conforming
|
|
if (need_refinement == 0)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
ref_loops_par++;
|
|
#endif
|
|
// MPI_Barrier(MyComm);
|
|
|
|
// (a) send the type of interface splitting
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
group_sface.GetRow(i, group_faces);
|
|
faces_in_group = group_faces.Size();
|
|
// it is enough to communicate through the faces
|
|
if (faces_in_group != 0)
|
|
{
|
|
for (j = 0; j < faces_in_group; j++)
|
|
face_splittings[i][j] =
|
|
GetFaceSplittings(shared_faces[group_faces[j]], v_to_v,
|
|
middle);
|
|
j = group_lproc.GetI()[i+1];
|
|
if (group_lproc.GetJ()[j] == 0)
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j+1]];
|
|
else
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j]];
|
|
MPI_Isend(face_splittings[i], faces_in_group, MPI_INT,
|
|
neighbor, 0, MyComm, &request);
|
|
}
|
|
}
|
|
|
|
// (b) receive the type of interface splitting
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
group_sface.GetRow(i, group_faces);
|
|
faces_in_group = group_faces.Size();
|
|
if (faces_in_group != 0)
|
|
{
|
|
j = group_lproc.GetI()[i+1];
|
|
if (group_lproc.GetJ()[j] == 0)
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j+1]];
|
|
else
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j]];
|
|
MPI_Recv(iBuf, faces_in_group, MPI_INT, neighbor,
|
|
MPI_ANY_TAG, MyComm, &status);
|
|
|
|
for (j = 0; j < faces_in_group; j++)
|
|
if (iBuf[j] != face_splittings[i][j])
|
|
{
|
|
int *v = shared_faces[group_faces[j]]->GetVertices();
|
|
for (int k = 0; k < 3; k++)
|
|
if (refined_edge[iBuf[j]][k] == 1 &&
|
|
refined_edge[face_splittings[i][j]][k] == 0)
|
|
{
|
|
int ii = v_to_v(v[k], v[(k+1)%3]);
|
|
if (middle[ii] == -1)
|
|
{
|
|
need_refinement = 1;
|
|
middle[ii] = NumOfVertices++;
|
|
for (int c = 0; c < 3; c++)
|
|
V(c) = 0.5 * (vertices[v[k]](c) +
|
|
vertices[v[(k+1)%3]](c));
|
|
vertices.Append(V);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
i = need_refinement;
|
|
MPI_Allreduce(&i, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
|
|
}
|
|
}
|
|
while (need_refinement == 1);
|
|
|
|
#ifdef MFEM_DEBUG
|
|
i = ref_loops_all;
|
|
MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
|
|
if (MyRank == 0)
|
|
{
|
|
cout << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
|
|
<< ref_loops_all << ", ref_loops_par = " << ref_loops_par
|
|
<< '\n' << endl;
|
|
}
|
|
#endif
|
|
|
|
delete [] iBuf;
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
delete [] face_splittings[i];
|
|
delete [] face_splittings;
|
|
|
|
|
|
// 5. Update the boundary elements.
|
|
do
|
|
{
|
|
need_refinement = 0;
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
if (boundary[i]->NeedRefinement(v_to_v, middle))
|
|
{
|
|
need_refinement = 1;
|
|
Bisection(i, v_to_v, middle);
|
|
}
|
|
}
|
|
while (need_refinement == 1);
|
|
|
|
if (NumOfBdrElements != boundary.Size())
|
|
mfem_error("ParMesh::LocalRefinement :"
|
|
" (NumOfBdrElements != boundary.Size())");
|
|
|
|
// 5a. Update the groups after refinement.
|
|
if (el_to_face != NULL)
|
|
{
|
|
if (WantTwoLevelState)
|
|
{
|
|
c_el_to_face = el_to_face;
|
|
el_to_face = NULL;
|
|
Swap(faces_info, fc_faces_info);
|
|
}
|
|
RefineGroups(v_to_v, middle);
|
|
// GetElementToFaceTable(); // Called by RefineGroups
|
|
GenerateFaces();
|
|
if (WantTwoLevelState)
|
|
{
|
|
f_el_to_face = el_to_face;
|
|
}
|
|
}
|
|
|
|
// 6. Un-mark the Pf elements.
|
|
int refinement_edges[2], type, flag;
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
Element *El = elements[i];
|
|
while (El->GetType() == Element::BISECTED)
|
|
El = ((BisectedElement *) El)->FirstChild;
|
|
((Tetrahedron *) El)->ParseRefinementFlag(refinement_edges,
|
|
type, flag);
|
|
if (type == Tetrahedron::TYPE_PF)
|
|
((Tetrahedron *) El)->CreateRefinementFlag(refinement_edges,
|
|
Tetrahedron::TYPE_PU,
|
|
flag);
|
|
}
|
|
|
|
// 7. Free the allocated memory.
|
|
middle.DeleteAll();
|
|
|
|
#ifdef MFEM_DEBUG
|
|
CheckElementOrientation();
|
|
#endif
|
|
|
|
if (el_to_edge != NULL)
|
|
{
|
|
if (WantTwoLevelState)
|
|
{
|
|
c_el_to_edge = el_to_edge;
|
|
f_el_to_edge = new Table;
|
|
c_bel_to_edge = bel_to_edge;
|
|
bel_to_edge = NULL;
|
|
NumOfEdges = GetElementToEdgeTable(*f_el_to_edge, be_to_edge);
|
|
el_to_edge = f_el_to_edge;
|
|
f_bel_to_edge = bel_to_edge;
|
|
}
|
|
else
|
|
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
|
}
|
|
|
|
if (WantTwoLevelState)
|
|
{
|
|
f_NumOfVertices = NumOfVertices;
|
|
f_NumOfEdges = NumOfEdges;
|
|
f_NumOfFaces = NumOfFaces;
|
|
f_NumOfElements = NumOfElements;
|
|
f_NumOfBdrElements = NumOfBdrElements;
|
|
}
|
|
} // 'if (Dim == 3)'
|
|
|
|
|
|
if (Dim == 2)
|
|
{
|
|
if (WantTwoLevelState)
|
|
{
|
|
c_NumOfVertices = NumOfVertices;
|
|
c_NumOfEdges = NumOfEdges;
|
|
c_NumOfElements = NumOfElements;
|
|
c_NumOfBdrElements = NumOfBdrElements;
|
|
}
|
|
|
|
int uniform_refinement = 0;
|
|
if (type < 0)
|
|
{
|
|
type = -type;
|
|
uniform_refinement = 1;
|
|
}
|
|
|
|
// 1. Get table of vertex to vertex connections.
|
|
DSTable v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(v_to_v);
|
|
|
|
// 2. Get edge to element connections in arrays edge1 and edge2
|
|
int nedges = v_to_v.NumberOfEntries();
|
|
int *edge1 = new int[nedges];
|
|
int *edge2 = new int[nedges];
|
|
int *middle = new int[nedges];
|
|
|
|
for (i = 0; i < nedges; i++)
|
|
edge1[i] = edge2[i] = middle[i] = -1;
|
|
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
int *v = elements[i]->GetVertices();
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
int ind = v_to_v(v[j], v[(j+1)%3]);
|
|
(edge1[ind] == -1) ? (edge1[ind] = i) : (edge2[ind] = i);
|
|
}
|
|
}
|
|
|
|
// 3. Do the red refinement.
|
|
for (i = 0; i < marked_el.Size(); i++)
|
|
RedRefinement(marked_el[i], v_to_v, edge1, edge2, middle);
|
|
|
|
if (WantTwoLevelState)
|
|
{
|
|
RefinedElement::State = RefinedElement::FINE;
|
|
State = Mesh::TWO_LEVEL_FINE;
|
|
}
|
|
|
|
// 4. Do the green refinement (to get conforming mesh).
|
|
int need_refinement;
|
|
int edges_in_group, max_edges_in_group = 0;
|
|
// edge_splittings identify how the shared edges have been split
|
|
int **edge_splittings = new int*[GetNGroups()-1];
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
edges_in_group = GroupNEdges(i+1);
|
|
edge_splittings[i] = new int[edges_in_group];
|
|
if (edges_in_group > max_edges_in_group)
|
|
max_edges_in_group = edges_in_group;
|
|
}
|
|
int neighbor, *iBuf = new int[max_edges_in_group];
|
|
|
|
Array<int> group_edges;
|
|
|
|
MPI_Request request;
|
|
MPI_Status status;
|
|
Vertex V;
|
|
V(2) = 0.0;
|
|
|
|
#ifdef MFEM_DEBUG
|
|
int ref_loops_all = 0, ref_loops_par = 0;
|
|
#endif
|
|
do
|
|
{
|
|
need_refinement = 0;
|
|
for (i = 0; i < nedges; i++)
|
|
if (middle[i] != -1 && edge1[i] != -1)
|
|
{
|
|
need_refinement = 1;
|
|
GreenRefinement(edge1[i], v_to_v, edge1, edge2, middle);
|
|
}
|
|
#ifdef MFEM_DEBUG
|
|
ref_loops_all++;
|
|
#endif
|
|
|
|
if (uniform_refinement)
|
|
continue;
|
|
|
|
// if the mesh is locally conforming start making it globally
|
|
// conforming
|
|
if (need_refinement == 0)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
ref_loops_par++;
|
|
#endif
|
|
// MPI_Barrier(MyComm);
|
|
|
|
// (a) send the type of interface splitting
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
group_sedge.GetRow(i, group_edges);
|
|
edges_in_group = group_edges.Size();
|
|
// it is enough to communicate through the edges
|
|
if (edges_in_group != 0)
|
|
{
|
|
for (j = 0; j < edges_in_group; j++)
|
|
edge_splittings[i][j] =
|
|
GetEdgeSplittings(shared_edges[group_edges[j]], v_to_v,
|
|
middle);
|
|
j = group_lproc.GetI()[i+1];
|
|
if (group_lproc.GetJ()[j] == 0)
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j+1]];
|
|
else
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j]];
|
|
MPI_Isend(edge_splittings[i], edges_in_group, MPI_INT,
|
|
neighbor, 0, MyComm, &request);
|
|
}
|
|
}
|
|
|
|
// (b) receive the type of interface splitting
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
{
|
|
group_sedge.GetRow(i, group_edges);
|
|
edges_in_group = group_edges.Size();
|
|
if (edges_in_group != 0)
|
|
{
|
|
j = group_lproc.GetI()[i+1];
|
|
if (group_lproc.GetJ()[j] == 0)
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j+1]];
|
|
else
|
|
neighbor = lproc_proc[group_lproc.GetJ()[j]];
|
|
MPI_Recv(iBuf, edges_in_group, MPI_INT, neighbor,
|
|
MPI_ANY_TAG, MyComm, &status);
|
|
|
|
for (j = 0; j < edges_in_group; j++)
|
|
if (iBuf[j] == 1 && edge_splittings[i][j] == 0)
|
|
{
|
|
int *v = shared_edges[group_edges[j]]->GetVertices();
|
|
int ii = v_to_v(v[0], v[1]);
|
|
#ifdef MFEM_DEBUG
|
|
if (middle[ii] != -1)
|
|
mfem_error("ParMesh::LocalRefinement (triangles) : "
|
|
"Oops!");
|
|
#endif
|
|
need_refinement = 1;
|
|
middle[ii] = NumOfVertices++;
|
|
for (int c = 0; c < 2; c++)
|
|
V(c) = 0.5 * (vertices[v[0]](c) + vertices[v[1]](c));
|
|
vertices.Append(V);
|
|
}
|
|
}
|
|
}
|
|
|
|
i = need_refinement;
|
|
MPI_Allreduce(&i, &need_refinement, 1, MPI_INT, MPI_LOR, MyComm);
|
|
}
|
|
}
|
|
while (need_refinement == 1);
|
|
|
|
#ifdef MFEM_DEBUG
|
|
i = ref_loops_all;
|
|
MPI_Reduce(&i, &ref_loops_all, 1, MPI_INT, MPI_MAX, 0, MyComm);
|
|
if (MyRank == 0)
|
|
{
|
|
cout << "\n\nParMesh::LocalRefinement : max. ref_loops_all = "
|
|
<< ref_loops_all << ", ref_loops_par = " << ref_loops_par
|
|
<< '\n' << endl;
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < GetNGroups()-1; i++)
|
|
delete [] edge_splittings[i];
|
|
delete [] edge_splittings;
|
|
|
|
delete [] iBuf;
|
|
|
|
// 5. Update the boundary elements.
|
|
int v1[2], v2[2], bisect, temp;
|
|
temp = NumOfBdrElements;
|
|
for (i = 0; i < temp; i++)
|
|
{
|
|
int *v = boundary[i]->GetVertices();
|
|
bisect = v_to_v(v[0], v[1]);
|
|
if (middle[bisect] != -1)
|
|
{ // the element was refined (needs updating)
|
|
if (boundary[i]->GetType() == Element::SEGMENT)
|
|
{
|
|
v1[0] = v[0]; v1[1] = middle[bisect];
|
|
v2[0] = middle[bisect]; v2[1] = v[1];
|
|
|
|
if (WantTwoLevelState)
|
|
{
|
|
boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
|
|
#ifdef MFEM_USE_MEMALLOC
|
|
BisectedElement *aux = BEMemory.Alloc();
|
|
aux->SetCoarseElem(boundary[i]);
|
|
#else
|
|
BisectedElement *aux = new BisectedElement(boundary[i]);
|
|
#endif
|
|
aux->FirstChild =
|
|
new Segment(v1, boundary[i]->GetAttribute());
|
|
aux->SecondChild = NumOfBdrElements;
|
|
boundary[i] = aux;
|
|
NumOfBdrElements++;
|
|
}
|
|
else
|
|
{
|
|
boundary[i]->SetVertices(v1);
|
|
boundary.Append(new Segment(v2, boundary[i]->GetAttribute()));
|
|
}
|
|
}
|
|
else
|
|
mfem_error("Only bisection of segment is implemented for bdr"
|
|
" elem.");
|
|
}
|
|
}
|
|
NumOfBdrElements = boundary.Size();
|
|
|
|
// 5a. Update the groups after refinement.
|
|
RefineGroups(v_to_v, middle);
|
|
|
|
// 6. Free the allocated memory.
|
|
delete [] edge1;
|
|
delete [] edge2;
|
|
delete [] middle;
|
|
|
|
#ifdef MFEM_DEBUG
|
|
CheckElementOrientation();
|
|
#endif
|
|
|
|
if (WantTwoLevelState)
|
|
{
|
|
f_NumOfVertices = NumOfVertices;
|
|
f_NumOfElements = NumOfElements;
|
|
f_NumOfBdrElements = NumOfBdrElements;
|
|
RefinedElement::State = RefinedElement::FINE;
|
|
State = Mesh::TWO_LEVEL_FINE;
|
|
}
|
|
|
|
if (el_to_edge != NULL)
|
|
{
|
|
if (WantTwoLevelState)
|
|
{
|
|
c_el_to_edge = el_to_edge;
|
|
Swap(be_to_edge, fc_be_to_edge); // save coarse be_to_edge
|
|
f_el_to_edge = new Table;
|
|
NumOfEdges = GetElementToEdgeTable(*f_el_to_edge, be_to_edge);
|
|
el_to_edge = f_el_to_edge;
|
|
f_NumOfEdges = NumOfEdges;
|
|
}
|
|
else
|
|
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
|
GenerateFaces();
|
|
}
|
|
} // 'if (Dim == 2)'
|
|
|
|
if (Nodes) // curved mesh
|
|
{
|
|
UpdateNodes();
|
|
UseTwoLevelState(wtls);
|
|
}
|
|
}
|
|
|
|
void ParMesh::RefineGroups(const DSTable &v_to_v, int *middle)
|
|
{
|
|
int i, attr, newv[3], ind, f_ind, *v;
|
|
|
|
int group;
|
|
Array<int> group_verts, group_edges, group_faces;
|
|
|
|
// To update the groups after a refinement, we observe that:
|
|
// - every (new and old) vertex, edge and face belongs to exactly one group
|
|
// - the refinement does not create new groups
|
|
// - a new vertex appears only as the middle of a refined edge
|
|
// - a face can be refined 2, 3 or 4 times producing new edges and faces
|
|
|
|
int *I_group_svert, *J_group_svert;
|
|
int *I_group_sedge, *J_group_sedge;
|
|
int *I_group_sface, *J_group_sface;
|
|
|
|
I_group_svert = new int[GetNGroups()+1];
|
|
I_group_sedge = new int[GetNGroups()+1];
|
|
if (Dim == 3)
|
|
I_group_sface = new int[GetNGroups()+1];
|
|
|
|
I_group_svert[0] = I_group_svert[1] = 0;
|
|
I_group_sedge[0] = I_group_sedge[1] = 0;
|
|
if (Dim == 3)
|
|
I_group_sface[0] = I_group_sface[1] = 0;
|
|
|
|
// overestimate the size of the J arrays
|
|
if (Dim == 3)
|
|
{
|
|
J_group_svert = new int[group_svert.Size_of_connections()
|
|
+ group_sedge.Size_of_connections()];
|
|
J_group_sedge = new int[2*group_sedge.Size_of_connections()
|
|
+ 3*group_sface.Size_of_connections()];
|
|
J_group_sface = new int[4*group_sface.Size_of_connections()];
|
|
}
|
|
else if (Dim == 2)
|
|
{
|
|
J_group_svert = new int[group_svert.Size_of_connections()
|
|
+ group_sedge.Size_of_connections()];
|
|
J_group_sedge = new int[2*group_sedge.Size_of_connections()];
|
|
}
|
|
|
|
for (group = 0; group < GetNGroups()-1; group++)
|
|
{
|
|
// Get the group shared objects
|
|
group_svert.GetRow(group, group_verts);
|
|
group_sedge.GetRow(group, group_edges);
|
|
group_sface.GetRow(group, group_faces);
|
|
|
|
// Check which edges have been refined
|
|
for (i = 0; i < group_sedge.RowSize(group); i++)
|
|
{
|
|
v = shared_edges[group_edges[i]]->GetVertices();
|
|
ind = middle[v_to_v(v[0], v[1])];
|
|
if (ind != -1)
|
|
{
|
|
// add a vertex
|
|
group_verts.Append(svert_lvert.Append(ind)-1);
|
|
// update the edges
|
|
attr = shared_edges[group_edges[i]]->GetAttribute();
|
|
shared_edges.Append(new Segment(v[1], ind, attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
v[1] = ind;
|
|
}
|
|
}
|
|
|
|
// Check which faces have been refined
|
|
for (i = 0; i < group_sface.RowSize(group); i++)
|
|
{
|
|
v = shared_faces[group_faces[i]]->GetVertices();
|
|
ind = middle[v_to_v(v[0], v[1])];
|
|
if (ind != -1)
|
|
{
|
|
attr = shared_faces[group_faces[i]]->GetAttribute();
|
|
// add the refinement edge
|
|
shared_edges.Append(new Segment(v[2], ind, attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
// add a face
|
|
f_ind = group_faces.Size();
|
|
shared_faces.Append(new Triangle(v[1], v[2], ind, attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
newv[0] = v[2]; newv[1] = v[0]; newv[2] = ind;
|
|
shared_faces[group_faces[i]]->SetVertices(newv);
|
|
|
|
// check if the left face has also been refined
|
|
// v = shared_faces[group_faces[i]]->GetVertices();
|
|
ind = middle[v_to_v(v[0], v[1])];
|
|
if (ind != -1)
|
|
{
|
|
// add the refinement edge
|
|
shared_edges.Append(new Segment(v[2], ind, attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
// add a face
|
|
shared_faces.Append(new Triangle(v[1], v[2], ind, attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
newv[0] = v[2]; newv[1] = v[0]; newv[2] = ind;
|
|
shared_faces[group_faces[i]]->SetVertices(newv);
|
|
}
|
|
|
|
// check if the right face has also been refined
|
|
v = shared_faces[group_faces[f_ind]]->GetVertices();
|
|
ind = middle[v_to_v(v[0], v[1])];
|
|
if (ind != -1)
|
|
{
|
|
// add the refinement edge
|
|
shared_edges.Append(new Segment(v[2], ind, attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
// add a face
|
|
shared_faces.Append(new Triangle(v[1], v[2], ind, attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
newv[0] = v[2]; newv[1] = v[0]; newv[2] = ind;
|
|
shared_faces[group_faces[f_ind]]->SetVertices(newv);
|
|
}
|
|
}
|
|
}
|
|
|
|
I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
|
|
I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
|
|
if (Dim == 3)
|
|
I_group_sface[group+1] = I_group_sface[group] + group_faces.Size();
|
|
|
|
int *J;
|
|
J = J_group_svert+I_group_svert[group];
|
|
for (i = 0; i < group_verts.Size(); i++)
|
|
J[i] = group_verts[i];
|
|
J = J_group_sedge+I_group_sedge[group];
|
|
for (i = 0; i < group_edges.Size(); i++)
|
|
J[i] = group_edges[i];
|
|
if (Dim == 3)
|
|
{
|
|
J = J_group_sface+I_group_sface[group];
|
|
for (i = 0; i < group_faces.Size(); i++)
|
|
J[i] = group_faces[i];
|
|
}
|
|
}
|
|
|
|
// Fix the local numbers of shared edges and faces
|
|
{
|
|
DSTable new_v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(new_v_to_v);
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
{
|
|
v = shared_edges[i]->GetVertices();
|
|
sedge_ledge[i] = new_v_to_v(v[0], v[1]);
|
|
}
|
|
}
|
|
if (Dim == 3)
|
|
{
|
|
STable3D *faces_tbl = GetElementToFaceTable(1);
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
v = shared_faces[i]->GetVertices();
|
|
sface_lface[i] = (*faces_tbl)(v[0], v[1], v[2]);
|
|
}
|
|
delete faces_tbl;
|
|
}
|
|
|
|
group_svert.SetIJ(I_group_svert, J_group_svert);
|
|
group_sedge.SetIJ(I_group_sedge, J_group_sedge);
|
|
if (Dim == 3)
|
|
group_sface.SetIJ(I_group_sface, J_group_sface);
|
|
}
|
|
|
|
void ParMesh::QuadUniformRefinement()
|
|
{
|
|
int oedge = NumOfVertices, wtls = WantTwoLevelState;
|
|
|
|
if (Nodes) // curved mesh
|
|
UseTwoLevelState(1);
|
|
|
|
// call Mesh::QuadUniformRefinement so that it won't update the nodes
|
|
{
|
|
GridFunction *nodes = Nodes;
|
|
Nodes = NULL;
|
|
Mesh::QuadUniformRefinement();
|
|
Nodes = nodes;
|
|
}
|
|
|
|
// update the groups
|
|
{
|
|
int i, attr, ind, *v;
|
|
|
|
int group;
|
|
Array<int> sverts, sedges;
|
|
|
|
int *I_group_svert, *J_group_svert;
|
|
int *I_group_sedge, *J_group_sedge;
|
|
|
|
I_group_svert = new int[GetNGroups()+1];
|
|
I_group_sedge = new int[GetNGroups()+1];
|
|
|
|
I_group_svert[0] = I_group_svert[1] = 0;
|
|
I_group_sedge[0] = I_group_sedge[1] = 0;
|
|
|
|
// compute the size of the J arrays
|
|
J_group_svert = new int[group_svert.Size_of_connections()
|
|
+ group_sedge.Size_of_connections()];
|
|
J_group_sedge = new int[2*group_sedge.Size_of_connections()];
|
|
|
|
for (group = 0; group < GetNGroups()-1; group++)
|
|
{
|
|
// Get the group shared objects
|
|
group_svert.GetRow(group, sverts);
|
|
group_sedge.GetRow(group, sedges);
|
|
|
|
// Process all the edges
|
|
for (i = 0; i < group_sedge.RowSize(group); i++)
|
|
{
|
|
v = shared_edges[sedges[i]]->GetVertices();
|
|
ind = oedge + sedge_ledge[sedges[i]];
|
|
// add a vertex
|
|
sverts.Append(svert_lvert.Append(ind)-1);
|
|
// update the edges
|
|
attr = shared_edges[sedges[i]]->GetAttribute();
|
|
shared_edges.Append(new Segment(v[1], ind, attr));
|
|
sedges.Append(sedge_ledge.Append(-1)-1);
|
|
v[1] = ind;
|
|
}
|
|
|
|
I_group_svert[group+1] = I_group_svert[group] + sverts.Size();
|
|
I_group_sedge[group+1] = I_group_sedge[group] + sedges.Size();
|
|
|
|
int *J;
|
|
J = J_group_svert+I_group_svert[group];
|
|
for (i = 0; i < sverts.Size(); i++)
|
|
J[i] = sverts[i];
|
|
J = J_group_sedge+I_group_sedge[group];
|
|
for (i = 0; i < sedges.Size(); i++)
|
|
J[i] = sedges[i];
|
|
}
|
|
|
|
// Fix the local numbers of shared edges
|
|
DSTable v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(v_to_v);
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
{
|
|
v = shared_edges[i]->GetVertices();
|
|
sedge_ledge[i] = v_to_v(v[0], v[1]);
|
|
}
|
|
|
|
group_svert.SetIJ(I_group_svert, J_group_svert);
|
|
group_sedge.SetIJ(I_group_sedge, J_group_sedge);
|
|
}
|
|
|
|
if (Nodes) // curved mesh
|
|
{
|
|
UpdateNodes();
|
|
UseTwoLevelState(wtls);
|
|
}
|
|
}
|
|
|
|
void ParMesh::HexUniformRefinement()
|
|
{
|
|
int wtls = WantTwoLevelState;
|
|
int oedge = NumOfVertices;
|
|
int oface = oedge + NumOfEdges;
|
|
|
|
DSTable v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(v_to_v);
|
|
STable3D *faces_tbl = GetElementToFaceTable(1);
|
|
|
|
if (Nodes) // curved mesh
|
|
UseTwoLevelState(1);
|
|
|
|
// call Mesh::HexUniformRefinement so that it won't update the nodes
|
|
{
|
|
GridFunction *nodes = Nodes;
|
|
Nodes = NULL;
|
|
Mesh::HexUniformRefinement();
|
|
Nodes = nodes;
|
|
}
|
|
|
|
// update the groups
|
|
{
|
|
int i, attr, newv[4], ind, m[5];
|
|
Array<int> v;
|
|
|
|
int group;
|
|
Array<int> group_verts, group_edges, group_faces;
|
|
|
|
int *I_group_svert, *J_group_svert;
|
|
int *I_group_sedge, *J_group_sedge;
|
|
int *I_group_sface, *J_group_sface;
|
|
|
|
I_group_svert = new int[GetNGroups()+1];
|
|
I_group_sedge = new int[GetNGroups()+1];
|
|
I_group_sface = new int[GetNGroups()+1];
|
|
|
|
I_group_svert[0] = I_group_svert[1] = 0;
|
|
I_group_sedge[0] = I_group_sedge[1] = 0;
|
|
I_group_sface[0] = I_group_sface[1] = 0;
|
|
|
|
// compute the size of the J arrays
|
|
J_group_svert = new int[group_svert.Size_of_connections()
|
|
+ group_sedge.Size_of_connections()
|
|
+ group_sface.Size_of_connections()];
|
|
J_group_sedge = new int[2*group_sedge.Size_of_connections()
|
|
+ 4*group_sface.Size_of_connections()];
|
|
J_group_sface = new int[4*group_sface.Size_of_connections()];
|
|
|
|
for (group = 0; group < GetNGroups()-1; group++)
|
|
{
|
|
// Get the group shared objects
|
|
group_svert.GetRow(group, group_verts);
|
|
group_sedge.GetRow(group, group_edges);
|
|
group_sface.GetRow(group, group_faces);
|
|
|
|
// Process the edges that have been refined
|
|
for (i = 0; i < group_sedge.RowSize(group); i++)
|
|
{
|
|
shared_edges[group_edges[i]]->GetVertices(v);
|
|
ind = oedge + v_to_v(v[0], v[1]);
|
|
// add a vertex
|
|
group_verts.Append(svert_lvert.Append(ind)-1);
|
|
// update the edges
|
|
attr = shared_edges[group_edges[i]]->GetAttribute();
|
|
shared_edges.Append(new Segment(v[1], ind, attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
newv[0] = v[0]; newv[1] = ind;
|
|
shared_edges[group_edges[i]]->SetVertices(newv);
|
|
}
|
|
|
|
// Process the faces that have been refined
|
|
for (i = 0; i < group_sface.RowSize(group); i++)
|
|
{
|
|
shared_faces[group_faces[i]]->GetVertices(v);
|
|
m[0] = oface+(*faces_tbl)(v[0], v[1], v[2], v[3]);
|
|
// add a vertex
|
|
group_verts.Append(svert_lvert.Append(m[0])-1);
|
|
// add the refinement edges
|
|
attr = shared_faces[group_faces[i]]->GetAttribute();
|
|
m[1] = oedge + v_to_v(v[0], v[1]);
|
|
m[2] = oedge + v_to_v(v[1], v[2]);
|
|
m[3] = oedge + v_to_v(v[2], v[3]);
|
|
m[4] = oedge + v_to_v(v[3], v[0]);
|
|
shared_edges.Append(new Segment(m[1], m[0], attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
shared_edges.Append(new Segment(m[2], m[0], attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
shared_edges.Append(new Segment(m[3], m[0], attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
shared_edges.Append(new Segment(m[4], m[0], attr));
|
|
group_edges.Append(sedge_ledge.Append(-1)-1);
|
|
// update faces
|
|
newv[0] = v[0]; newv[1] = m[1]; newv[2] = m[0]; newv[3] = m[4];
|
|
shared_faces[group_faces[i]]->SetVertices(newv);
|
|
shared_faces.Append(new Quadrilateral(m[1],v[1],m[2],m[0],attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
shared_faces.Append(new Quadrilateral(m[0],m[2],v[2],m[3],attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
shared_faces.Append(new Quadrilateral(m[4],m[0],m[3],v[3],attr));
|
|
group_faces.Append(sface_lface.Append(-1)-1);
|
|
}
|
|
|
|
I_group_svert[group+1] = I_group_svert[group] + group_verts.Size();
|
|
I_group_sedge[group+1] = I_group_sedge[group] + group_edges.Size();
|
|
I_group_sface[group+1] = I_group_sface[group] + group_faces.Size();
|
|
|
|
int *J;
|
|
J = J_group_svert+I_group_svert[group];
|
|
for (i = 0; i < group_verts.Size(); i++)
|
|
J[i] = group_verts[i];
|
|
J = J_group_sedge+I_group_sedge[group];
|
|
for (i = 0; i < group_edges.Size(); i++)
|
|
J[i] = group_edges[i];
|
|
J = J_group_sface+I_group_sface[group];
|
|
for (i = 0; i < group_faces.Size(); i++)
|
|
J[i] = group_faces[i];
|
|
}
|
|
|
|
// Fix the local numbers of shared edges and faces
|
|
DSTable new_v_to_v(NumOfVertices);
|
|
GetVertexToVertexTable(new_v_to_v);
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
{
|
|
shared_edges[i]->GetVertices(v);
|
|
sedge_ledge[i] = new_v_to_v(v[0], v[1]);
|
|
}
|
|
|
|
delete faces_tbl;
|
|
faces_tbl = GetElementToFaceTable(1);
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
shared_faces[i]->GetVertices(v);
|
|
sface_lface[i] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
|
|
}
|
|
delete faces_tbl;
|
|
|
|
group_svert.SetIJ(I_group_svert, J_group_svert);
|
|
group_sedge.SetIJ(I_group_sedge, J_group_sedge);
|
|
group_sface.SetIJ(I_group_sface, J_group_sface);
|
|
}
|
|
|
|
if (Nodes) // curved mesh
|
|
{
|
|
UpdateNodes();
|
|
UseTwoLevelState(wtls);
|
|
}
|
|
}
|
|
|
|
void ParMesh::Print(ostream &out) const
|
|
{
|
|
if (Dim == 3 && meshgen == 1)
|
|
{
|
|
int i, j, nv;
|
|
const int *ind;
|
|
|
|
out << "NETGEN_Neutral_Format\n";
|
|
// print the vertices
|
|
out << NumOfVertices << '\n';
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << " " << vertices[i](j);
|
|
out << '\n';
|
|
}
|
|
|
|
// print the elements
|
|
out << NumOfElements << '\n';
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
nv = elements[i]->GetNVertices();
|
|
ind = elements[i]->GetVertices();
|
|
out << elements[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
|
|
// print the boundary + shared faces information
|
|
out << NumOfBdrElements + shared_faces.Size() << '\n';
|
|
// boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
nv = boundary[i]->GetNVertices();
|
|
ind = boundary[i]->GetVertices();
|
|
out << boundary[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
// shared faces
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
nv = shared_faces[i]->GetNVertices();
|
|
ind = shared_faces[i]->GetVertices();
|
|
out << shared_faces[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
}
|
|
|
|
if (Dim == 3 && meshgen == 2)
|
|
{
|
|
int i, j, nv;
|
|
const int *ind;
|
|
|
|
out << "TrueGrid\n"
|
|
<< "1 " << NumOfVertices << " " << NumOfElements << " 0 0 0 0 0 0 0\n"
|
|
<< "0 0 0 1 0 0 0 0 0 0 0\n"
|
|
<< "0 0 " << NumOfBdrElements+shared_faces.Size()
|
|
<< " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
|
|
<< "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
|
|
<< "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
|
|
|
|
// print the vertices
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
out << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
|
|
<< " " << vertices[i](2) << " 0.0\n";
|
|
|
|
// print the elements
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
nv = elements[i]->GetNVertices();
|
|
ind = elements[i]->GetVertices();
|
|
out << i+1 << " " << elements[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
|
|
// print the boundary information
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
nv = boundary[i]->GetNVertices();
|
|
ind = boundary[i]->GetVertices();
|
|
out << boundary[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << " 1.0 1.0 1.0 1.0\n";
|
|
}
|
|
|
|
// print the shared faces information
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
nv = shared_faces[i]->GetNVertices();
|
|
ind = shared_faces[i]->GetVertices();
|
|
out << shared_faces[i]->GetAttribute();
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << " 1.0 1.0 1.0 1.0\n";
|
|
}
|
|
}
|
|
|
|
if (Dim == 2)
|
|
{
|
|
int i, j, attr;
|
|
Array<int> v;
|
|
|
|
out << "areamesh2\n\n";
|
|
|
|
// print the boundary + shared edges information
|
|
out << NumOfBdrElements + shared_edges.Size() << '\n';
|
|
// boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
attr = boundary[i]->GetAttribute();
|
|
boundary[i]->GetVertices(v);
|
|
out << attr << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
// shared edges
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
{
|
|
attr = shared_edges[i]->GetAttribute();
|
|
shared_edges[i]->GetVertices(v);
|
|
out << attr << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
|
|
// print the elements
|
|
out << NumOfElements << '\n';
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
attr = elements[i]->GetAttribute();
|
|
elements[i]->GetVertices(v);
|
|
|
|
out << attr << " ";
|
|
if ((j = GetElementType(i)) == Element::TRIANGLE)
|
|
out << 3 << " ";
|
|
else
|
|
if (j == Element::QUADRILATERAL)
|
|
out << 4 << " ";
|
|
else
|
|
if (j == Element::SEGMENT)
|
|
out << 2 << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
|
|
// print the vertices
|
|
out << NumOfVertices << '\n';
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << vertices[i](j) << " ";
|
|
out << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParMesh::PrintAsOne(ostream &out)
|
|
{
|
|
int i, j, k, p, nv_ne[2], &nv = nv_ne[0], &ne = nv_ne[1], vc;
|
|
const int *v;
|
|
MPI_Status status;
|
|
Array<double> vert;
|
|
Array<int> ints;
|
|
|
|
if (MyRank == 0)
|
|
{
|
|
out << "MFEM mesh v1.0\n";
|
|
|
|
// optional
|
|
out <<
|
|
"\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"
|
|
"#\n";
|
|
|
|
out << "\ndimension\n" << Dim;
|
|
}
|
|
|
|
nv = NumOfElements;
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
if (MyRank == 0)
|
|
{
|
|
out << "\n\nelements\n" << ne << '\n';
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
// processor number + 1 as attribute and geometry type
|
|
out << 1 << ' ' << elements[i]->GetGeometryType();
|
|
// vertices
|
|
nv = elements[i]->GetNVertices();
|
|
v = elements[i]->GetVertices();
|
|
for (j = 0; j < nv; j++)
|
|
out << ' ' << v[j];
|
|
out << '\n';
|
|
}
|
|
vc = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(nv_ne, 2, MPI_INT, p, 444, MyComm, &status);
|
|
ints.SetSize(ne);
|
|
MPI_Recv(&ints[0], ne, MPI_INT, p, 445, MyComm, &status);
|
|
for (i = 0; i < ne; )
|
|
{
|
|
// processor number + 1 as attribute and geometry type
|
|
out << p+1 << ' ' << ints[i];
|
|
// vertices
|
|
k = Geometries.GetVertices(ints[i++])->GetNPoints();
|
|
for (j = 0; j < k; j++)
|
|
out << ' ' << vc + ints[i++];
|
|
out << '\n';
|
|
}
|
|
vc += nv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// for each element send its geometry type and its vertices
|
|
ne = 0;
|
|
for (i = 0; i < NumOfElements; i++)
|
|
ne += 1 + elements[i]->GetNVertices();
|
|
nv = NumOfVertices;
|
|
MPI_Send(nv_ne, 2, MPI_INT, 0, 444, MyComm);
|
|
ints.SetSize(ne);
|
|
for (i = j = 0; i < NumOfElements; i++)
|
|
{
|
|
ints[j++] = elements[i]->GetGeometryType();
|
|
nv = elements[i]->GetNVertices();
|
|
v = elements[i]->GetVertices();
|
|
for (k = 0; k < nv; k++)
|
|
ints[j++] = v[k];
|
|
}
|
|
MPI_Send(&ints[0], ne, MPI_INT, 0, 445, MyComm);
|
|
}
|
|
|
|
// boundary + shared boundary
|
|
Array<Element *> &shared_boundary =
|
|
(Dim == 2) ? shared_edges : shared_faces;
|
|
nv = NumOfBdrElements + shared_boundary.Size();
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
if (MyRank == 0)
|
|
{
|
|
out << "\nboundary\n" << ne << '\n';
|
|
// actual boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
// processor number + 1 as bdr. attr. and bdr. geometry type
|
|
out << 1 << ' ' << boundary[i]->GetGeometryType();
|
|
// vertices
|
|
nv = boundary[i]->GetNVertices();
|
|
v = boundary[i]->GetVertices();
|
|
for (j = 0; j < nv; j++)
|
|
out << ' ' << v[j];
|
|
out << '\n';
|
|
}
|
|
// shared boundary (interface)
|
|
for (i = 0; i < shared_boundary.Size(); i++)
|
|
{
|
|
// processor number + 1 as bdr. attr. and bdr. geometry type
|
|
out << 1 << ' ' << shared_boundary[i]->GetGeometryType();
|
|
// vertices
|
|
nv = shared_boundary[i]->GetNVertices();
|
|
v = shared_boundary[i]->GetVertices();
|
|
for (j = 0; j < nv; j++)
|
|
out << ' ' << v[j];
|
|
out << '\n';
|
|
}
|
|
vc = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(nv_ne, 2, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(ne);
|
|
MPI_Recv(&ints[0], ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; )
|
|
{
|
|
// processor number + 1 as bdr. attr. and bdr. geometry type
|
|
out << p+1 << ' ' << ints[i];
|
|
k = Geometries.GetVertices(ints[i++])->GetNPoints();
|
|
// vertices
|
|
for (j = 0; j < k; j++)
|
|
out << ' ' << vc + ints[i++];
|
|
out << '\n';
|
|
}
|
|
vc += nv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// for each boundary and shared boundary element send its
|
|
// geometry type and its vertices
|
|
ne = 0;
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
ne += 1 + boundary[i]->GetNVertices();
|
|
for (i = 0; i < shared_boundary.Size(); i++)
|
|
ne += 1 + shared_boundary[i]->GetNVertices();
|
|
nv = NumOfVertices;
|
|
MPI_Send(nv_ne, 2, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(ne);
|
|
// boundary
|
|
for (i = j = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
ints[j++] = boundary[i]->GetGeometryType();
|
|
nv = boundary[i]->GetNVertices();
|
|
v = boundary[i]->GetVertices();
|
|
for (k = 0; k < nv; k++)
|
|
ints[j++] = v[k];
|
|
}
|
|
// shared boundary
|
|
for (i = 0; i < shared_boundary.Size(); i++)
|
|
{
|
|
ints[j++] = shared_boundary[i]->GetGeometryType();
|
|
nv = shared_boundary[i]->GetNVertices();
|
|
v = shared_boundary[i]->GetVertices();
|
|
for (k = 0; k < nv; k++)
|
|
ints[j++] = v[k];
|
|
}
|
|
MPI_Send(&ints[0], ne, MPI_INT, 0, 447, MyComm);
|
|
}
|
|
|
|
// vertices / nodes
|
|
MPI_Reduce(&NumOfVertices, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
if (MyRank == 0)
|
|
out << "\nvertices\n" << nv << '\n';
|
|
if (Nodes == NULL)
|
|
{
|
|
if (MyRank == 0)
|
|
{
|
|
out << Dim << '\n';
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
{
|
|
out << vertices[i](0);
|
|
for (j = 1; j < Dim; j++)
|
|
out << ' ' << vertices[i](j);
|
|
out << '\n';
|
|
}
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 448, MyComm, &status);
|
|
vert.SetSize(nv*Dim);
|
|
MPI_Recv(&vert[0], nv*Dim, MPI_DOUBLE, p, 449, MyComm, &status);
|
|
for (i = 0; i < nv; i++)
|
|
{
|
|
out << vert[i*Dim];
|
|
for (j = 1; j < Dim; j++)
|
|
out << ' ' << vert[i*Dim+j];
|
|
out << '\n';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 448, MyComm);
|
|
vert.SetSize(NumOfVertices*Dim);
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
for (j = 0; j < Dim; j++)
|
|
vert[i*Dim+j] = vertices[i](j);
|
|
MPI_Send(&vert[0], NumOfVertices*Dim, MPI_DOUBLE, 0, 449, MyComm);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (MyRank == 0)
|
|
out << "\nnodes\n";
|
|
ParGridFunction *pnodes = dynamic_cast<ParGridFunction *>(Nodes);
|
|
if (pnodes)
|
|
{
|
|
pnodes->SaveAsOne(out);
|
|
}
|
|
else
|
|
{
|
|
ParFiniteElementSpace *pfes =
|
|
dynamic_cast<ParFiniteElementSpace *>(Nodes->FESpace());
|
|
if (pfes)
|
|
{
|
|
// create a wrapper ParGridFunction
|
|
ParGridFunction ParNodes(pfes, Nodes);
|
|
ParNodes.SaveAsOne(out);
|
|
}
|
|
else
|
|
mfem_error("ParMesh::PrintAsOne : Nodes have no parallel info!");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ParMesh::PrintAsOneXG(ostream &out)
|
|
{
|
|
if (Dim == 3 && meshgen == 1)
|
|
{
|
|
int i, j, k, nv, ne, p;
|
|
const int *ind, *v;
|
|
MPI_Status status;
|
|
Array<double> vert;
|
|
Array<int> ints;
|
|
|
|
if (MyRank == 0)
|
|
{
|
|
out << "NETGEN_Neutral_Format\n";
|
|
// print the vertices
|
|
ne = NumOfVertices;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << nv << '\n';
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << " " << vertices[i](j);
|
|
out << '\n';
|
|
}
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
vert.SetSize(Dim*nv);
|
|
MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
|
|
for (i = 0; i < nv; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << " " << vert[Dim*i+j];
|
|
out << '\n';
|
|
}
|
|
}
|
|
|
|
// print the elements
|
|
nv = NumOfElements;
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << ne << '\n';
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
nv = elements[i]->GetNVertices();
|
|
ind = elements[i]->GetVertices();
|
|
out << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(4*ne);
|
|
MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << p+1;
|
|
for (j = 0; j < 4; j++)
|
|
out << " " << k+ints[i*4+j]+1;
|
|
out << '\n';
|
|
}
|
|
k += nv;
|
|
}
|
|
// print the boundary + shared faces information
|
|
nv = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << ne << '\n';
|
|
// boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
nv = boundary[i]->GetNVertices();
|
|
ind = boundary[i]->GetVertices();
|
|
out << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
// shared faces
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
nv = shared_faces[i]->GetNVertices();
|
|
ind = shared_faces[i]->GetVertices();
|
|
out << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(3*ne);
|
|
MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << p+1;
|
|
for (j = 0; j < 3; j++)
|
|
out << " " << k+ints[i*3+j]+1;
|
|
out << '\n';
|
|
}
|
|
k += nv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ne = NumOfVertices;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
vert.SetSize(Dim*NumOfVertices);
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
for (j = 0; j < Dim; j++)
|
|
vert[Dim*i+j] = vertices[i](j);
|
|
MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
|
|
0, 445, MyComm);
|
|
// elements
|
|
ne = NumOfElements;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(NumOfElements*4);
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
v = elements[i]->GetVertices();
|
|
for (j = 0; j < 4; j++)
|
|
ints[4*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 4*NumOfElements, MPI_INT, 0, 447, MyComm);
|
|
// boundary + shared faces
|
|
nv = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
ne = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(3*ne);
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
v = boundary[i]->GetVertices();
|
|
for (j = 0; j < 3; j++)
|
|
ints[3*i+j] = v[j];
|
|
}
|
|
for ( ; i < ne; i++)
|
|
{
|
|
v = shared_faces[i-NumOfBdrElements]->GetVertices();
|
|
for (j = 0; j < 3; j++)
|
|
ints[3*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 3*ne, MPI_INT, 0, 447, MyComm);
|
|
}
|
|
}
|
|
|
|
if (Dim == 3 && meshgen == 2)
|
|
{
|
|
int i, j, k, nv, ne, p;
|
|
const int *ind, *v;
|
|
MPI_Status status;
|
|
Array<double> vert;
|
|
Array<int> ints;
|
|
|
|
int TG_nv, TG_ne, TG_nbe;
|
|
|
|
if (MyRank == 0)
|
|
{
|
|
MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
nv = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
|
|
out << "TrueGrid\n"
|
|
<< "1 " << TG_nv << " " << TG_ne << " 0 0 0 0 0 0 0\n"
|
|
<< "0 0 0 1 0 0 0 0 0 0 0\n"
|
|
<< "0 0 " << TG_nbe << " 0 0 0 0 0 0 0 0 0 0 0 0 0\n"
|
|
<< "0.0 0.0 0.0 0 0 0.0 0.0 0 0.0\n"
|
|
<< "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n";
|
|
|
|
// print the vertices
|
|
nv = TG_nv;
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
out << i+1 << " 0.0 " << vertices[i](0) << " " << vertices[i](1)
|
|
<< " " << vertices[i](2) << " 0.0\n";
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
vert.SetSize(Dim*nv);
|
|
MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
|
|
for (i = 0; i < nv; i++)
|
|
out << i+1 << " 0.0 " << vert[Dim*i] << " " << vert[Dim*i+1]
|
|
<< " " << vert[Dim*i+2] << " 0.0\n";
|
|
}
|
|
|
|
// print the elements
|
|
ne = TG_ne;
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
nv = elements[i]->GetNVertices();
|
|
ind = elements[i]->GetVertices();
|
|
out << i+1 << " " << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << '\n';
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(8*ne);
|
|
MPI_Recv(&ints[0], 8*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << i+1 << " " << p+1;
|
|
for (j = 0; j < 8; j++)
|
|
out << " " << k+ints[i*8+j]+1;
|
|
out << '\n';
|
|
}
|
|
k += nv;
|
|
}
|
|
|
|
// print the boundary + shared faces information
|
|
ne = TG_nbe;
|
|
// boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
nv = boundary[i]->GetNVertices();
|
|
ind = boundary[i]->GetVertices();
|
|
out << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << " 1.0 1.0 1.0 1.0\n";
|
|
}
|
|
// shared faces
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
{
|
|
nv = shared_faces[i]->GetNVertices();
|
|
ind = shared_faces[i]->GetVertices();
|
|
out << 1;
|
|
for (j = 0; j < nv; j++)
|
|
out << " " << ind[j]+1;
|
|
out << " 1.0 1.0 1.0 1.0\n";
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(4*ne);
|
|
MPI_Recv(&ints[0], 4*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << p+1;
|
|
for (j = 0; j < 4; j++)
|
|
out << " " << k+ints[i*4+j]+1;
|
|
out << " 1.0 1.0 1.0 1.0\n";
|
|
}
|
|
k += nv;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MPI_Reduce(&NumOfVertices, &TG_nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Reduce(&NumOfElements, &TG_ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
nv = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Reduce(&nv, &TG_nbe, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
vert.SetSize(Dim*NumOfVertices);
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
for (j = 0; j < Dim; j++)
|
|
vert[Dim*i+j] = vertices[i](j);
|
|
MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE, 0, 445, MyComm);
|
|
// elements
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(NumOfElements*8);
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
v = elements[i]->GetVertices();
|
|
for (j = 0; j < 8; j++)
|
|
ints[8*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 8*NumOfElements, MPI_INT, 0, 447, MyComm);
|
|
// boundary + shared faces
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
ne = NumOfBdrElements + shared_faces.Size();
|
|
MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(4*ne);
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
v = boundary[i]->GetVertices();
|
|
for (j = 0; j < 4; j++)
|
|
ints[4*i+j] = v[j];
|
|
}
|
|
for ( ; i < ne; i++)
|
|
{
|
|
v = shared_faces[i-NumOfBdrElements]->GetVertices();
|
|
for (j = 0; j < 4; j++)
|
|
ints[4*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 4*ne, MPI_INT, 0, 447, MyComm);
|
|
}
|
|
}
|
|
|
|
if (Dim == 2)
|
|
{
|
|
int i, j, k, attr, nv, ne, p;
|
|
Array<int> v;
|
|
MPI_Status status;
|
|
Array<double> vert;
|
|
Array<int> ints;
|
|
|
|
|
|
if (MyRank == 0)
|
|
{
|
|
out << "areamesh2\n\n";
|
|
|
|
// print the boundary + shared edges information
|
|
nv = NumOfBdrElements + shared_edges.Size();
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << ne << '\n';
|
|
// boundary
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
attr = boundary[i]->GetAttribute();
|
|
boundary[i]->GetVertices(v);
|
|
out << attr << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
// shared edges
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
{
|
|
attr = shared_edges[i]->GetAttribute();
|
|
shared_edges[i]->GetVertices(v);
|
|
out << attr << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(2*ne);
|
|
MPI_Recv(&ints[0], 2*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << p+1;
|
|
for (j = 0; j < 2; j++)
|
|
out << " " << k+ints[i*2+j]+1;
|
|
out << '\n';
|
|
}
|
|
k += nv;
|
|
}
|
|
|
|
// print the elements
|
|
nv = NumOfElements;
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << ne << '\n';
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
attr = elements[i]->GetAttribute();
|
|
elements[i]->GetVertices(v);
|
|
out << 1 << " " << 3 << " ";
|
|
for (j = 0; j < v.Size(); j++)
|
|
out << v[j] + 1 << " ";
|
|
out << '\n';
|
|
}
|
|
k = NumOfVertices;
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
MPI_Recv(&ne, 1, MPI_INT, p, 446, MyComm, &status);
|
|
ints.SetSize(3*ne);
|
|
MPI_Recv(&ints[0], 3*ne, MPI_INT, p, 447, MyComm, &status);
|
|
for (i = 0; i < ne; i++)
|
|
{
|
|
out << p+1 << " " << 3;
|
|
for (j = 0; j < 3; j++)
|
|
out << " " << k+ints[i*3+j]+1;
|
|
out << '\n';
|
|
}
|
|
k += nv;
|
|
}
|
|
|
|
// print the vertices
|
|
ne = NumOfVertices;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
out << nv << '\n';
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << vertices[i](j) << " ";
|
|
out << '\n';
|
|
}
|
|
for (p = 1; p < NRanks; p++)
|
|
{
|
|
MPI_Recv(&nv, 1, MPI_INT, p, 444, MyComm, &status);
|
|
vert.SetSize(Dim*nv);
|
|
MPI_Recv(&vert[0], Dim*nv, MPI_DOUBLE, p, 445, MyComm, &status);
|
|
for (i = 0; i < nv; i++)
|
|
{
|
|
for (j = 0; j < Dim; j++)
|
|
out << " " << vert[Dim*i+j];
|
|
out << '\n';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// boundary + shared faces
|
|
nv = NumOfBdrElements + shared_edges.Size();
|
|
MPI_Reduce(&nv, &ne, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
ne = NumOfBdrElements + shared_edges.Size();
|
|
MPI_Send(&ne, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(2*ne);
|
|
for (i = 0; i < NumOfBdrElements; i++)
|
|
{
|
|
boundary[i]->GetVertices(v);
|
|
for (j = 0; j < 2; j++)
|
|
ints[2*i+j] = v[j];
|
|
}
|
|
for ( ; i < ne; i++)
|
|
{
|
|
shared_edges[i-NumOfBdrElements]->GetVertices(v);
|
|
for (j = 0; j < 2; j++)
|
|
ints[2*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 2*ne, MPI_INT, 0, 447, MyComm);
|
|
// elements
|
|
ne = NumOfElements;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
MPI_Send(&NumOfElements, 1, MPI_INT, 0, 446, MyComm);
|
|
ints.SetSize(NumOfElements*3);
|
|
for (i = 0; i < NumOfElements; i++)
|
|
{
|
|
elements[i]->GetVertices(v);
|
|
for (j = 0; j < 3; j++)
|
|
ints[3*i+j] = v[j];
|
|
}
|
|
MPI_Send(&ints[0], 3*NumOfElements, MPI_INT, 0, 447, MyComm);
|
|
// vertices
|
|
ne = NumOfVertices;
|
|
MPI_Reduce(&ne, &nv, 1, MPI_INT, MPI_SUM, 0, MyComm);
|
|
MPI_Send(&NumOfVertices, 1, MPI_INT, 0, 444, MyComm);
|
|
vert.SetSize(Dim*NumOfVertices);
|
|
for (i = 0; i < NumOfVertices; i++)
|
|
for (j = 0; j < Dim; j++)
|
|
vert[Dim*i+j] = vertices[i](j);
|
|
MPI_Send(&vert[0], Dim*NumOfVertices, MPI_DOUBLE,
|
|
0, 445, MyComm);
|
|
}
|
|
}
|
|
}
|
|
|
|
ParMesh::~ParMesh()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < shared_faces.Size(); i++)
|
|
FreeElement(shared_faces[i]);
|
|
for (i = 0; i < shared_edges.Size(); i++)
|
|
FreeElement(shared_edges[i]);
|
|
|
|
// The Mesh destructor is called automatically
|
|
}
|
|
|
|
#endif
|