12 #include "../config/config.hpp"
18 #include "../general/forall.hpp"
19 #include "../general/sort_pairs.hpp"
20 #include "../mesh/mesh_headers.hpp"
21 #include "../general/binaryio.hpp"
34 ParInit(pmesh ? pmesh : orig.pmesh);
50 f ? f : global_fes->FEColl(),
51 global_fes->GetVDim(), global_fes->GetOrdering())
70 int dim,
int ordering)
80 if (globNURBSext == NULL) {
return NULL; }
81 const ParNURBSExtension *pNURBSext =
82 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
83 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
85 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
87 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
90 void ParFiniteElementSpace::ParInit(ParMesh *pm)
115 MFEM_ASSERT(
own_ext,
"internal error");
117 ParNURBSExtension *pNe =
new ParNURBSExtension(
133 void ParFiniteElementSpace::Construct()
136 " for ParFiniteElementSpace yet.");
140 ConstructTrueNURBSDofs();
141 GenerateGlobalOffsets();
146 GenerateGlobalOffsets();
159 ngedofs = ngfdofs = 0;
179 ngdofs = ngvdofs + ngedofs + ngfdofs;
183 ltdof_size = BuildParallelConformingInterpolation(
184 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
194 long ltdofs = ltdof_size;
195 long min_ltdofs, max_ltdofs, sum_ltdofs;
197 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG, MPI_MIN, 0, MyComm);
198 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG, MPI_MAX, 0, MyComm);
199 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG, MPI_SUM, 0, MyComm);
203 double avg = double(sum_ltdofs) / NRanks;
204 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
205 <<
", avg " << std::fixed << std::setprecision(1) << avg
206 <<
", max " << max_ltdofs
207 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
215 mfem::out <<
"True DOFs by rank: " << ltdofs;
216 for (
int i = 1; i < NRanks; i++)
219 MPI_Recv(<dofs, 1, MPI_LONG, i, 123, MyComm, &status);
226 MPI_Send(<dofs, 1, MPI_LONG, 0, 123, MyComm);
231 void ParFiniteElementSpace::GetGroupComm(
236 int nvd, ned, ntd = 0, nqd = 0;
239 int group_ldof_counter;
264 group_ldof_counter = 0;
265 for (gr = 1; gr < ng; gr++)
268 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
274 group_ldof_counter *=
vdim;
277 group_ldof.
SetDims(ng, group_ldof_counter);
280 group_ldof_counter = 0;
281 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
282 for (gr = 1; gr < ng; gr++)
284 int j, k, l, m, o, nv, ne, nt, nq;
295 for (j = 0; j < nv; j++)
301 for (l = 0; l < nvd; l++, m++)
311 for (l = 0; l < dofs.
Size(); l++)
313 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
321 for (j = 0; j < ne; j++)
328 for (l = 0; l < ned; l++)
332 dofs[l] = m + (-1-ind[l]);
335 (*ldof_sign)[dofs[l]] = -1;
340 dofs[l] = m + ind[l];
349 for (l = 0; l < dofs.
Size(); l++)
351 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
359 for (j = 0; j < nt; j++)
366 for (l = 0; l < ntd; l++)
370 dofs[l] = m + (-1-ind[l]);
373 (*ldof_sign)[dofs[l]] = -1;
378 dofs[l] = m + ind[l];
387 for (l = 0; l < dofs.
Size(); l++)
389 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
397 for (j = 0; j < nq; j++)
404 for (l = 0; l < nqd; l++)
408 dofs[l] = m + (-1-ind[l]);
411 (*ldof_sign)[dofs[l]] = -1;
416 dofs[l] = m + ind[l];
425 for (l = 0; l < dofs.
Size(); l++)
427 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
432 group_ldof.
GetI()[gr+1] = group_ldof_counter;
438 void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
442 for (
int i = 0; i < dofs.Size(); i++)
446 if (ldof_sign[-1-dofs[i]] < 0)
448 dofs[i] = -1-dofs[i];
453 if (ldof_sign[dofs[i]] < 0)
455 dofs[i] = -1-dofs[i];
461 void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
463 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
464 ApplyLDofSigns(all_dofs);
477 ApplyLDofSigns(dofs);
491 ApplyLDofSigns(dofs);
506 ApplyLDofSigns(dofs);
524 auto key = std::make_tuple(is_dg_space, e_ordering, type, m);
525 auto itr =
L2F.find(key);
526 if (itr !=
L2F.end())
550 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
551 pmesh->
GroupEdge(group, ei, l_edge, ori);
561 for (
int i = 0; i < dofs.
Size(); i++)
563 const int di = dofs[i];
564 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
573 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
574 "invalid triangular face index");
585 for (
int i = 0; i < dofs.
Size(); i++)
587 const int di = dofs[i];
588 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
597 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
598 "invalid quadrilateral face index");
609 for (
int i = 0; i < dofs.
Size(); i++)
611 const int di = dofs[i];
612 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
617 void ParFiniteElementSpace::GenerateGlobalOffsets()
const
629 if (HYPRE_AssumedPartitionCheck())
632 GroupTopology > = GetGroupTopo();
633 int nsize = gt.GetNumNeighbors()-1;
634 MPI_Request *requests =
new MPI_Request[2*nsize];
635 MPI_Status *statuses =
new MPI_Status[2*nsize];
636 tdof_nb_offsets.
SetSize(nsize+1);
637 tdof_nb_offsets[0] = tdof_offsets[0];
640 int request_counter = 0;
641 for (
int i = 1; i <= nsize; i++)
643 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
644 gt.GetNeighborRank(i), 5365, MyComm,
645 &requests[request_counter++]);
647 for (
int i = 1; i <= nsize; i++)
649 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
650 gt.GetNeighborRank(i), 5365, MyComm,
651 &requests[request_counter++]);
653 MPI_Waitall(request_counter, requests, statuses);
660 void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
669 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
670 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
673 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
674 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
682 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
684 i_diag[0] = i_offd[0] = 0;
685 diag_counter = offd_counter = 0;
686 for (
int i = 0; i < ldof; i++)
691 j_diag[diag_counter++] = ltdof;
696 cmap_j_offd[offd_counter].two = offd_counter;
699 i_diag[i+1] = diag_counter;
700 i_offd[i+1] = offd_counter;
703 SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_counter);
705 for (
int i = 0; i < offd_counter; i++)
707 cmap[i] = cmap_j_offd[i].one;
708 j_offd[cmap_j_offd[i].two] = i;
711 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
712 i_diag, j_diag, i_offd, j_offd, cmap, offd_counter);
723 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
724 P_pc_col_starts, NULL,
true);
733 for (
int i = 0; i < ldof_group.
Size(); i++)
737 if (ldof_ltdof[i] >= 0)
755 gc->
Create(pNURBSext()->ldof_group);
759 GetGroupComm(*gc, 0);
769 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
773 gcomm->
Bcast(ldof_marker);
804 const int *ess_dofs_data = ess_dofs.
HostRead();
805 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
808 const int *ted = true_ess_dofs.
HostRead();
809 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
811 if (
bool(ted[i]) != bool(true_ess_dofs2[i])) { counter++; }
813 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter);
825 return ldof_ltdof[ldof];
829 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
831 return ldof_ltdof[ldof];
844 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
850 if (HYPRE_AssumedPartitionCheck())
852 return ldof_ltdof[ldof] +
857 return ldof_ltdof[ldof] +
867 MFEM_ABORT(
"Not implemented for NC mesh.");
870 if (HYPRE_AssumedPartitionCheck())
874 return ldof_ltdof[sldof] +
876 ldof_group[sldof])] /
vdim;
880 return (ldof_ltdof[sldof*
vdim] +
881 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
888 return ldof_ltdof[sldof] +
890 ldof_group[sldof])] /
vdim;
894 return (ldof_ltdof[sldof*
vdim] +
895 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
902 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
907 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
914 if (Pconf) {
return Pconf; }
943 if (Rconf) {
return Rconf; }
986 if (num_face_nbrs == 0)
992 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
993 MPI_Request *send_requests = requests;
994 MPI_Request *recv_requests = requests + num_face_nbrs;
995 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1001 Table send_nbr_elem_dof;
1008 for (
int fn = 0; fn < num_face_nbrs; fn++)
1013 for (
int i = 0; i < num_my_elems; i++)
1016 for (
int j = 0; j < ldofs.
Size(); j++)
1018 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1020 if (ldof_marker[ldof] != fn)
1022 ldof_marker[ldof] = fn;
1032 MyComm, &send_requests[fn]);
1035 MyComm, &recv_requests[fn]);
1038 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1043 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1051 int *send_I = send_nbr_elem_dof.
GetI();
1053 for (
int fn = 0; fn < num_face_nbrs; fn++)
1057 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1058 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1060 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1061 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1064 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1065 send_nbr_elem_dof.
MakeJ();
1069 for (
int fn = 0; fn < num_face_nbrs; fn++)
1074 for (
int i = 0; i < num_my_elems; i++)
1077 for (
int j = 0; j < ldofs.
Size(); j++)
1079 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1081 if (ldof_marker[ldof] != fn)
1083 ldof_marker[ldof] = fn;
1088 send_el_off[fn] + i, ldofs, ldofs.
Size());
1095 int *send_J = send_nbr_elem_dof.
GetJ();
1096 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1100 int j_end = send_I[send_el_off[fn+1]];
1102 for (
int i = 0; i < num_ldofs; i++)
1104 int ldof = (ldofs[i] >= 0 ? ldofs[i] : -1-ldofs[i]);
1105 ldof_marker[ldof] = i;
1108 for ( ; j < j_end; j++)
1110 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1111 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1115 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1122 for (
int fn = 0; fn < num_face_nbrs; fn++)
1127 MPI_Isend(send_J + send_I[send_el_off[fn]],
1128 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1129 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1131 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1132 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1133 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1136 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1139 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1142 int j_end = recv_I[recv_el_off[fn+1]];
1144 for ( ; j < j_end; j++)
1157 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1161 for (
int fn = 0; fn < num_face_nbrs; fn++)
1168 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1172 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1175 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1176 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1183 for (
int fn = 0; fn < num_face_nbrs; fn++)
1188 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1189 MyComm, &send_requests[fn]);
1191 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1192 MyComm, &recv_requests[fn]);
1195 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1199 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1213 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1231 int el1, el2, inf1, inf2;
1244 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1246 for (
int j = 0; j < vdofs.
Size(); j++)
1248 const int ldof = vdofs[j];
1249 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1261 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
1262 " does not support NURBS!");
1281 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1282 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1283 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1289 void ParFiniteElementSpace::ConstructTrueDofs()
1296 GetGroupComm(*gcomm, 1, &ldof_sign);
1306 for (gr = 1; gr < group_ldof.
Size(); gr++)
1308 const int *ldofs = group_ldof.
GetRow(gr);
1309 const int nldofs = group_ldof.
RowSize(gr);
1310 for (i = 0; i < nldofs; i++)
1312 ldof_group[ldofs[i]] = gr;
1317 for (i = 0; i < nldofs; i++)
1319 ldof_ltdof[ldofs[i]] = -2;
1326 for (i = 0; i < n; i++)
1328 if (ldof_ltdof[i] == -1)
1330 ldof_ltdof[i] = ltdof_size++;
1336 gcomm->
Bcast(ldof_ltdof);
1339 void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1342 GroupTopology > = pNURBSext()->
gtopo;
1343 gcomm =
new GroupCommunicator(gt);
1348 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1352 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1354 for (
int i = 0; i < n; i++)
1356 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1360 gcomm->
Create(ldof_group);
1368 for (
int i = 0; i < n; i++)
1370 if (gt.IAmMaster(ldof_group[i]))
1372 ldof_ltdof[i] = ltdof_size;
1383 gcomm->
Bcast(ldof_ltdof);
1386 void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1387 Array<int> &dofs)
const
1391 for (
int j = 0; j < nv; j++)
1393 dofs[j] =
ndofs + nv*
id.index + j;
1397 void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1398 Array<int> &dofs)
const
1402 dofs.SetSize(2*nv + ne);
1407 for (
int i = 0; i < 2; i++)
1409 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1410 for (
int j = 0; j < nv; j++)
1412 dofs[i*nv + j] = k++;
1416 int k =
ndofs + ngvdofs + (edge_id.index - pncmesh->
GetNEdges())*ne;
1417 for (
int j = 0; j < ne; j++)
1419 dofs[2*nv + j] = k++;
1423 void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1424 Array<int> &dofs)
const
1426 int nfv, V[4], E[4], Eo[4];
1433 int nf = (nfv == 3) ? nf_tri : nf_quad;
1435 dofs.SetSize(nfv*(nv + ne) + nf);
1438 for (
int i = 0; i < nfv; i++)
1441 int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1442 for (
int j = 0; j < nv; j++)
1444 dofs[offset++] = first + j;
1448 for (
int i = 0; i < nfv; i++)
1451 int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
1452 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
1454 for (
int j = 0; j < ne; j++)
1456 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
1457 : (-1 - (first + (-1 - ind[j])));
1461 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1462 int first =
ndofs + ngvdofs + ngedofs + nf_quad*ghost_face_index;
1464 for (
int j = 0; j < nf; j++)
1466 dofs[offset++] = first + j;
1470 void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
1471 Array<int> &dofs)
const
1476 case 0: GetGhostVertexDofs(
id, dofs);
break;
1477 case 1: GetGhostEdgeDofs(
id, dofs);
break;
1478 case 2: GetGhostFaceDofs(
id, dofs);
break;
1482 void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
1483 Array<int> &dofs)
const
1485 int ned, ghost, first;
1491 first = (index < ghost)
1493 :
ndofs + (index - ghost)*ned;
1499 first = (index < ghost)
1501 :
ndofs + ngvdofs + (index - ghost)*ned;
1520 first =
ndofs + ngvdofs + ngedofs + index*stride;
1526 for (
int i = 0; i < ned; i++)
1528 dofs[i] = first + i;
1532 int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof)
const
1544 return (index < ghost)
1546 :
ndofs + (index - ghost)*ned + edof;
1552 return (index < ghost)
1553 ?
nvdofs + index*ned + edof
1554 :
ndofs + ngvdofs + (index - ghost)*ned + edof;
1568 return ndofs + ngvdofs + ngedofs + index*stride + edof;
1573 static int bisect(
const int* array,
int size,
int value)
1575 const int* end = array + size;
1576 const int* pos = std::lower_bound(array, end, value);
1577 MFEM_VERIFY(pos != end,
"value not found");
1584 void ParFiniteElementSpace::UnpackDof(
int dof,
1585 int &entity,
int &index,
int &edof)
const
1587 MFEM_VERIFY(dof >= 0,
"");
1593 entity = 0, index = dof / nv, edof = dof % nv;
1600 entity = 1, index = dof / ne, edof = dof % ne;
1609 index = dof / nf, edof = dof % nf;
1614 MFEM_ASSERT(table.Size(),
"");
1615 int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
1616 index = bisect(table.GetI(), table.Size(), jpos);
1617 edof = dof - table.GetRow(index)[0];
1622 MFEM_ABORT(
"Cannot unpack internal DOF");
1630 entity = 0, index = pncmesh->
GetNVertices() + dof / nv, edof = dof % nv;
1637 entity = 1, index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
1644 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
1648 MFEM_ABORT(
"Out of range DOF.");
1656 struct PMatrixElement
1662 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
1663 : column(col), stride(str), value(val) {}
1665 bool operator<(
const PMatrixElement &other)
const
1666 {
return column < other.column; }
1668 typedef std::vector<PMatrixElement> List;
1676 PMatrixElement::List elems;
1679 void AddRow(
const PMatrixRow &other,
double coef)
1681 elems.reserve(elems.size() + other.elems.size());
1682 for (
unsigned i = 0; i < other.elems.size(); i++)
1684 const PMatrixElement &oei = other.elems[i];
1686 PMatrixElement(oei.column, oei.stride, coef * oei.value));
1693 if (!elems.size()) {
return; }
1694 std::sort(elems.begin(), elems.end());
1697 for (
unsigned i = 1; i < elems.size(); i++)
1699 if (elems[j].column == elems[i].column)
1701 elems[j].value += elems[i].value;
1705 elems[++j] = elems[i];
1711 void write(std::ostream &os,
double sign)
const
1713 bin_io::write<int>(os, elems.size());
1714 for (
unsigned i = 0; i < elems.size(); i++)
1716 const PMatrixElement &e = elems[i];
1717 bin_io::write<HYPRE_BigInt>(os, e.column);
1718 bin_io::write<int>(os, e.stride);
1719 bin_io::write<double>(os, e.value * sign);
1723 void read(std::istream &is,
double sign)
1725 elems.resize(bin_io::read<int>(is));
1726 for (
unsigned i = 0; i < elems.size(); i++)
1728 PMatrixElement &e = elems[i];
1729 e.column = bin_io::read<HYPRE_BigInt>(is);
1730 e.stride = bin_io::read<int>(is);
1731 e.value = bin_io::read<double>(is) * sign;
1739 class NeighborRowMessage :
public VarMessage<314>
1742 typedef NCMesh::MeshId MeshId;
1747 int entity,
index, edof;
1751 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row)
1752 : entity(ent), index(idx), edof(edof), group(grp), row(row) {}
1754 RowInfo(
int ent,
int idx,
int edof, GroupId grp)
1755 : entity(ent), index(idx), edof(edof), group(grp) {}
1757 typedef std::vector<RowInfo> List;
1760 NeighborRowMessage() : pncmesh(NULL) {}
1762 void AddRow(
int entity,
int index,
int edof, GroupId group,
1763 const PMatrixRow &row)
1765 rows.push_back(RowInfo(entity, index, edof, group, row));
1768 const RowInfo::List& GetRows()
const {
return rows; }
1770 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
1771 void SetFEC(
const FiniteElementCollection* fec) { this->fec = fec; }
1773 typedef std::map<int, NeighborRowMessage> Map;
1779 const FiniteElementCollection* fec;
1781 virtual void Encode(
int rank);
1782 virtual void Decode(
int);
1786 void NeighborRowMessage::Encode(
int rank)
1788 std::ostringstream stream;
1790 Array<MeshId> ent_ids[3];
1791 Array<GroupId> group_ids[3];
1792 Array<int> row_idx[3];
1795 for (
unsigned i = 0; i < rows.size(); i++)
1797 const RowInfo &ri = rows[i];
1798 const MeshId &
id = pncmesh->GetNCList(ri.entity).LookUp(ri.index);
1799 ent_ids[ri.entity].Append(
id);
1800 row_idx[ri.entity].Append(i);
1801 group_ids[ri.entity].Append(ri.group);
1804 Array<GroupId> all_group_ids;
1805 all_group_ids.Reserve(rows.size());
1806 for (
int i = 0; i < 3; i++)
1808 all_group_ids.Append(group_ids[i]);
1811 pncmesh->AdjustMeshIds(ent_ids, rank);
1812 pncmesh->EncodeMeshIds(stream, ent_ids);
1813 pncmesh->EncodeGroups(stream, all_group_ids);
1816 for (
int ent = 0; ent < 3; ent++)
1818 const Array<MeshId> &ids = ent_ids[ent];
1819 for (
int i = 0; i < ids.Size(); i++)
1821 const MeshId &
id = ids[i];
1822 const RowInfo &ri = rows[row_idx[ent][i]];
1823 MFEM_ASSERT(ent == ri.entity,
"");
1825 #ifdef MFEM_DEBUG_PMATRIX
1826 mfem::out <<
"Rank " << pncmesh->MyRank <<
" sending to " << rank
1827 <<
": ent " << ri.entity <<
", index " << ri.index
1828 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
1829 << int(
id.local) <<
")" << std::endl;
1837 int eo = pncmesh->GetEdgeNCOrientation(
id);
1839 if ((edof = ind[edof]) < 0)
1846 bin_io::write<int>(stream, edof);
1847 ri.row.write(stream, s);
1852 stream.str().swap(data);
1855 void NeighborRowMessage::Decode(
int rank)
1857 std::istringstream stream(data);
1859 Array<MeshId> ent_ids[3];
1860 Array<GroupId> group_ids;
1863 pncmesh->DecodeMeshIds(stream, ent_ids);
1864 pncmesh->DecodeGroups(stream, group_ids);
1866 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
1867 MFEM_ASSERT(nrows == group_ids.Size(),
"");
1870 rows.reserve(nrows);
1873 for (
int ent = 0, gi = 0; ent < 3; ent++)
1875 const Array<MeshId> &ids = ent_ids[ent];
1876 for (
int i = 0; i < ids.Size(); i++)
1878 const MeshId &
id = ids[i];
1879 int edof = bin_io::read<int>(stream);
1882 const int *ind = NULL;
1885 int eo = pncmesh->GetEdgeNCOrientation(
id);
1891 int fo = pncmesh->GetFaceOrientation(
id.index);
1892 ind = fec->DofOrderForOrientation(geom, fo);
1896 if (ind && (edof = ind[edof]) < 0)
1902 rows.push_back(RowInfo(ent,
id.index, edof, group_ids[gi++]));
1903 rows.back().row.read(stream, s);
1905 #ifdef MFEM_DEBUG_PMATRIX
1906 mfem::out <<
"Rank " << pncmesh->MyRank <<
" receiving from " << rank
1907 <<
": ent " << rows.back().entity <<
", index "
1908 << rows.back().index <<
", edof " << rows.back().edof
1916 ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
1918 NeighborRowMessage::Map &send_msg)
const
1921 UnpackDof(dof, ent, idx, edof);
1924 for (
unsigned i = 0; i < group.size(); i++)
1926 int rank = group[i];
1929 NeighborRowMessage &msg = send_msg[rank];
1930 msg.AddRow(ent, idx, edof, group_id, row);
1931 msg.SetNCMesh(pncmesh);
1933 #ifdef MFEM_PMATRIX_STATS
1940 void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
1941 GroupId group_sent_id, GroupId group_id,
1942 NeighborRowMessage::Map &send_msg)
const
1945 UnpackDof(dof, ent, idx, edof);
1948 for (
unsigned i = 0; i < group.size(); i++)
1950 int rank = group[i];
1951 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
1953 NeighborRowMessage &msg = send_msg[rank];
1954 GroupId invalid = -1;
1955 msg.AddRow(ent, idx, edof, invalid, row);
1956 msg.SetNCMesh(pncmesh);
1958 #ifdef MFEM_PMATRIX_STATS
1961 #ifdef MFEM_DEBUG_PMATRIX
1963 << rank <<
": ent " << ent <<
", index" << idx
1964 <<
", edof " << edof << std::endl;
1970 #ifdef MFEM_DEBUG_PMATRIX
1971 void ParFiniteElementSpace
1972 ::DebugDumpDOFs(std::ostream &os,
1973 const SparseMatrix &deps,
1974 const Array<GroupId> &dof_group,
1975 const Array<GroupId> &dof_owner,
1976 const Array<bool> &finalized)
const
1978 for (
int i = 0; i < dof_group.Size(); i++)
1984 UnpackDof(i, ent, idx, edof);
1986 os << edof <<
" @ ";
1987 if (i >
ndofs) { os <<
"ghost "; }
1990 case 0: os <<
"vertex ";
break;
1991 case 1: os <<
"edge ";
break;
1992 default: os <<
"face ";
break;
1996 if (i < deps.Height() && deps.RowSize(i))
1998 os <<
"depends on ";
1999 for (
int j = 0; j < deps.RowSize(i); j++)
2001 os << deps.GetRowColumns(i)[j] <<
" ("
2002 << deps.GetRowEntries(i)[j] <<
")";
2003 if (j < deps.RowSize(i)-1) { os <<
", "; }
2012 os <<
"group " << dof_group[i] <<
" (";
2014 for (
unsigned j = 0; j < g.size(); j++)
2016 if (j) { os <<
", "; }
2020 os <<
"), owner " << dof_owner[i] <<
" (rank "
2021 << pncmesh->
GetGroup(dof_owner[i])[0] <<
"); "
2022 << (finalized[i] ?
"finalized" :
"NOT finalized");
2033 int ParFiniteElementSpace
2034 ::BuildParallelConformingInterpolation(HypreParMatrix **P, SparseMatrix **R,
2035 Array<HYPRE_BigInt> &dof_offs,
2036 Array<HYPRE_BigInt> &tdof_offs,
2037 Array<int> *dof_tdof,
2042 #ifdef MFEM_PMATRIX_STATS
2043 n_msgs_sent = n_msgs_recv = 0;
2044 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
2049 int total_dofs =
ndofs + ngdofs;
2050 SparseMatrix deps(
ndofs, total_dofs);
2052 if (!dg && !partial)
2054 Array<int> master_dofs, slave_dofs;
2057 for (
int entity = 0; entity <= 2; entity++)
2059 const NCMesh::NCList &list = pncmesh->
GetNCList(entity);
2060 if (!list.masters.Size()) {
continue; }
2062 IsoparametricTransformation T;
2066 for (
int mi = 0; mi < list.masters.Size(); mi++)
2068 const NCMesh::Master &mf = list.
masters[mi];
2071 if (pncmesh->
IsGhost(entity, mf.index))
2073 GetGhostDofs(entity, mf, master_dofs);
2080 if (!master_dofs.Size()) {
continue; }
2083 if (!fe) {
continue; }
2090 default: MFEM_ABORT(
"unsupported geometry");
2094 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
2096 const NCMesh::Slave &sf = list.slaves[si];
2097 if (pncmesh->
IsGhost(entity, sf.index)) {
continue; }
2099 const int variant = 0;
2100 GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
2101 if (!slave_dofs.Size()) {
continue; }
2103 list.OrientedPointMatrix(sf, T.GetPointMat());
2104 fe->GetLocalInterpolation(T, I);
2117 Array<GroupId> dof_group(total_dofs);
2118 Array<GroupId> dof_owner(total_dofs);
2127 for (
int entity = 0; entity <= 2; entity++)
2129 const NCMesh::NCList &list = pncmesh->
GetNCList(entity);
2132 { list.
conforming.Size(), list.masters.Size(), list.slaves.Size() };
2134 for (
int l = 0; l < 3; l++)
2136 for (
int i = 0; i < lsize[l]; i++)
2139 (l == 0) ? list.conforming[i] :
2140 (l == 1) ? (
const MeshId&) list.masters[i]
2141 : (
const MeshId&) list.slaves[i];
2143 if (
id.index < 0) {
continue; }
2148 GetBareDofs(entity,
id.index, dofs);
2150 for (
int j = 0; j < dofs.Size(); j++)
2153 dof_owner[dof] = owner;
2154 dof_group[dof] = group;
2163 Array<bool> finalized(total_dofs);
2167 int num_true_dofs = 0;
2168 for (
int i = 0; i <
ndofs; i++)
2170 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
2173 finalized[i] =
true;
2179 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
2183 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
2188 *R =
new SparseMatrix(num_true_dofs*
vdim, ndofs*vdim);
2192 dof_tdof->SetSize(ndofs*
vdim);
2196 std::vector<PMatrixRow> pmatrix(total_dofs);
2199 int vdim_factor = bynodes ? 1 :
vdim;
2200 int dof_stride = bynodes ? ndofs : 1;
2201 int tdof_stride = bynodes ? num_true_dofs : 1;
2204 std::list<NeighborRowMessage::Map> send_msg;
2205 send_msg.push_back(NeighborRowMessage::Map());
2208 for (
int dof = 0, tdof = 0; dof <
ndofs; dof++)
2212 pmatrix[dof].elems.push_back(
2213 PMatrixElement(my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.));
2216 if (dof_group[dof] != 0)
2218 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
2221 for (
int vd = 0; vd <
vdim; vd++)
2223 int vdof = dof*vdim_factor + vd*dof_stride;
2224 int vtdof = tdof*vdim_factor + vd*tdof_stride;
2226 if (R) { (*R)->Add(vtdof, vdof, 1.0); }
2227 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
2234 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2235 #ifdef MFEM_PMATRIX_STATS
2236 n_msgs_sent += send_msg.back().size();
2239 if (R) { (*R)->Finalize(); }
2244 NeighborRowMessage recv_msg;
2245 recv_msg.SetNCMesh(pncmesh);
2246 recv_msg.SetFEC(
fec);
2248 int num_finalized = num_true_dofs;
2250 buffer.elems.reserve(1024);
2252 while (num_finalized < ndofs)
2255 if (send_msg.back().size())
2257 send_msg.push_back(NeighborRowMessage::Map());
2262 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2264 recv_msg.Recv(rank, size, MyComm);
2265 #ifdef MFEM_PMATRIX_STATS
2267 n_rows_recv += recv_msg.GetRows().size();
2270 const NeighborRowMessage::RowInfo::List &rows = recv_msg.GetRows();
2271 for (
unsigned i = 0; i < rows.size(); i++)
2273 const NeighborRowMessage::RowInfo &ri = rows[i];
2274 int dof = PackDof(ri.entity, ri.index, ri.edof);
2275 pmatrix[dof] = ri.row;
2277 if (dof < ndofs && !finalized[dof]) { num_finalized++; }
2278 finalized[dof] =
true;
2280 if (ri.group >= 0 && dof_group[dof] != ri.group)
2283 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
2293 for (
int dof = 0; dof <
ndofs; dof++)
2295 if (finalized[dof]) {
continue; }
2297 bool owned = (dof_owner[dof] == 0);
2298 bool shared = (dof_group[dof] != 0);
2302 const int* dep_col = deps.GetRowColumns(dof);
2303 const double* dep_coef = deps.GetRowEntries(dof);
2304 int num_dep = deps.RowSize(dof);
2307 buffer.elems.clear();
2308 for (
int j = 0; j < num_dep; j++)
2310 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
2313 pmatrix[dof] = buffer;
2315 finalized[dof] =
true;
2322 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
2329 #ifdef MFEM_DEBUG_PMATRIX
2342 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2343 #ifdef MFEM_PMATRIX_STATS
2344 n_msgs_sent += send_msg.back().size();
2350 *P = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
2351 dof_offs, tdof_offs);
2357 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2359 recv_msg.RecvDrop(rank, size, MyComm);
2363 for (std::list<NeighborRowMessage::Map>::iterator
2364 it = send_msg.begin(); it != send_msg.end(); ++it)
2366 NeighborRowMessage::WaitAllSent(*it);
2369 #ifdef MFEM_PMATRIX_STATS
2370 int n_rounds = send_msg.size();
2371 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
2372 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
2374 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
2375 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2376 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2377 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2378 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2379 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
2383 mfem::out <<
"P matrix stats (avg per rank): "
2384 << double(glob_rounds)/NRanks <<
" rounds, "
2385 << double(glob_msgs_sent)/NRanks <<
" msgs sent, "
2386 << double(glob_msgs_recv)/NRanks <<
" msgs recv, "
2387 << double(glob_rows_sent)/NRanks <<
" rows sent, "
2388 << double(glob_rows_recv)/NRanks <<
" rows recv, "
2389 << double(glob_rows_fwd)/NRanks <<
" rows forwarded."
2394 return num_true_dofs*
vdim;
2398 HypreParMatrix* ParFiniteElementSpace
2399 ::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
2400 int local_rows,
int local_cols,
2401 Array<HYPRE_BigInt> &row_starts,
2402 Array<HYPRE_BigInt> &col_starts)
const
2404 bool assumed = HYPRE_AssumedPartitionCheck();
2407 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
2408 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
2411 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
2412 std::map<HYPRE_BigInt, int> col_map;
2413 for (
int i = 0; i < local_rows; i++)
2415 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2417 const PMatrixElement &elem = rows[i].elems[j];
2419 if (col >= first_col && col < next_col)
2426 for (
int vd = 0; vd <
vdim; vd++)
2436 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(col_map.size());
2438 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
2440 cmap[offd_col] = it->first;
2441 it->second = offd_col++;
2444 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
2445 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
2447 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
2448 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
2450 double *A_diag = Memory<double>(nnz_diag);
2451 double *A_offd = Memory<double>(nnz_offd);
2453 int vdim1 = bynodes ? vdim : 1;
2454 int vdim2 = bynodes ? 1 :
vdim;
2455 int vdim_offset = bynodes ? local_cols : 1;
2458 nnz_diag = nnz_offd = 0;
2460 for (
int vd1 = 0; vd1 < vdim1; vd1++)
2462 for (
int i = 0; i < local_rows; i++)
2464 for (
int vd2 = 0; vd2 < vdim2; vd2++)
2466 I_diag[vrow] = nnz_diag;
2467 I_offd[vrow++] = nnz_offd;
2469 int vd = bynodes ? vd1 : vd2;
2470 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2472 const PMatrixElement &elem = rows[i].elems[j];
2473 if (elem.column >= first_col && elem.column < next_col)
2475 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
2476 A_diag[nnz_diag++] = elem.value;
2480 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
2481 A_offd[nnz_offd++] = elem.value;
2487 MFEM_ASSERT(vrow == vdim*local_rows,
"");
2488 I_diag[vrow] = nnz_diag;
2489 I_offd[vrow] = nnz_offd;
2491 return new HypreParMatrix(MyComm,
2492 row_starts.Last(), col_starts.Last(),
2493 row_starts.GetData(), col_starts.GetData(),
2494 I_diag, J_diag, A_diag,
2495 I_offd, J_offd, A_offd,
2496 col_map.size(), cmap);
2499 template <
typename int_type>
2500 static int_type* make_i_array(
int nrows)
2502 int_type *I = Memory<int_type>(nrows+1);
2503 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
2507 template <
typename int_type>
2508 static int_type* make_j_array(int_type* I,
int nrows)
2511 for (
int i = 0; i < nrows; i++)
2513 if (I[i] >= 0) { nnz++; }
2515 int_type *J = Memory<int_type>(nnz);
2518 for (
int i = 0, k = 0; i <= nrows; i++)
2520 int_type col = I[i];
2522 if (col >= 0) { J[k++] = col; }
2528 ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
2529 const Table* old_elem_dof)
2531 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
2532 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
2533 "be called before ParFiniteElementSpace::RebalanceMatrix");
2535 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2536 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2539 ParNCMesh* pncmesh = pmesh->
pncmesh;
2545 const Array<int> &old_index = pncmesh->GetRebalanceOldIndex();
2546 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
2547 "Mesh::Rebalance was not called before "
2548 "ParFiniteElementSpace::RebalanceMatrix");
2551 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
2552 for (
int i = 0; i < pmesh->
GetNE(); i++)
2554 if (old_index[i] >= 0)
2556 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
2559 for (
int vd = 0; vd <
vdim; vd++)
2561 for (
int j = 0; j < dofs.Size(); j++)
2564 if (row < 0) { row = -1 - row; }
2566 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
2567 if (col < 0) { col = -1 - col; }
2574 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
2577 Array<int> new_elements;
2578 Array<long> old_remote_dofs;
2579 pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
2582 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
2583 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
2586 const long* old_dofs = &old_remote_dofs[pos];
2587 pos += dofs.Size() *
vdim;
2589 for (
int vd = 0; vd <
vdim; vd++)
2591 for (
int j = 0; j < dofs.Size(); j++)
2594 if (row < 0) { row = -1 - row; }
2596 if (i_diag[row] == i_diag[row+1])
2598 i_offd[row] = old_dofs[j + vd * dofs.Size()];
2605 #ifndef HYPRE_MIXEDINT
2606 HYPRE_Int *i_offd_hi = i_offd;
2609 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
2610 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
2611 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
2615 int offd_cols = i_offd_hi[vsize];
2616 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
2617 for (
int i = 0; i < offd_cols; i++)
2619 cmap_offd[i].one = j_offd[i];
2620 cmap_offd[i].two = i;
2623 #ifndef HYPRE_MIXEDINT
2624 HYPRE_Int *j_offd_hi = j_offd;
2626 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
2627 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
2630 SortPairs<HYPRE_BigInt, int>(cmap_offd, offd_cols);
2633 for (
int i = 0; i < offd_cols; i++)
2635 cmap[i] = cmap_offd[i].one;
2636 j_offd_hi[cmap_offd[i].two] = i;
2640 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
2641 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
2646 struct DerefDofMessage
2648 std::vector<HYPRE_BigInt> dofs;
2649 MPI_Request request;
2653 ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
2654 const Table* old_elem_dof)
2656 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
2658 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
2659 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
2661 #if 0 // check no longer seems to work with NC tet refinement
2662 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
2663 "Previous space is not finer.");
2671 Mesh::GeometryList elem_geoms(*
mesh);
2673 Array<int> dofs, old_dofs, old_vdofs;
2676 ParNCMesh* pncmesh = pmesh->
pncmesh;
2683 for (
int i = 0; i < elem_geoms.Size(); i++)
2689 const CoarseFineTransformations &dtrans = pncmesh->GetDerefinementTransforms();
2690 const Array<int> &old_ranks = pncmesh->GetDerefineOldRanks();
2692 std::map<int, DerefDofMessage> messages;
2694 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2695 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2699 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
2701 const Embedding &emb = dtrans.embeddings[k];
2703 int fine_rank = old_ranks[k];
2704 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
2705 : pncmesh->ElementRank(emb.parent);
2707 if (coarse_rank != MyRank && fine_rank == MyRank)
2709 old_elem_dof->GetRow(k, dofs);
2712 DerefDofMessage &msg = messages[k];
2713 msg.dofs.resize(dofs.Size());
2714 for (
int i = 0; i < dofs.Size(); i++)
2716 msg.dofs[i] = old_offset + dofs[i];
2719 MPI_Isend(&msg.dofs[0], msg.dofs.size(), HYPRE_MPI_BIG_INT,
2720 coarse_rank, 291, MyComm, &msg.request);
2722 else if (coarse_rank == MyRank && fine_rank != MyRank)
2724 MFEM_ASSERT(emb.parent >= 0,
"");
2727 DerefDofMessage &msg = messages[k];
2728 msg.dofs.resize(ldof[geom]*vdim);
2730 MPI_Irecv(&msg.dofs[0], ldof[geom]*vdim, HYPRE_MPI_BIG_INT,
2731 fine_rank, 291, MyComm, &msg.request);
2739 for (
int i = 0; i < elem_geoms.Size(); i++)
2745 SparseMatrix *diag =
new SparseMatrix(ndofs*vdim, old_ndofs*vdim);
2747 Array<char> mark(diag->Height());
2750 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
2752 const Embedding &emb = dtrans.embeddings[k];
2753 if (emb.parent < 0) {
continue; }
2755 int coarse_rank = pncmesh->ElementRank(emb.parent);
2756 int fine_rank = old_ranks[k];
2758 if (coarse_rank == MyRank && fine_rank == MyRank)
2761 DenseMatrix &lR = localR[
geom](emb.matrix);
2764 old_elem_dof->GetRow(k, old_dofs);
2766 for (
int vd = 0; vd <
vdim; vd++)
2768 old_dofs.Copy(old_vdofs);
2771 for (
int i = 0; i < lR.Height(); i++)
2773 if (!std::isfinite(lR(i, 0))) {
continue; }
2776 int m = (r >= 0) ? r : (-1 - r);
2781 diag->SetRow(r, old_vdofs, row);
2791 for (
auto it = messages.begin(); it != messages.end(); ++it)
2793 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
2797 SparseMatrix *offd =
new SparseMatrix(ndofs*vdim, 1);
2799 std::map<HYPRE_BigInt, int> col_map;
2800 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
2802 const Embedding &emb = dtrans.embeddings[k];
2803 if (emb.parent < 0) {
continue; }
2805 int coarse_rank = pncmesh->ElementRank(emb.parent);
2806 int fine_rank = old_ranks[k];
2808 if (coarse_rank == MyRank && fine_rank != MyRank)
2811 DenseMatrix &lR = localR[
geom](emb.matrix);
2815 DerefDofMessage &msg = messages[k];
2816 MFEM_ASSERT(msg.dofs.size(),
"");
2818 for (
int vd = 0; vd <
vdim; vd++)
2820 MFEM_ASSERT(ldof[geom],
"");
2823 for (
int i = 0; i < lR.Height(); i++)
2825 if (!std::isfinite(lR(i, 0))) {
continue; }
2828 int m = (r >= 0) ? r : (-1 - r);
2833 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
2834 for (
int j = 0; j < ldof[
geom]; j++)
2836 if (row[j] == 0.0) {
continue; }
2837 int &lcol = col_map[remote_dofs[j]];
2838 if (!lcol) { lcol = col_map.size(); }
2839 offd->_Set_(m, lcol-1, row[j]);
2849 offd->SetWidth(col_map.size());
2852 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
2853 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
2855 cmap[it->second-1] = it->first;
2862 int width = offd->Width();
2863 Array<Pair<HYPRE_BigInt, int> > reorder(width);
2864 for (
int i = 0; i < width; i++)
2866 reorder[i].one = cmap[i];
2871 Array<int> reindex(width);
2872 for (
int i = 0; i < width; i++)
2874 reindex[reorder[i].two] = i;
2875 cmap[i] = reorder[i].one;
2878 int *J = offd->GetJ();
2879 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
2881 J[i] = reindex[J[i]];
2883 offd->SortColumnIndices();
2887 R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
2888 dof_offsets, old_dof_offsets, diag, offd, cmap,
2891 R->SetOwnerFlags(R->OwnsDiag(), R->OwnsOffd(), 1);
2896 void ParFiniteElementSpace::Destroy()
2907 delete Pconf; Pconf = NULL;
2908 delete Rconf; Rconf = NULL;
2909 delete R_transpose; R_transpose = NULL;
2912 delete gcomm; gcomm = NULL;
2921 void ParFiniteElementSpace::CopyProlongationAndRestriction(
2922 const FiniteElementSpace &fes,
const Array<int> *perm)
2926 MFEM_VERIFY(pfes != NULL,
"");
2927 MFEM_VERIFY(P == NULL,
"");
2928 MFEM_VERIFY(R == NULL,
"");
2930 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
2933 int n = perm->Size();
2934 perm_mat =
new SparseMatrix(n, n);
2935 for (
int i=0; i<n; ++i)
2939 perm_mat->Set(i, j, s);
2941 perm_mat->Finalize();
2945 if (pfes->P != NULL)
2947 if (perm) { P = pfes->P->LeftDiagMult(*perm_mat); }
2948 else { P =
new HypreParMatrix(*pfes->P); }
2951 if (pfes->R != NULL)
2953 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
2954 else { R =
new SparseMatrix(*pfes->R); }
2972 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
2983 false, Tgf.OwnsOperator(),
false));
2984 Tgf.SetOperatorOwner(
false);
2991 "Parallel variable order space not supported yet.");
2999 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
3000 "each mesh modification.");
3009 Table* old_elem_dof = NULL;
3018 Swap(dof_offsets, old_dof_offsets);
3041 old_elem_dof = NULL;
3053 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof));
3058 false,
false,
true));
3065 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof));
3073 delete old_elem_dof;
3077 void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
3080 MFEM_VERIFY(new_pmesh != NULL,
3081 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
3088 : gc(gc_), local(local_)
3093 for (
int g=1; g<group_ldof.
Size(); ++g)
3097 n_external += group_ldof.
RowSize(g);
3100 int tsize = lsize - n_external;
3106 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3124 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
3126 gc(pfes.GroupComm()),
3132 for (
int gr = 1; gr < group_ldof.Size(); gr++)
3151 for ( ; j < end; j++)
3157 for ( ; j <
Height(); j++)
3171 const double *xdata = x.
HostRead();
3175 const int in_layout = 2;
3182 gc.
BcastBegin(const_cast<double*>(xdata), in_layout);
3186 for (
int i = 0; i < m; i++)
3189 std::copy(xdata+j-i, xdata+end-i, ydata+j);
3192 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
3194 const int out_layout = 0;
3207 const double *xdata = x.
HostRead();
3217 for (
int i = 0; i < m; i++)
3220 std::copy(xdata+j, xdata+end, ydata+j-i);
3223 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
3225 const int out_layout = 2;
3235 mpi_gpu_aware(
Device::GetGPUAwareMPI())
3238 const int tdofs = R->
Height();
3239 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
3241 ltdof_ldof.UseDevice();
3254 unique_ltdof.
Sort();
3260 MFEM_ASSERT(
shr_ltdof[i] != -1,
"internal error");
3285 int req_counter = 0;
3290 if (send_size > 0) { req_counter++; }
3294 if (recv_size > 0) { req_counter++; }
3296 requests =
new MPI_Request[req_counter];
3302 pfes.GetRestrictionMatrix(),
3305 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
3309 static void ExtractSubVector(
const Array<int> &indices,
3312 MFEM_ASSERT(indices.
Size() == out.
Size(),
"incompatible sizes!");
3313 auto y = out.
Write();
3314 const auto x = in.
Read();
3315 const auto I = indices.
Read();
3316 MFEM_FORALL(i, indices.
Size(), y[i] = x[I[i]];);
3330 static void SetSubVector(
const Array<int> &indices,
3333 MFEM_ASSERT(indices.
Size() == in.
Size(),
"incompatible sizes!");
3336 const auto x = in.
Read();
3337 const auto I = indices.
Read();
3338 MFEM_FORALL(i, indices.
Size(), y[I[i]] = x[i];);
3361 int req_counter = 0;
3382 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3384 gtopo.
GetComm(), &requests[req_counter++]);
3391 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3393 gtopo.
GetComm(), &requests[req_counter++]);
3400 MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
3431 static void AddSubVector(
const Array<int> &unique_dst_indices,
3438 const auto x = src.
Read();
3439 const auto DST_I = unique_dst_indices.
Read();
3440 const auto SRC_O = unique_to_src_offsets.
Read();
3441 const auto SRC_I = unique_to_src_indices.
Read();
3442 MFEM_FORALL(i, unique_dst_indices.
Size(),
3444 const int dst_idx = DST_I[i];
3445 double sum = y[dst_idx];
3446 const int end = SRC_O[i+1];
3447 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
3463 int req_counter = 0;
3474 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3476 gtopo.
GetComm(), &requests[req_counter++]);
3483 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3485 gtopo.
GetComm(), &requests[req_counter++]);
3492 MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
Abstract class for all finite elements.
Geometry::Type GetGeometryType() const
int GetNFaceNeighbors() const
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
void SendRebalanceDofs(int old_ndofs, const Table &old_element_dofs, long old_global_offset, FiniteElementSpace *space)
Use the communication pattern from last Rebalance() to send element DOFs.
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with ReduceBegin().
int Size() const
Return the logical size of the array.
void SetSubVector(const Array< int > &dofs, const double value)
Set the entries listed in dofs to the given value.
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root.
void GetNeighborLDofTable(Table &nbr_ldof) const
Dofs to be received from communication neighbors.
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1)
HYPRE_BigInt GetMyTDofOffset() const
int GetNDofs() const
Returns number of degrees of freedom.
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
std::vector< int > CommGroup
HYPRE_BigInt GetGlobalScalarTDofNumber(int sldof)
Operator that extracts Face degrees of freedom in parallel.
int DofToVDof(int dof, int vd, int ndofs=-1) const
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T...
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
static void AddDependencies(SparseMatrix &deps, Array< int > &master_dofs, Array< int > &slave_dofs, DenseMatrix &I, int skipfirst=0)
const CommGroup & GetGroup(GroupId id) const
Return a list of ranks contained in the group of the given ID.
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
void GetNeighborLTDofTable(Table &nbr_ltdof) const
Dofs to be sent to communication neighbors.
Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
void BuildElementToDofTable() const
void AddColumnsInRow(int r, int ncol)
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const
Determine the boundary degrees of freedom.
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
void SetSize(int s)
Resize the vector to size s.
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof)
void GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i'th element.
Array< Element * > face_nbr_elements
void Delete()
Delete the owned pointers. The Memory is not reset by this method, i.e. it will, generally, not be Empty() after this call.
void Create(const Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
int TrueVSize() const
Obsolete, kept for backward compatibility.
static int DecodeDof(int dof)
Helpers to remove encoded sign from a DOF.
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
int GetGroupSize(int g) const
Get the number of processors in a group.
virtual void Update(bool want_transform=true)
Pointer to an Operator of a specified type.
void SetDims(int rows, int nnz)
Operator::Type Type() const
Get the currently set operator type id.
int VDofToDof(int vdof) const
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_vdofs, int component=-1) const
Mark degrees of freedom associated with boundary elements with the specified boundary attributes (mar...
virtual double * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
int vdim
Vector dimension (number of unknowns per degree of freedom).
int Size() const
Returns the size of the vector.
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes, defined on a coarse mesh, to this FE space, defined on a refined mesh.
int GetNE() const
Returns number of elements.
int GetEdgeDofs(int edge, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
void DivideByGroupSize(double *vec)
Scale a vector of true dofs.
void LoseData()
Call this if data has been stolen.
Abstract parallel finite element space.
bool operator<(const Pair< A, B > &p, const Pair< A, B > &q)
Comparison operator for class Pair, based on the first element only.
void Synchronize(Array< int > &ldof_marker) const
Given an integer array on the local degrees of freedom, perform a bitwise OR between the shared dofs...
virtual void GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'.
void Lose_Dof_TrueDof_Matrix()
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
int GetLocalTDofNumber(int ldof) const
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
const FiniteElementCollection * fec
Associated FE collection (not owned).
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
virtual int GetFaceDofs(int face, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
int GroupNQuadrilaterals(int group)
Geometry::Type GetElementBaseGeometry(int i) const
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
int Size_of_connections() const
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
void DeleteAll()
Delete the whole array.
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
void AddConnections(int r, const int *c, int nc)
void GroupTriangle(int group, int i, int &face, int &o)
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with BcastBegin().
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
virtual int DofForGeometry(Geometry::Type GeomType) const =0
virtual int GetTrueVSize() const
Return the number of local vector true dofs.
virtual void GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i'th boundary element.
int GroupVertex(int group, int i)
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
int uni_fdof
of single face DOFs if all faces uniform; -1 otherwise
void GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
ID for class SparseMatrix.
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
const FiniteElement * GetFaceNbrFE(int i) const
ID for the base class Operator, i.e. any type.
Array< int > face_nbr_elements_offset
void ExchangeFaceNbrData()
double f(const Vector &xvec)
void write(std::ostream &os, T value)
Write 'value' to stream.
virtual const FiniteElement * GetFE(int i) const
static const int Dimension[NumGeom]
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
int Append(const T &el)
Append element 'el' to array, resize if necessary.
GroupId GetEntityGroupId(int entity, int index)
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
Memory< int > & GetJMemory()
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
MPI_Comm GetComm() const
Return the MPI communicator.
int GetNGhostEdges() const
void AddConnection(int r, int c)
void LoseData()
NULL-ifies the data.
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
const int * HostReadJ() const
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
void CopyFrom(const U *src)
Copy from src into this array. Copies enough entries to fill the Capacity size of this array...
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
HypreParMatrix * Transpose() const
Returns the transpose of *this.
T read(std::istream &is)
Read a value from the stream and return it.
HYPRE_BigInt GetMyDofOffset() const
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
int GroupNVertices(int group)
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
virtual void GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'.
int Size() const
Returns the number of TYPE I elements.
GroupTopology & GetGroupTopology()
Get a reference to the associated GroupTopology object.
HYPRE_BigInt * GetDofOffsets() const
virtual const double * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
Operator * Ptr() const
Access the underlying Operator pointer.
int GetEntityDofs(int entity, int index, Array< int > &dofs, Geometry::Type master_geom=Geometry::INVALID, int variant=0) const
Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
int FirstFaceDof(int face, int variant=0) const
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor...
void GetTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer GridFunction data from coarse_fes...
bool Finalized() const
Returns whether or not CSR format has been finalized.
void GroupQuadrilateral(int group, int i, int &face, int &o)
HYPRE_BigInt * GetTrueDofOffsets() const
int GroupNTriangles(int group)
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
int GetNGhostFaces() const
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
virtual const int * DofOrderForOrientation(Geometry::Type GeomType, int Or) const =0
Returns an array, say p, that maps a local permuted index i to a local base index: base_i = p[i]...
void Swap(Array< T > &, Array< T > &)
double p(const Vector &x, double t)
Memory< int > & GetIMemory()
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
int GetDof() const
Returns the number of degrees of freedom in the finite element.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
void AddAColumnInRow(int r)
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
GroupId GetEntityOwnerId(int entity, int index)
Return vertex/edge/face ('entity' == 0/1/2, resp.) owner.
ParFiniteElementSpace(const ParFiniteElementSpace &orig, ParMesh *pmesh=NULL, const FiniteElementCollection *fec=NULL)
Copy constructor: deep copy all data from orig except the ParMesh, the FiniteElementCollection, and some derived data.
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
Linear2DFiniteElement TriangleFE
int height
Dimension of the output / number of rows in the matrix.
void BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root.
Operation GetLastOperation() const
Return type of last modification of the mesh.
bool Nonconforming() const
Array< HYPRE_BigInt > face_nbr_glob_dof_map
NURBSExtension * NURBSext
Optional NURBS mesh extension.
virtual const Operator * GetRestrictionOperator() const
virtual void GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i'th element.
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Table face_nbr_element_dof
Mesh * mesh
The mesh that FE space lives on (not owned).
GridFunction interpolation operator applicable after mesh refinement.
void PrintPartitionStats()
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
virtual const SparseMatrix * GetRestrictionMatrix() const
Get the R matrix which restricts a local dof vector to true dof vector.
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
virtual int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
General triple product operator x -> A*B*C*x, with ownership of the factors.
Array< MeshId > conforming
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) const
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
int index(int i, int j, int nx, int ny)
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
const FiniteElement * GetFaceNbrFaceFE(int i) const
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root.
virtual const Operator * GetRestrictionTransposeOperator() const
Return logical transpose of restriction matrix, but in non-assembled optimized matrix-free form...
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
Geometry::Type GetFaceGeometryType(int Face) const
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
void MakeRef(T *, int)
Make this Array a reference to a pointer.
void GroupEdge(int group, int i, int &edge, int &o)
BiLinear2DFiniteElement QuadrilateralFE
const int * HostReadI() const
Identity Operator I: x -> x.
ID for class HypreParMatrix.
bool UseDevice() const
Read the internal device flag.
static bool DofFinalizable(int dof, const Array< bool > &finalized, const SparseMatrix &deps)
Biwise-OR of all device backends.
NURBSExtension * NURBSext
int GetNGhostVertices() const
Table send_face_nbr_elements
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Base class for operators that extracts Face degrees of freedom.
Wrapper for hypre's ParCSR matrix class.
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
Class for parallel meshes.
Abstract data type element.
int GetFaceNbrRank(int fn) const
Linear1DFiniteElement SegmentFE
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
int GroupNEdges(int group)
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const