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);
90void ParFiniteElementSpace::ParInit(ParMesh *pm)
113 MFEM_ASSERT(
own_ext,
"internal error");
115 ParNURBSExtension *pNe =
new ParNURBSExtension(
134void ParFiniteElementSpace::Construct()
137 " for ParFiniteElementSpace yet.");
141 ConstructTrueNURBSDofs();
142 GenerateGlobalOffsets();
147 GenerateGlobalOffsets();
160 ngedofs = ngfdofs = 0;
180 ngdofs = ngvdofs + ngedofs + ngfdofs;
184 ltdof_size = BuildParallelConformingInterpolation(
185 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
195 long long ltdofs = ltdof_size;
196 long long min_ltdofs, max_ltdofs, sum_ltdofs;
198 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
199 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
200 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
205 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
206 <<
", avg " << std::fixed << std::setprecision(1) << avg
207 <<
", max " << max_ltdofs
208 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
216 mfem::out <<
"True DOFs by rank: " << ltdofs;
217 for (
int i = 1; i < NRanks; i++)
220 MPI_Recv(<dofs, 1, MPI_LONG_LONG, i, 123, MyComm, &status);
227 MPI_Send(<dofs, 1, MPI_LONG_LONG, 0, 123, MyComm);
232void ParFiniteElementSpace::GetGroupComm(
237 int nvd, ned, ntd = 0, nqd = 0;
240 int group_ldof_counter;
265 group_ldof_counter = 0;
266 for (gr = 1; gr < ng; gr++)
269 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
275 group_ldof_counter *=
vdim;
278 group_ldof.
SetDims(ng, group_ldof_counter);
281 group_ldof_counter = 0;
282 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
283 for (gr = 1; gr < ng; gr++)
285 int j, k, l, m, o, nv, ne, nt, nq;
296 for (j = 0; j < nv; j++)
302 for (l = 0; l < nvd; l++, m++)
312 for (l = 0; l < dofs.
Size(); l++)
314 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
322 for (j = 0; j < ne; j++)
329 for (l = 0; l < ned; l++)
333 dofs[l] = m + (-1-ind[l]);
336 (*g_ldof_sign)[dofs[l]] = -1;
341 dofs[l] = m + ind[l];
350 for (l = 0; l < dofs.
Size(); l++)
352 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
360 for (j = 0; j < nt; j++)
367 for (l = 0; l < ntd; l++)
371 dofs[l] = m + (-1-ind[l]);
374 (*g_ldof_sign)[dofs[l]] = -1;
379 dofs[l] = m + ind[l];
388 for (l = 0; l < dofs.
Size(); l++)
390 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
398 for (j = 0; j < nq; j++)
405 for (l = 0; l < nqd; l++)
409 dofs[l] = m + (-1-ind[l]);
412 (*g_ldof_sign)[dofs[l]] = -1;
417 dofs[l] = m + ind[l];
426 for (l = 0; l < dofs.
Size(); l++)
428 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
433 group_ldof.
GetI()[gr+1] = group_ldof_counter;
439void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
443 for (
int i = 0; i < dofs.Size(); i++)
447 if (ldof_sign[-1-dofs[i]] < 0)
449 dofs[i] = -1-dofs[i];
454 if (ldof_sign[dofs[i]] < 0)
456 dofs[i] = -1-dofs[i];
462void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
464 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
465 ApplyLDofSigns(all_dofs);
489 ApplyLDofSigns(dofs);
514 ApplyLDofSigns(dofs);
521 if (
face_dof !=
nullptr && variant == 0)
529 ApplyLDofSigns(dofs);
547 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
548 auto itr =
L2F.find(key);
549 if (itr !=
L2F.end())
587 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
588 pmesh->
GroupEdge(group, ei, l_edge, ori);
598 for (
int i = 0; i < dofs.
Size(); i++)
600 const int di = dofs[i];
601 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
610 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
611 "invalid triangular face index");
622 for (
int i = 0; i < dofs.
Size(); i++)
624 const int di = dofs[i];
625 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
634 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
635 "invalid quadrilateral face index");
646 for (
int i = 0; i < dofs.
Size(); i++)
648 const int di = dofs[i];
649 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
654void ParFiniteElementSpace::GenerateGlobalOffsets()
const
666 if (HYPRE_AssumedPartitionCheck())
669 GroupTopology > = GetGroupTopo();
670 int nsize = gt.GetNumNeighbors()-1;
671 MPI_Request *requests =
new MPI_Request[2*nsize];
672 MPI_Status *statuses =
new MPI_Status[2*nsize];
673 tdof_nb_offsets.
SetSize(nsize+1);
674 tdof_nb_offsets[0] = tdof_offsets[0];
677 int request_counter = 0;
678 for (
int i = 1; i <= nsize; i++)
680 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
681 gt.GetNeighborRank(i), 5365, MyComm,
682 &requests[request_counter++]);
684 for (
int i = 1; i <= nsize; i++)
686 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
687 gt.GetNeighborRank(i), 5365, MyComm,
688 &requests[request_counter++]);
690 MPI_Waitall(request_counter, requests, statuses);
697void ParFiniteElementSpace::CheckNDSTriaDofs()
700 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
721 for (
int g = 1; g < ngrps; g++)
728 int loc_nd_strias = strias ? 1 : 0;
729 int glb_nd_strias = 0;
730 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1,
731 MPI_INTEGER, MPI_SUM, MyComm);
732 nd_strias = glb_nd_strias > 0;
735void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
747 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
748 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
751 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
752 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
760 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
762 i_diag[0] = i_offd[0] = 0;
763 diag_counter = offd_counter = 0;
764 for (
int i = 0; i < ldof; i++)
769 j_diag[diag_counter++] = ltdof_i;
774 cmap_j_offd[offd_counter].two = offd_counter;
777 i_diag[i+1] = diag_counter;
778 i_offd[i+1] = offd_counter;
783 for (
int i = 0; i < offd_counter; i++)
785 cmap[i] = cmap_j_offd[i].one;
786 j_offd[cmap_j_offd[i].two] = i;
789 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
790 i_diag, j_diag, i_offd, j_offd,
802 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
803 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
810 Array<int> ldsize(ldof); ldsize = 0;
811 Array<int> ltori(ldof); ltori = 0;
816 for (
int g = 1; g < ngrps; g++)
825 for (
int i=0; i<sdofs.Size(); i++)
827 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
828 if (ldsize[ind] == 0) { nnz_offd++; }
834 int face, ori, info1, info2;
838 for (
int i=0; i<3*
nedofs; i++)
840 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
841 if (ldsize[ind] == 0) { nnz_offd++; }
844 for (
int i=3*
nedofs; i<sdofs.Size(); i++)
846 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
847 ldsize[sdofs[i]] = 2;
848 ltori[sdofs[i]] = info2 % 64;
854 for (
int i=0; i<sdofs.Size(); i++)
856 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
857 if (ldsize[ind] == 0) { nnz_offd++; }
864 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
865 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
866 real_t *d_diag = Memory<real_t>(ltdof);
869 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
870 HYPRE_Int *j_offd = Memory<HYPRE_Int>(nnz_offd);
871 real_t *d_offd = Memory<real_t>(nnz_offd);
879 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
881 i_diag[0] = i_offd[0] = 0;
882 diag_counter = offd_counter = 0;
883 int offd_col_counter = 0;
884 for (
int i = 0; i < ldof; i++)
889 j_diag[diag_counter] = ltdofi;
890 d_diag[diag_counter++] = 1.0;
897 cmap_j_offd[offd_col_counter].two = offd_counter;
904 cmap_j_offd[offd_col_counter].two = offd_counter;
907 i_diag[i+1] = diag_counter;
908 i_offd[i+1] = offd_counter;
911 cmap_j_offd[offd_col_counter].two = offd_counter;
916 i_diag[i+1] = diag_counter;
917 i_offd[i+1] = offd_counter;
922 for (
int i = 0; i < nnz_offd; i++)
928 for (
int i = 0; i < offd_col_counter; i++)
930 cmap[i] = cmap_j_offd[i].one;
931 j_offd[cmap_j_offd[i].two] = i;
934 for (
int i = 0; i < ldof; i++)
936 if (i_offd[i+1] == i_offd[i] + 1)
938 d_offd[i_offd[i]] = 1.0;
940 else if (i_offd[i+1] == i_offd[i] + 2)
944 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
945 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
947 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
948 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
949 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
953 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
954 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
955 offd_col_counter, cmap);
967 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
968 P_pc_col_starts, NULL,
true);
977 for (
int i = 0; i < ldof_group.
Size(); i++)
981 if (ldof_ltdof[i] >= 0)
999 gc->
Create(pNURBSext()->ldof_group);
1003 GetGroupComm(*gc, 0);
1013 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1017 gcomm->
Bcast(ldof_marker);
1022 int component)
const
1034 int component)
const
1046 const int *ess_dofs_data = ess_dofs.
HostRead();
1047 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1049 const int *ted = true_ess_dofs.
HostRead();
1050 std::string error_msg =
"failed dof: ";
1051 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1053 if (
bool(ted[i]) !=
bool(true_ess_dofs2[i]))
1055 error_msg += std::to_string(i) +=
"(R ";
1056 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1057 error_msg += std::to_string(
bool(true_ess_dofs2[i])) +=
") ";
1063 MFEM_ASSERT(R->
Width() == ess_dofs.
Size(),
"!");
1064 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1065 <<
", rank = " << MyRank <<
", " << error_msg);
1077 return ldof_ltdof[ldof];
1081 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1083 return ldof_ltdof[ldof];
1096 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1102 if (HYPRE_AssumedPartitionCheck())
1104 return ldof_ltdof[ldof] +
1105 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1109 return ldof_ltdof[ldof] +
1119 MFEM_ABORT(
"Not implemented for NC mesh.");
1122 if (HYPRE_AssumedPartitionCheck())
1126 return ldof_ltdof[sldof] +
1128 ldof_group[sldof])] /
vdim;
1132 return (ldof_ltdof[sldof*
vdim] +
1133 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1140 return ldof_ltdof[sldof] +
1142 ldof_group[sldof])] /
vdim;
1146 return (ldof_ltdof[sldof*
vdim] +
1147 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1154 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1159 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1166 if (Pconf) {
return Pconf; }
1197 if (Rconf) {
return Rconf; }
1234 if (num_face_nbrs == 0)
1240 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1241 MPI_Request *send_requests = requests;
1242 MPI_Request *recv_requests = requests + num_face_nbrs;
1243 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1249 Table send_nbr_elem_dof;
1256 for (
int fn = 0; fn < num_face_nbrs; fn++)
1261 for (
int i = 0; i < num_my_elems; i++)
1264 for (
int j = 0; j < ldofs.
Size(); j++)
1266 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1268 if (ldof_marker[ldof] != fn)
1270 ldof_marker[ldof] = fn;
1280 MyComm, &send_requests[fn]);
1283 MyComm, &recv_requests[fn]);
1286 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1291 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1299 int *send_I = send_nbr_elem_dof.
GetI();
1301 for (
int fn = 0; fn < num_face_nbrs; fn++)
1305 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1306 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1308 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1309 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1312 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1313 send_nbr_elem_dof.
MakeJ();
1317 for (
int fn = 0; fn < num_face_nbrs; fn++)
1322 for (
int i = 0; i < num_my_elems; i++)
1325 for (
int j = 0; j < ldofs.
Size(); j++)
1327 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1329 if (ldof_marker[ldof] != fn)
1331 ldof_marker[ldof] = fn;
1336 send_el_off[fn] + i, ldofs, ldofs.
Size());
1343 int *send_J = send_nbr_elem_dof.
GetJ();
1344 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1348 int j_end = send_I[send_el_off[fn+1]];
1350 for (
int i = 0; i < num_ldofs; i++)
1352 int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
1353 ldof_marker[ldof] = i;
1356 for ( ; j < j_end; j++)
1358 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1359 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1363 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1370 for (
int fn = 0; fn < num_face_nbrs; fn++)
1375 MPI_Isend(send_J + send_I[send_el_off[fn]],
1376 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1377 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1379 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1380 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1381 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1384 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1387 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1390 int j_end = recv_I[recv_el_off[fn+1]];
1392 for ( ; j < j_end; j++)
1405 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1409 for (
int fn = 0; fn < num_face_nbrs; fn++)
1416 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1420 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1423 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1424 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1431 for (
int fn = 0; fn < num_face_nbrs; fn++)
1436 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1437 MyComm, &send_requests[fn]);
1439 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1440 MyComm, &recv_requests[fn]);
1443 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1447 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1461 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1497 int el1, el2, inf1, inf2;
1510 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1512 for (
int j = 0; j < vdofs.
Size(); j++)
1514 const int ldof = vdofs[j];
1515 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1527 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
1528 " does not support NURBS!");
1548#if MFEM_HYPRE_VERSION <= 22200
1549 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1550 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1551 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1560void ParFiniteElementSpace::ConstructTrueDofs()
1567 GetGroupComm(*gcomm, 1, &ldof_sign);
1577 for (gr = 1; gr < group_ldof.
Size(); gr++)
1579 const int *ldofs = group_ldof.
GetRow(gr);
1580 const int nldofs = group_ldof.
RowSize(gr);
1581 for (i = 0; i < nldofs; i++)
1583 ldof_group[ldofs[i]] = gr;
1588 for (i = 0; i < nldofs; i++)
1590 ldof_ltdof[ldofs[i]] = -2;
1597 for (i = 0; i < n; i++)
1599 if (ldof_ltdof[i] == -1)
1601 ldof_ltdof[i] = ltdof_size++;
1607 gcomm->
Bcast(ldof_ltdof);
1610void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1613 GroupTopology > = pNURBSext()->
gtopo;
1614 gcomm =
new GroupCommunicator(gt);
1619 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1623 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1625 for (
int i = 0; i < n; i++)
1627 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1631 gcomm->
Create(ldof_group);
1639 for (
int i = 0; i < n; i++)
1641 if (gt.IAmMaster(ldof_group[i]))
1643 ldof_ltdof[i] = ltdof_size;
1654 gcomm->
Bcast(ldof_ltdof);
1657void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1658 Array<int> &dofs)
const
1662 for (
int j = 0; j < nv; j++)
1664 dofs[j] =
ndofs + nv*
id.index + j;
1668void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1669 Array<int> &dofs)
const
1673 dofs.SetSize(2*nv + ne);
1678 for (
int i = 0; i < 2; i++)
1680 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1681 for (
int j = 0; j < nv; j++)
1683 dofs[i*nv + j] = k++;
1687 int k =
ndofs + ngvdofs + (edge_id.index - pncmesh->
GetNEdges())*ne;
1688 for (
int j = 0; j < ne; j++)
1690 dofs[2*nv + j] = k++;
1694void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1695 Array<int> &dofs)
const
1697 int nfv, V[4], E[4], Eo[4];
1704 int nf = (nfv == 3) ? nf_tri : nf_quad;
1706 dofs.SetSize(nfv*(nv + ne) + nf);
1709 for (
int i = 0; i < nfv; i++)
1712 int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1713 for (
int j = 0; j < nv; j++)
1715 dofs[offset++] = first + j;
1719 for (
int i = 0; i < nfv; i++)
1722 int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
1723 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
1725 for (
int j = 0; j < ne; j++)
1727 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
1728 : (-1 - (first + (-1 - ind[j])));
1732 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1733 int first =
ndofs + ngvdofs + ngedofs + nf_quad*ghost_face_index;
1735 for (
int j = 0; j < nf; j++)
1737 dofs[offset++] = first + j;
1741void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
1742 Array<int> &dofs)
const
1747 case 0: GetGhostVertexDofs(
id, dofs);
break;
1748 case 1: GetGhostEdgeDofs(
id, dofs);
break;
1749 case 2: GetGhostFaceDofs(
id, dofs);
break;
1753void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
1754 Array<int> &dofs)
const
1756 int ned, ghost, first;
1762 first = (
index < ghost)
1770 first = (
index < ghost)
1791 first =
ndofs + ngvdofs + ngedofs +
index*stride;
1797 for (
int i = 0; i < ned; i++)
1799 dofs[i] = first + i;
1803int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof)
const
1815 return (
index < ghost)
1823 return (
index < ghost)
1825 :
ndofs + ngvdofs + (
index - ghost)*ned + edof;
1839 return ndofs + ngvdofs + ngedofs +
index*stride + edof;
1844static int bisect(
const int* array,
int size,
int value)
1846 const int* end = array + size;
1847 const int* pos = std::upper_bound(array, end, value);
1848 MFEM_VERIFY(pos != array,
"value not found");
1851 MFEM_VERIFY(*(array+size - 1) == value,
"Last entry must be exact")
1853 return pos - array - 1;
1859void ParFiniteElementSpace::UnpackDof(
int dof,
1860 int &entity,
int &
index,
int &edof)
const
1862 MFEM_VERIFY(dof >= 0,
"");
1868 entity = 0,
index = dof / nv, edof = dof % nv;
1875 entity = 1,
index = dof / ne, edof = dof % ne;
1884 index = dof / nf, edof = dof % nf;
1890 MFEM_ASSERT(table.Size() > 0,
"");
1891 int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
1892 index = bisect(table.GetI(), table.Size(), jpos);
1893 edof = dof - table.GetRow(
index)[0];
1898 MFEM_ABORT(
"Cannot unpack internal DOF");
1913 entity = 1,
index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
1920 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
1924 MFEM_ABORT(
"Out of range DOF.");
1932struct PMatrixElement
1939 : column(col), stride(str), value(val) {}
1941 bool operator<(
const PMatrixElement &other)
const
1942 {
return column < other.column; }
1944 typedef std::vector<PMatrixElement> List;
1952 PMatrixElement::List elems;
1955 void AddRow(
const PMatrixRow &other,
real_t coef)
1957 elems.reserve(elems.size() + other.elems.size());
1958 for (
const PMatrixElement &oei : other.elems)
1960 elems.emplace_back(oei.column, oei.stride, coef * oei.value);
1967 if (!elems.size()) {
return; }
1968 std::sort(elems.begin(), elems.end());
1971 for (
unsigned i = 1; i < elems.size(); i++)
1973 if (elems[j].column == elems[i].column)
1975 elems[j].value += elems[i].value;
1979 elems[++j] = elems[i];
1985 void write(std::ostream &os,
real_t sign)
const
1988 for (
unsigned i = 0; i < elems.size(); i++)
1990 const PMatrixElement &e = elems[i];
1997 void read(std::istream &is,
real_t sign)
2000 for (
unsigned i = 0; i < elems.size(); i++)
2002 PMatrixElement &e = elems[i];
2013class NeighborRowMessage :
public VarMessage<314>
2016 typedef NCMesh::MeshId MeshId;
2020 int entity,
index, edof;
2024 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row)
2025 : entity(ent),
index(idx), edof(edof), group(grp), row(row) {}
2027 RowInfo(
int ent,
int idx,
int edof, GroupId grp)
2028 : entity(ent),
index(idx), edof(edof), group(grp) {}
2031 NeighborRowMessage() : pncmesh(NULL) {}
2033 void AddRow(
int entity,
int index,
int edof, GroupId group,
2034 const PMatrixRow &row)
2036 rows.emplace_back(entity,
index, edof, group, row);
2039 const std::vector<RowInfo>& GetRows()
const {
return rows; }
2041 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2042 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
2044 typedef std::map<int, NeighborRowMessage> Map;
2047 std::vector<RowInfo> rows;
2050 const FiniteElementCollection* fec;
2053 void Encode(
int rank)
override;
2055 void Decode(
int rank)
override;
2058void NeighborRowMessage::Encode(
int rank)
2060 std::ostringstream stream;
2062 Array<MeshId> ent_ids[3];
2063 Array<GroupId> group_ids[3];
2064 Array<int> row_idx[3];
2067 for (
unsigned i = 0; i < rows.size(); i++)
2069 const RowInfo &ri = rows[i];
2071 ent_ids[ri.entity].Append(
id);
2072 row_idx[ri.entity].Append(i);
2073 group_ids[ri.entity].Append(ri.group);
2076 Array<GroupId> all_group_ids;
2077 all_group_ids.Reserve(rows.size());
2078 for (
int i = 0; i < 3; i++)
2080 all_group_ids.Append(group_ids[i]);
2088 for (
int ent = 0; ent < 3; ent++)
2090 const Array<MeshId> &ids = ent_ids[ent];
2091 for (
int i = 0; i < ids.Size(); i++)
2093 const MeshId &
id = ids[i];
2094 const RowInfo &ri = rows[row_idx[ent][i]];
2095 MFEM_ASSERT(ent == ri.entity,
"");
2097#ifdef MFEM_DEBUG_PMATRIX
2099 <<
": ent " << ri.entity <<
", index " << ri.index
2100 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
2101 << int(
id.local) <<
")" << std::endl;
2111 if ((edof = ind[edof]) < 0)
2119 ri.row.write(stream,
s);
2124 stream.str().swap(
data);
2127void NeighborRowMessage::Decode(
int rank)
2129 std::istringstream stream(
data);
2131 Array<MeshId> ent_ids[3];
2132 Array<GroupId> group_ids;
2138 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2139 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2142 rows.reserve(nrows);
2145 for (
int ent = 0, gi = 0; ent < 3; ent++)
2148 const Array<MeshId> &ids = ent_ids[ent];
2149 for (
int i = 0; i < ids.Size(); i++)
2151 const MeshId &
id = ids[i];
2160 const int *ind =
nullptr;
2176 const bool process_dof_pairs = (ent == 2 &&
2180#ifdef MFEM_DEBUG_PMATRIX
2182 <<
": ent " << ent <<
", index " <<
id.index
2183 <<
", edof " << edof <<
" (id " <<
id.element <<
"/"
2184 << int(
id.local) <<
")" << std::endl;
2188 real_t s = (edof < 0) ? -1.0 : 1.0;
2189 edof = (edof < 0) ? -1 - edof : edof;
2190 if (ind && (edof = ind[edof]) < 0)
2198 rows.emplace_back(ent,
id.
index, edof, group_ids[gi++]);
2199 rows.back().row.read(stream,
s);
2201#ifdef MFEM_DEBUG_PMATRIX
2203 <<
": ent " << rows.back().entity <<
", index "
2204 << rows.back().index <<
", edof " << rows.back().edof
2208 if (process_dof_pairs)
2228 auto &first_row = rows.back().row;
2230 const auto initial_first_row = first_row;
2233 const MeshId &next_id = ids[++i];
2239 s = (edof < 0) ? -1.0 : 1.0;
2240 edof = (edof < 0) ? -1 - edof : edof;
2241 if (ind && (edof = ind[edof]) < 0)
2247 rows.emplace_back(ent, next_id.index, edof, group_ids[gi++]);
2248 rows.back().row.read(stream,
s);
2249 auto &second_row = rows.back().row;
2252 const auto initial_second_row = second_row;
2270 MFEM_ASSERT(fo != 2 &&
2271 fo != 4,
"This code branch is ambiguous for face orientations 2 and 4."
2272 " Please report this mesh for further testing.\n");
2274 first_row.AddRow(initial_first_row, T[0] - 1.0);
2275 first_row.AddRow(initial_second_row, T[2]);
2276 second_row.AddRow(initial_first_row, T[1]);
2277 second_row.AddRow(initial_second_row, T[3] - 1.0);
2279 first_row.Collapse();
2280 second_row.Collapse();
2287ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
2289 NeighborRowMessage::Map &send_msg)
const
2292 UnpackDof(dof, ent, idx, edof);
2294 for (
const auto &rank : pncmesh->GetGroup(group_id))
2298 NeighborRowMessage &msg = send_msg[rank];
2299 msg.AddRow(ent, idx, edof, group_id, row);
2300 msg.SetNCMesh(pncmesh);
2302#ifdef MFEM_PMATRIX_STATS
2309void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
2310 GroupId group_sent_id, GroupId group_id,
2311 NeighborRowMessage::Map &send_msg)
const
2314 UnpackDof(dof, ent, idx, edof);
2317 for (
unsigned i = 0; i < group.size(); i++)
2319 int rank = group[i];
2320 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
2322 NeighborRowMessage &msg = send_msg[rank];
2323 GroupId invalid = -1;
2324 msg.AddRow(ent, idx, edof, invalid, row);
2325 msg.SetNCMesh(pncmesh);
2327#ifdef MFEM_PMATRIX_STATS
2330#ifdef MFEM_DEBUG_PMATRIX
2332 << rank <<
": ent " << ent <<
", index" << idx
2333 <<
", edof " << edof << std::endl;
2339#ifdef MFEM_DEBUG_PMATRIX
2340void ParFiniteElementSpace
2341::DebugDumpDOFs(std::ostream &os,
2342 const SparseMatrix &deps,
2343 const Array<GroupId> &dof_group,
2344 const Array<GroupId> &dof_owner,
2345 const Array<bool> &finalized)
const
2347 for (
int i = 0; i < dof_group.Size(); i++)
2350 if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
2353 UnpackDof(i, ent, idx, edof);
2355 os << edof <<
" @ ";
2356 if (i > ndofs) { os <<
"ghost "; }
2359 case 0: os <<
"vertex ";
break;
2360 case 1: os <<
"edge ";
break;
2361 default: os <<
"face ";
break;
2365 if (i < deps.Height() && deps.RowSize(i))
2367 os <<
"depends on ";
2368 for (
int j = 0; j < deps.RowSize(i); j++)
2370 os << deps.GetRowColumns(i)[j] <<
" ("
2371 << deps.GetRowEntries(i)[j] <<
")";
2372 if (j < deps.RowSize(i)-1) { os <<
", "; }
2381 os <<
"group " << dof_group[i] <<
" (";
2383 for (
unsigned j = 0; j < g.size(); j++)
2385 if (j) { os <<
", "; }
2389 os <<
"), owner " << dof_owner[i] <<
" (rank "
2390 << pncmesh->GetGroup(dof_owner[i])[0] <<
"); "
2391 << (finalized[i] ?
"finalized" :
"NOT finalized");
2402int ParFiniteElementSpace
2403::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
2404 Array<HYPRE_BigInt> &dof_offs,
2405 Array<HYPRE_BigInt> &tdof_offs,
2406 Array<int> *dof_tdof,
2409 const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
2411#ifdef MFEM_PMATRIX_STATS
2412 n_msgs_sent = n_msgs_recv = 0;
2413 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
2418 const int total_dofs = ndofs + ngdofs;
2419 SparseMatrix deps(ndofs, total_dofs);
2421 if (!dg && !partial)
2423 Array<int> master_dofs, slave_dofs;
2426 for (
int entity = 0; entity <= 2; entity++)
2428 const NCMesh::NCList &list = pncmesh->GetNCList(entity);
2429 if (list.masters.Size() == 0) {
continue; }
2431 IsoparametricTransformation T;
2435 for (
const auto &mf : list.masters)
2438 if (pncmesh->IsGhost(entity, mf.index))
2440 GetGhostDofs(entity, mf, master_dofs);
2444 GetEntityDofs(entity, mf.index, master_dofs, mf.Geom());
2447 if (master_dofs.Size() == 0) {
continue; }
2449 const FiniteElement *
const fe = fec->FiniteElementForGeometry(mf.Geom());
2450 if (fe ==
nullptr) {
continue; }
2457 default: MFEM_ABORT(
"unsupported geometry");
2461 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
2463 const NCMesh::Slave &sf = list.slaves[si];
2464 if (pncmesh->IsGhost(entity, sf.index)) {
continue; }
2466 constexpr int variant = 0;
2467 GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
2468 if (!slave_dofs.Size()) {
continue; }
2470 list.OrientedPointMatrix(sf, T.GetPointMat());
2471 fe->GetLocalInterpolation(T, I);
2474 AddDependencies(deps, master_dofs, slave_dofs, I);
2483 Array<GroupId> dof_group(total_dofs);
2484 Array<GroupId> dof_owner(total_dofs);
2492 auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
2493 this](
int entity,
const MeshId &id)
2495 if (
id.
index < 0) {
return; }
2497 GroupId owner = pncmesh->GetEntityOwnerId(entity,
id.
index);
2498 GroupId group = pncmesh->GetEntityGroupId(entity,
id.
index);
2500 GetBareDofs(entity,
id.
index, dofs);
2502 for (
auto dof : dofs)
2504 dof_owner[dof] = owner;
2505 dof_group[dof] = group;
2510 for (
int entity : {0,1,2})
2512 for (
const auto &
id : pncmesh->GetNCList(entity).conforming)
2514 initialize_group_and_owner(entity,
id);
2516 for (
const auto &
id : pncmesh->GetNCList(entity).masters)
2518 initialize_group_and_owner(entity,
id);
2520 for (
const auto &
id : pncmesh->GetNCList(entity).slaves)
2522 initialize_group_and_owner(entity,
id);
2529 Array<bool> finalized(total_dofs);
2533 int num_true_dofs = 0;
2534 for (
int i = 0; i < ndofs; ++i)
2536 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
2539 finalized[i] =
true;
2543#ifdef MFEM_DEBUG_PMATRIX
2545 auto dof_diagnostics = [&](
int dof,
bool print_diagnostic)
2547 const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
2548 std::stringstream msg;
2549 msg << std::boolalpha;
2551 <<
" owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] <<
" CommGroup {";
2552 for (
const auto &x : comm_group)
2556 msg <<
"} finalized " << finalized[dof];
2562 deps.GetRow(dof, cols, row);
2563 msg <<
" deps cols {";
2564 for (
const auto &x : cols)
2571 int entity,
index, edof;
2572 UnpackDof(dof, entity,
index, edof);
2573 msg <<
" entity " << entity <<
" index " <<
index <<
" edof " << edof;
2579 HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
2580 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
2581 pmesh->GenerateOffsets(2, loc_sizes, offsets);
2584 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
2589 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
2593 dof_tdof->SetSize(ndofs*vdim);
2597 std::vector<PMatrixRow> pmatrix(total_dofs);
2600 const int vdim_factor = bynodes ? 1 : vdim;
2601 const int dof_stride = bynodes ? ndofs : 1;
2602 const int tdof_stride = bynodes ? num_true_dofs : 1;
2605 std::list<NeighborRowMessage::Map> send_msg;
2606 send_msg.emplace_back();
2609 for (
int dof = 0, tdof = 0; dof < ndofs; dof++)
2613 pmatrix[dof].elems.emplace_back(
2614 my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.);
2617 if (dof_group[dof] != 0)
2619 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
2622 for (
int vd = 0; vd < vdim; vd++)
2624 const int vdof = dof*vdim_factor + vd*dof_stride;
2625 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
2627 if (R_) { (*R_)->Add(vtdof, vdof, 1.0); }
2628 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
2636#ifdef MFEM_PMATRIX_STATS
2637 n_msgs_sent += send_msg.back().size();
2640 if (R_) { (*R_)->Finalize(); }
2645 NeighborRowMessage recv_msg;
2646 recv_msg.SetNCMesh(pncmesh);
2647 recv_msg.SetFEC(fec);
2649 int num_finalized = num_true_dofs;
2651 buffer.elems.reserve(1024);
2653 while (num_finalized < ndofs)
2656 if (send_msg.back().size())
2658 send_msg.emplace_back();
2665 recv_msg.Recv(rank, size, MyComm);
2666#ifdef MFEM_PMATRIX_STATS
2668 n_rows_recv += recv_msg.GetRows().size();
2671 for (
const auto &ri : recv_msg.GetRows())
2673 const int dof = PackDof(ri.entity, ri.index, ri.edof);
2674 pmatrix[dof] = ri.row;
2676 if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
2677 finalized[dof] =
true;
2679 if (ri.group >= 0 && dof_group[dof] != ri.group)
2682 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
2692 for (
int dof = 0; dof < ndofs; dof++)
2694 const bool owned = (dof_owner[dof] == 0);
2697 && DofFinalizable(dof, finalized, deps))
2700 UnpackDof(dof, ent, idx, edof);
2702 const int* dep_col = deps.GetRowColumns(dof);
2703 const real_t* dep_coef = deps.GetRowEntries(dof);
2704 int num_dep = deps.RowSize(dof);
2707 buffer.elems.clear();
2708 for (
int j = 0; j < num_dep; j++)
2710 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
2713 pmatrix[dof] = buffer;
2715 finalized[dof] =
true;
2720 const bool shared = (dof_group[dof] != 0);
2723 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
2730#ifdef MFEM_DEBUG_PMATRIX
2731 static int dump = 0;
2735 snprintf(fname, 100,
"dofs%02d.txt", MyRank);
2736 std::ofstream
f(fname);
2737 DebugDumpDOFs(
f, deps, dof_group, dof_owner, finalized);
2744#ifdef MFEM_PMATRIX_STATS
2745 n_msgs_sent += send_msg.back().size();
2751 *P_ = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
2752 dof_offs, tdof_offs);
2760 recv_msg.RecvDrop(rank, size, MyComm);
2764 for (
auto &msg : send_msg)
2769#ifdef MFEM_PMATRIX_STATS
2770 int n_rounds = send_msg.size();
2771 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
2772 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
2774 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
2775 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2776 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2777 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2778 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2779 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
2783 mfem::out <<
"P matrix stats (avg per rank): "
2784 <<
real_t(glob_rounds)/NRanks <<
" rounds, "
2785 <<
real_t(glob_msgs_sent)/NRanks <<
" msgs sent, "
2786 <<
real_t(glob_msgs_recv)/NRanks <<
" msgs recv, "
2787 <<
real_t(glob_rows_sent)/NRanks <<
" rows sent, "
2788 <<
real_t(glob_rows_recv)/NRanks <<
" rows recv, "
2789 <<
real_t(glob_rows_fwd)/NRanks <<
" rows forwarded."
2794 return num_true_dofs*vdim;
2798HypreParMatrix* ParFiniteElementSpace
2799::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
2800 int local_rows,
int local_cols,
2801 Array<HYPRE_BigInt> &row_starts,
2802 Array<HYPRE_BigInt> &col_starts)
const
2804 bool assumed = HYPRE_AssumedPartitionCheck();
2807 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
2808 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
2811 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
2812 std::map<HYPRE_BigInt, int> col_map;
2813 for (
int i = 0; i < local_rows; i++)
2815 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2817 const PMatrixElement &elem = rows[i].elems[j];
2819 if (col >= first_col && col < next_col)
2826 for (
int vd = 0; vd < vdim; vd++)
2836 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(col_map.size());
2838 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
2840 cmap[offd_col] = it->first;
2841 it->second = offd_col++;
2844 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
2845 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
2847 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
2848 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
2850 real_t *A_diag = Memory<real_t>(nnz_diag);
2851 real_t *A_offd = Memory<real_t>(nnz_offd);
2853 int vdim1 = bynodes ? vdim : 1;
2854 int vdim2 = bynodes ? 1 : vdim;
2855 int vdim_offset = bynodes ? local_cols : 1;
2858 nnz_diag = nnz_offd = 0;
2860 for (
int vd1 = 0; vd1 < vdim1; vd1++)
2862 for (
int i = 0; i < local_rows; i++)
2864 for (
int vd2 = 0; vd2 < vdim2; vd2++)
2866 I_diag[vrow] = nnz_diag;
2867 I_offd[vrow++] = nnz_offd;
2869 int vd = bynodes ? vd1 : vd2;
2870 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2872 const PMatrixElement &elem = rows[i].elems[j];
2873 if (elem.column >= first_col && elem.column < next_col)
2875 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
2876 A_diag[nnz_diag++] = elem.value;
2880 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
2881 A_offd[nnz_offd++] = elem.value;
2887 MFEM_ASSERT(vrow == vdim*local_rows,
"");
2888 I_diag[vrow] = nnz_diag;
2889 I_offd[vrow] = nnz_offd;
2891 return new HypreParMatrix(MyComm,
2892 row_starts.Last(), col_starts.Last(),
2893 row_starts.GetData(), col_starts.GetData(),
2894 I_diag, J_diag, A_diag,
2895 I_offd, J_offd, A_offd,
2896 col_map.size(), cmap);
2899template <
typename int_type>
2900static int_type* make_i_array(
int nrows)
2902 int_type *I = Memory<int_type>(nrows+1);
2903 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
2907template <
typename int_type>
2908static int_type* make_j_array(int_type* I,
int nrows)
2911 for (
int i = 0; i < nrows; i++)
2913 if (I[i] >= 0) { nnz++; }
2915 int_type *J = Memory<int_type>(nnz);
2918 for (
int i = 0, k = 0; i <= nrows; i++)
2920 int_type col = I[i];
2922 if (col >= 0) { J[k++] = col; }
2928ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
2929 const Table* old_elem_dof,
2930 const Table* old_elem_fos)
2932 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
2933 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
2934 "be called before ParFiniteElementSpace::RebalanceMatrix");
2936 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2937 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2940 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
2946 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
2947 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
2948 "Mesh::Rebalance was not called before "
2949 "ParFiniteElementSpace::RebalanceMatrix");
2952 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
2953 for (
int i = 0; i < pmesh->
GetNE(); i++)
2955 if (old_index[i] >= 0)
2957 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
2960 for (
int vd = 0; vd <
vdim; vd++)
2962 for (
int j = 0; j < dofs.Size(); j++)
2965 if (row < 0) { row = -1 - row; }
2967 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
2968 if (col < 0) { col = -1 - col; }
2975 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
2978 Array<int> new_elements;
2979 Array<long> old_remote_dofs;
2980 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
2983 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
2984 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
2987 const long* old_dofs = &old_remote_dofs[pos];
2988 pos += dofs.Size() *
vdim;
2990 for (
int vd = 0; vd <
vdim; vd++)
2992 for (
int j = 0; j < dofs.Size(); j++)
2995 if (row < 0) { row = -1 - row; }
2997 if (i_diag[row] == i_diag[row+1])
2999 i_offd[row] = old_dofs[j + vd * dofs.Size()];
3006#ifndef HYPRE_MIXEDINT
3007 HYPRE_Int *i_offd_hi = i_offd;
3010 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
3011 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
3012 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
3016 int offd_cols = i_offd_hi[vsize];
3017 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
3018 for (
int i = 0; i < offd_cols; i++)
3020 cmap_offd[i].one = j_offd[i];
3021 cmap_offd[i].two = i;
3024#ifndef HYPRE_MIXEDINT
3025 HYPRE_Int *j_offd_hi = j_offd;
3027 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
3028 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
3034 for (
int i = 0; i < offd_cols; i++)
3036 cmap[i] = cmap_offd[i].one;
3037 j_offd_hi[cmap_offd[i].two] = i;
3041 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
3042 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
3047struct DerefDofMessage
3049 std::vector<HYPRE_BigInt> dofs;
3050 MPI_Request request;
3054ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
3055 const Table* old_elem_dof,
3056 const Table *old_elem_fos)
3058 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
3060 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
3061 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
3064 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
3065 "Previous space is not finer.");
3073 Mesh::GeometryList elem_geoms(*
mesh);
3075 Array<int> dofs, old_dofs, old_vdofs;
3078 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
3085 for (
int i = 0; i < elem_geoms.Size(); i++)
3091 const CoarseFineTransformations &dtrans =
3092 old_pncmesh->GetDerefinementTransforms();
3093 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
3095 std::map<int, DerefDofMessage> messages;
3097 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
3098 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
3102 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3104 const Embedding &emb = dtrans.embeddings[k];
3106 int fine_rank = old_ranks[k];
3107 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
3108 : old_pncmesh->ElementRank(emb.parent);
3110 if (coarse_rank != MyRank && fine_rank == MyRank)
3112 old_elem_dof->GetRow(k, dofs);
3115 DerefDofMessage &msg = messages[k];
3116 msg.dofs.resize(dofs.Size());
3117 for (
int i = 0; i < dofs.Size(); i++)
3119 msg.dofs[i] = old_offset + dofs[i];
3122 MPI_Isend(&msg.dofs[0], msg.dofs.size(), HYPRE_MPI_BIG_INT,
3123 coarse_rank, 291, MyComm, &msg.request);
3125 else if (coarse_rank == MyRank && fine_rank != MyRank)
3127 MFEM_ASSERT(emb.parent >= 0,
"");
3130 DerefDofMessage &msg = messages[k];
3131 msg.dofs.resize(ldof[geom]*
vdim);
3133 MPI_Irecv(&msg.dofs[0], ldof[geom]*
vdim, HYPRE_MPI_BIG_INT,
3134 fine_rank, 291, MyComm, &msg.request);
3142 for (
int i = 0; i < elem_geoms.Size(); i++)
3148 SparseMatrix *diag =
new SparseMatrix(
ndofs*
vdim, old_ndofs*
vdim);
3150 Array<char> mark(diag->Height());
3155 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3157 const Embedding &emb = dtrans.embeddings[k];
3158 if (emb.parent < 0) {
continue; }
3160 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3161 int fine_rank = old_ranks[k];
3163 if (coarse_rank == MyRank && fine_rank == MyRank)
3166 DenseMatrix &lR = localR[geom](emb.matrix);
3169 old_elem_dof->GetRow(k, old_dofs);
3171 for (
int vd = 0; vd <
vdim; vd++)
3173 old_dofs.Copy(old_vdofs);
3176 for (
int i = 0; i < lR.Height(); i++)
3178 if (!std::isfinite(lR(i, 0))) {
continue; }
3181 int m = (r >= 0) ? r : (-1 - r);
3183 if (is_dg || !mark[m])
3186 diag->SetRow(r, old_vdofs, row);
3196 for (
auto it = messages.begin(); it != messages.end(); ++it)
3198 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
3202 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
3204 std::map<HYPRE_BigInt, int> col_map;
3205 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3207 const Embedding &emb = dtrans.embeddings[k];
3208 if (emb.parent < 0) {
continue; }
3210 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3211 int fine_rank = old_ranks[k];
3213 if (coarse_rank == MyRank && fine_rank != MyRank)
3216 DenseMatrix &lR = localR[geom](emb.matrix);
3220 DerefDofMessage &msg = messages[k];
3221 MFEM_ASSERT(msg.dofs.size(),
"");
3223 for (
int vd = 0; vd <
vdim; vd++)
3225 MFEM_ASSERT(ldof[geom],
"");
3228 for (
int i = 0; i < lR.Height(); i++)
3230 if (!std::isfinite(lR(i, 0))) {
continue; }
3233 int m = (r >= 0) ? r : (-1 - r);
3235 if (is_dg || !mark[m])
3238 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
3239 for (
int j = 0; j < ldof[geom]; j++)
3241 if (row[j] == 0.0) {
continue; }
3242 int &lcol = col_map[remote_dofs[j]];
3243 if (!lcol) { lcol = col_map.size(); }
3244 offd->_Set_(m, lcol-1, row[j]);
3255 offd->SetWidth(col_map.size());
3258 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
3259 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
3261 cmap[it->second-1] = it->first;
3268 int width = offd->Width();
3269 Array<Pair<HYPRE_BigInt, int> > reorder(width);
3270 for (
int i = 0; i < width; i++)
3272 reorder[i].one = cmap[i];
3277 Array<int> reindex(width);
3278 for (
int i = 0; i < width; i++)
3280 reindex[reorder[i].two] = i;
3281 cmap[i] = reorder[i].one;
3284 int *J = offd->GetJ();
3285 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
3287 J[i] = reindex[J[i]];
3289 offd->SortColumnIndices();
3292 HypreParMatrix* new_R;
3293 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
3294 dof_offsets, old_dof_offsets, diag, offd, cmap,
3297 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
3302void ParFiniteElementSpace::Destroy()
3313 delete Pconf; Pconf = NULL;
3314 delete Rconf; Rconf = NULL;
3317 delete gcomm; gcomm = NULL;
3326void ParFiniteElementSpace::CopyProlongationAndRestriction(
3327 const FiniteElementSpace &fes,
const Array<int> *perm)
3331 MFEM_VERIFY(pfes != NULL,
"");
3332 MFEM_VERIFY(P == NULL,
"");
3333 MFEM_VERIFY(R == NULL,
"");
3336 pfes->Dof_TrueDof_Matrix();
3338 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
3344 int n = perm->Size();
3345 perm_mat =
new SparseMatrix(n, fes.GetVSize());
3346 for (
int i=0; i<n; ++i)
3350 perm_mat->Set(i, j,
s);
3352 perm_mat->Finalize();
3356 if (pfes->P != NULL)
3359 else { P =
new HypreParMatrix(*pfes->P); }
3362 else if (perm != NULL)
3368 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
3369 col_starts, perm_mat);
3372 if (pfes->R != NULL)
3374 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
3375 else { R =
new SparseMatrix(*pfes->R); }
3377 else if (perm != NULL)
3398 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
3409 false, Tgf.OwnsOperator(),
false));
3410 Tgf.SetOperatorOwner(
false);
3417 "Parallel variable order space not supported yet.");
3425 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
3426 "each mesh modification.");
3435 Table* old_elem_dof = NULL;
3436 Table* old_elem_fos = NULL;
3447 Swap(dof_offsets, old_dof_offsets);
3468 old_elem_fos, old_ndofs));
3471 old_elem_dof = NULL;
3472 old_elem_fos = NULL;
3484 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
3490 false,
false,
true));
3497 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
3505 delete old_elem_dof;
3506 delete old_elem_fos;
3510void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
3513 MFEM_VERIFY(new_pmesh != NULL,
3514 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
3521 : gc(gc_), local(local_)
3526 for (
int g=1; g<group_ldof.
Size(); ++g)
3530 n_external += group_ldof.
RowSize(g);
3533 int tsize = lsize - n_external;
3539 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3557 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
3559 gc(pfes.GroupComm()),
3565 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3584 for ( ; j < end; j++)
3590 for ( ; j <
Height(); j++)
3608 const int in_layout = 2;
3619 for (
int i = 0; i < m; i++)
3622 std::copy(xdata+j-i, xdata+end-i, ydata+j);
3625 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
3627 const int out_layout = 0;
3650 for (
int i = 0; i < m; i++)
3653 std::copy(xdata+j, xdata+end, ydata+j-i);
3656 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
3658 const int out_layout = 2;
3668 mpi_gpu_aware(
Device::GetGPUAwareMPI())
3671 const int tdofs = R->
Height();
3672 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
3686 unique_ltdof.
Sort();
3689 for (
int i = 0; i < shared_ltdof.
Size(); i++)
3691 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
3692 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
3717 int req_counter = 0;
3722 if (send_size > 0) { req_counter++; }
3726 if (recv_size > 0) { req_counter++; }
3728 requests =
new MPI_Request[req_counter];
3734 pfes.GetRestrictionMatrix(),
3737 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
3741static void ExtractSubVector(
const Array<int> &indices,
3744 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
3745 auto y = vout.
Write();
3746 const auto x = vin.
Read();
3747 const auto I = indices.
Read();
3765static void SetSubVector(
const Array<int> &indices,
3768 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
3771 const auto x = vin.
Read();
3772 const auto I = indices.
Read();
3799 int req_counter = 0;
3838 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
3869static void AddSubVector(
const Array<int> &unique_dst_indices,
3876 const auto x = src.
Read();
3877 const auto DST_I = unique_dst_indices.
Read();
3878 const auto SRC_O = unique_to_src_offsets.
Read();
3879 const auto SRC_I = unique_to_src_indices.
Read();
3882 const int dst_idx = DST_I[i];
3884 const int end = SRC_O[i+1];
3885 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
3901 int req_counter = 0;
3930 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
void CopyFrom(const U *src)
Copy from src into this array. Copies enough entries to fill the Capacity size of this array....
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
void LoseData()
NULL-ifies the data.
int Size() const
Return the logical size of the array.
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
void DeleteAll()
Delete the whole array.
int Append(const T &el)
Append element 'el' to array, resize if necessary.
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T.
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
real_t * GetData() const
Returns the matrix data array.
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Abstract data type element.
Geometry::Type GetGeometryType() const
Base class for operators that extracts Face degrees of freedom.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
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].
@ DISCONTINUOUS
Field is discontinuous across element interfaces.
@ TANGENTIAL
Tangential components of vector field.
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
virtual int GetContType() const =0
int HasFaceDofs(Geometry::Type geom, int p) const
virtual int DofForGeometry(Geometry::Type GeomType) const =0
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
GridFunction interpolation operator applicable after mesh refinement.
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
friend void Mesh::Swap(Mesh &, bool)
DofTransformation DoFTrans
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Array< StatelessDofTransformation * > DoFTransArray
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
NURBSExtension * NURBSext
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 GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
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,...
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
const FiniteElementCollection * fec
Associated FE collection (not owned).
int VDofToDof(int vdof) const
Compute the inverse of the Dof to VDof mapping for a single index vdof.
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
int FirstFaceDof(int face, int variant=0) const
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
int vdim
Vector dimension (number of unknowns per degree of freedom).
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
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...
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
Mesh * mesh
The mesh that FE space lives on (not owned).
const FiniteElementCollection * FEColl() const
int GetMaxElementOrder() const
Return the maximum polynomial order.
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'. The returned indices are offsets in...
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
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...
int DofToVDof(int dof, int vd, int ndofs=-1) const
Compute a single vdof corresponding to the index dof and the vector index vd.
void BuildElementToDofTable() const
Abstract class for all finite elements.
int GetDof() const
Returns the number of degrees of freedom in the finite element.
static const int Dimension[NumGeom]
static bool IsTensorProduct(Type geom)
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
void GetNeighborLTDofTable(Table &nbr_ltdof) const
Dofs to be sent to communication neighbors.
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize().
void GetNeighborLDofTable(Table &nbr_ldof) const
Dofs to be received from communication neighbors.
void BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with BcastBegin().
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root.
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with ReduceBegin().
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root.
const GroupTopology & GetGroupTopology()
Get a reference to the associated GroupTopology object.
void Create(const Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
int GetGroupSize(int g) const
Get the number of processors in a group.
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor.
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
MPI_Comm GetComm() const
Return the MPI communicator.
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
Wrapper for hypre's ParCSR matrix class.
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
HypreParMatrix * LeftDiagMult(const SparseMatrix &D, HYPRE_BigInt *row_starts=NULL) const
Multiply the HypreParMatrix on the left by a block-diagonal parallel matrix D and return the result a...
Identity Operator I: x -> x.
bool UseDevice() const
Read the internal device flag.
void Delete()
Delete the owned pointers and reset the Memory object.
Operation GetLastOperation() const
Return type of last modification of the mesh.
NURBSExtension * NURBSext
Optional NURBS mesh extension.
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Geometry::Type GetBdrElementGeometry(int i) const
int GetNE() const
Returns number of elements.
int Dimension() const
Dimension of the reference space used within the elements.
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
Geometry::Type GetElementBaseGeometry(int i) const
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
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'.
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
int GetEdgeNCOrientation(const MeshId &edge_id) const
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
int GetNVertices() const
Return the number of vertices in the NCMesh.
int MyRank
used in parallel, or when loading a parallel file in serial
int GetNFaces() const
Return the number of (2D) faces in the NCMesh.
int GetNEdges() const
Return the number of edges in the NCMesh.
Pointer to an Operator of a specified type.
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Operator * Ptr() const
Access the underlying Operator pointer.
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Operator::Type Type() const
Get the currently set operator type id.
int width
Dimension of the input / number of columns in the matrix.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
int height
Dimension of the output / number of rows in the matrix.
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
@ ANY_TYPE
ID for the base class Operator, i.e. any type.
@ MFEM_SPARSEMAT
ID for class SparseMatrix.
@ Hypre_ParCSR
ID for class HypreParMatrix.
Abstract parallel finite element space.
void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const override
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes,...
HYPRE_BigInt GetGlobalScalarTDofNumber(int sldof)
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) const override
HYPRE_BigInt * GetTrueDofOffsets() const
void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const override
void PrintPartitionStats()
HYPRE_BigInt GlobalVSize() const
const Operator * GetRestrictionOperator() const override
int GetLocalTDofNumber(int ldof) const
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.
ParFiniteElementSpace(const ParFiniteElementSpace &orig, ParMesh *pmesh=NULL, const FiniteElementCollection *fec=NULL)
Copy constructor: deep copy all data from orig except the ParMesh, the FiniteElementCollection,...
void DivideByGroupSize(real_t *vec)
Scale a vector of true dofs.
void ExchangeFaceNbrData()
HYPRE_BigInt GlobalTrueVSize() const
int GetTrueVSize() const override
Return the number of local vector true dofs.
HYPRE_BigInt GetMyDofOffset() const
HYPRE_BigInt * GetDofOffsets() const
Array< HYPRE_BigInt > face_nbr_glob_dof_map
bool Nonconforming() const
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
const Operator * GetProlongationMatrix() const override
The returned Operator is owned by the FiniteElementSpace.
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const override
Determine the boundary degrees of freedom.
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
void GetFaceNbrElementVDofs(int i, Array< int > &vdofs, DofTransformation &doftrans) const
int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const override
HYPRE_BigInt GetMyTDofOffset() const
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
void GetElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetElementDofs(), but with a user-allocated DofTransformation object. doftrans must be al...
const SparseMatrix * GetRestrictionMatrix() const override
Get the R matrix which restricts a local dof vector to true dof vector.
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
void Update(bool want_transform=true) override
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
int TrueVSize() const
Obsolete, kept for backward compatibility.
void Lose_Dof_TrueDof_Matrix()
const FiniteElement * GetFaceNbrFaceFE(int i) const
const FiniteElement * GetFaceNbrFE(int i) const
const FiniteElement * GetFE(int i) const override
Table face_nbr_element_dof
void GetBdrElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetBdrElementDofs(), but with a user-allocated DofTransformation object....
Operator that extracts Face degrees of freedom in parallel.
Class for parallel meshes.
int GroupNQuadrilaterals(int group) const
Table send_face_nbr_elements
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
int GroupVertex(int group, int i) const
void GetFaceNbrElementFaces(int i, Array< int > &faces, Array< int > &orientation) const
Array< Element * > face_nbr_elements
int GroupNTriangles(int group) const
int GroupNEdges(int group) const
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
Array< int > face_nbr_elements_offset
int GetNFaceNeighbors() const
void GroupQuadrilateral(int group, int i, int &face, int &o) const
int GetFaceNbrRank(int fn) const
void GroupTriangle(int group, int i, int &face, int &o) const
void GroupEdge(int group, int i, int &edge, int &o) const
int GroupNVertices(int group) const
Operator that extracts Face degrees of freedom for NCMesh in parallel.
Operator that extracts Face degrees of freedom for NCMesh in parallel.
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.
int GetFaceOrientation(int index) const
Return (shared) face orientation relative to its owner element.
int GetNGhostEdges() const
void DecodeGroups(std::istream &is, Array< GroupId > &ids)
const CommGroup & GetGroup(GroupId id) const
Return a list of ranks contained in the group of the given ID.
int GetNGhostFaces() const
void EncodeGroups(std::ostream &os, const Array< GroupId > &ids)
void AdjustMeshIds(Array< MeshId > ids[], int rank)
std::vector< int > CommGroup
void DecodeMeshIds(std::istream &is, Array< MeshId > ids[])
void EncodeMeshIds(std::ostream &os, Array< MeshId > ids[])
int GetNGhostVertices() const
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
const int * HostReadJ() const
bool Finalized() const
Returns whether or not CSR format has been finalized.
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
const int * HostReadI() const
void LoseData()
Call this if data has been stolen.
void AddConnections(int r, const int *c, int nc)
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
void AddConnection(int r, int c)
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
int Size() const
Returns the number of TYPE I elements.
int Size_of_connections() const
void AddColumnsInRow(int r, int ncol)
Memory< int > & GetJMemory()
void AddAColumnInRow(int r)
void SetDims(int rows, int nnz)
Memory< int > & GetIMemory()
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
General triple product operator x -> A*B*C*x, with ownership of the factors.
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
int Size() const
Returns the size of the vector.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
void SetSize(int s)
Resize the vector to size s.
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
int index(int i, int j, int nx, int ny)
void write(std::ostream &os, T value)
Write 'value' to stream.
T read(std::istream &is)
Read a value from the stream and return it.
Linear1DFiniteElement SegmentFE
void mfem_error(const char *msg)
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
BiLinear2DFiniteElement QuadrilateralFE
void SortPairs(Pair< A, B > *pairs, int size)
Sort an array of Pairs with respect to the first element.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
MFEM_EXPORT Linear2DFiniteElement TriangleFE
void forall(int N, lambda &&body)
real_t p(const Vector &x, real_t t)
@ DEVICE_MASK
Biwise-OR of all device backends.
Helper struct to convert a C++ type to an MPI type.
MeshIdAndType GetMeshIdAndType(int index) const
Return a mesh id and type for a given nc index.
static void WaitAllSent(MapT &rank_msg)
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
static bool IProbe(int &rank, int &size, MPI_Comm comm)