37 ParInit(pmesh ? pmesh : orig.pmesh);
53 f ?
f : global_fes->FEColl(),
54 global_fes->GetVDim(), global_fes->GetOrdering())
73 int dim,
int ordering)
83 if (globNURBSext == NULL) {
return NULL; }
84 const ParNURBSExtension *pNURBSext =
85 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
86 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
88 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
90 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
93void ParFiniteElementSpace::ParInit(ParMesh *pm)
116 MFEM_ASSERT(
own_ext,
"internal error");
118 ParNURBSExtension *pNe =
new ParNURBSExtension(
137void ParFiniteElementSpace::CommunicateGhostOrder()
142 "Variable-order space requires a nonconforming mesh.");
149 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
150 "each mesh modification.");
160 int global_orders_changed = 0;
162 MPI_Allreduce(&local_orders_changed, &global_orders_changed, 1, MPI_INT,
165 if ((global_orders_changed == 0 && !href) || NRanks == 1)
172 Array<ParNCMesh::VarOrderElemInfo> localOrders(
mesh->
GetNE());
175 ParNCMesh::VarOrderElemInfo order_i{(
unsigned int) i,
elem_order[i]};
176 localOrders[i] = order_i;
182void ParFiniteElementSpace::Construct()
186 ConstructTrueNURBSDofs();
187 GenerateGlobalOffsets();
192 GenerateGlobalOffsets();
205 ngedofs = ngfdofs = 0;
221 const int ghostEdge = pncmesh->
GetNEdges() + i;
223 for (
int var=0; var<nvar; ++var)
248 const int ghostFace = pncmesh->
GetNFaces() + i;
250 for (
int var=0; var<nvar; ++var)
268 ngdofs = ngvdofs + ngedofs + ngfdofs;
278 ltdof_size = BuildParallelConformingInterpolation(
279 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
289 long long ltdofs = ltdof_size;
290 long long min_ltdofs, max_ltdofs, sum_ltdofs;
292 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
293 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
294 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
299 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
300 <<
", avg " << std::fixed << std::setprecision(1) << avg
301 <<
", max " << max_ltdofs
302 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
310 mfem::out <<
"True DOFs by rank: " << ltdofs;
311 for (
int i = 1; i < NRanks; i++)
314 MPI_Recv(<dofs, 1, MPI_LONG_LONG, i, 123, MyComm, &status);
321 MPI_Send(<dofs, 1, MPI_LONG_LONG, 0, 123, MyComm);
326void ParFiniteElementSpace::GetGroupComm(
331 int nvd, ned, ntd = 0, nqd = 0;
334 int group_ldof_counter;
359 group_ldof_counter = 0;
360 for (gr = 1; gr < ng; gr++)
363 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
369 group_ldof_counter *=
vdim;
372 group_ldof.
SetDims(ng, group_ldof_counter);
375 group_ldof_counter = 0;
376 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
377 for (gr = 1; gr < ng; gr++)
379 int j, k, l, m, o, nv, ne, nt, nq;
390 for (j = 0; j < nv; j++)
396 for (l = 0; l < nvd; l++, m++)
406 for (l = 0; l < dofs.
Size(); l++)
408 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
416 for (j = 0; j < ne; j++)
423 for (l = 0; l < ned; l++)
427 dofs[l] = m + (-1-ind[l]);
430 (*g_ldof_sign)[dofs[l]] = -1;
435 dofs[l] = m + ind[l];
444 for (l = 0; l < dofs.
Size(); l++)
446 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
454 for (j = 0; j < nt; j++)
461 for (l = 0; l < ntd; l++)
465 dofs[l] = m + (-1-ind[l]);
468 (*g_ldof_sign)[dofs[l]] = -1;
473 dofs[l] = m + ind[l];
482 for (l = 0; l < dofs.
Size(); l++)
484 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
492 for (j = 0; j < nq; j++)
499 for (l = 0; l < nqd; l++)
503 dofs[l] = m + (-1-ind[l]);
506 (*g_ldof_sign)[dofs[l]] = -1;
511 dofs[l] = m + ind[l];
520 for (l = 0; l < dofs.
Size(); l++)
522 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
527 group_ldof.
GetI()[gr+1] = group_ldof_counter;
533void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
537 for (
int i = 0; i < dofs.Size(); i++)
541 if (ldof_sign[-1-dofs[i]] < 0)
543 dofs[i] = -1-dofs[i];
548 if (ldof_sign[dofs[i]] < 0)
550 dofs[i] = -1-dofs[i];
556void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
558 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
559 ApplyLDofSigns(all_dofs);
585 ApplyLDofSigns(dofs);
612 ApplyLDofSigns(dofs);
619 if (
face_dof !=
nullptr && variant == 0)
627 ApplyLDofSigns(dofs);
645 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
646 auto itr =
L2F.find(key);
647 if (itr !=
L2F.end())
689 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
690 pmesh->
GroupEdge(group, ei, l_edge, ori);
700 for (
int i = 0; i < dofs.
Size(); i++)
702 const int di = dofs[i];
703 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
712 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
713 "invalid triangular face index");
724 for (
int i = 0; i < dofs.
Size(); i++)
726 const int di = dofs[i];
727 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
736 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
737 "invalid quadrilateral face index");
748 for (
int i = 0; i < dofs.
Size(); i++)
750 const int di = dofs[i];
751 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
756void ParFiniteElementSpace::GenerateGlobalOffsets()
const
768 if (HYPRE_AssumedPartitionCheck())
771 GroupTopology > = GetGroupTopo();
772 int nsize = gt.GetNumNeighbors()-1;
773 MPI_Request *requests =
new MPI_Request[2*nsize];
774 MPI_Status *statuses =
new MPI_Status[2*nsize];
775 tdof_nb_offsets.
SetSize(nsize+1);
776 tdof_nb_offsets[0] = tdof_offsets[0];
779 int request_counter = 0;
780 for (
int i = 1; i <= nsize; i++)
782 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
783 gt.GetNeighborRank(i), 5365, MyComm,
784 &requests[request_counter++]);
786 for (
int i = 1; i <= nsize; i++)
788 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
789 gt.GetNeighborRank(i), 5365, MyComm,
790 &requests[request_counter++]);
792 MPI_Waitall(request_counter, requests, statuses);
799void ParFiniteElementSpace::CheckNDSTriaDofs()
802 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
823 for (
int g = 1; g < ngrps; g++)
830 int loc_nd_strias = strias ? 1 : 0;
831 int glb_nd_strias = 0;
832 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1, MPI_INT, MPI_SUM, MyComm);
833 nd_strias = glb_nd_strias > 0;
836void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
848 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
849 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
852 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
853 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
861 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
863 i_diag[0] = i_offd[0] = 0;
864 diag_counter = offd_counter = 0;
865 for (
int i = 0; i < ldof; i++)
870 j_diag[diag_counter++] = ltdof_i;
875 cmap_j_offd[offd_counter].two = offd_counter;
878 i_diag[i+1] = diag_counter;
879 i_offd[i+1] = offd_counter;
884 for (
int i = 0; i < offd_counter; i++)
886 cmap[i] = cmap_j_offd[i].one;
887 j_offd[cmap_j_offd[i].two] = i;
890 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
891 i_diag, j_diag, i_offd, j_offd,
903 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
904 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
911 Array<int> ldsize(ldof); ldsize = 0;
912 Array<int> ltori(ldof); ltori = 0;
917 for (
int g = 1; g < ngrps; g++)
926 for (
int i=0; i<sdofs.Size(); i++)
928 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
929 if (ldsize[ind] == 0) { nnz_offd++; }
935 int face, ori, info1, info2;
939 for (
int i=0; i<3*
nedofs; i++)
941 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
942 if (ldsize[ind] == 0) { nnz_offd++; }
945 for (
int i=3*
nedofs; i<sdofs.Size(); i++)
947 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
948 ldsize[sdofs[i]] = 2;
949 ltori[sdofs[i]] = info2 % 64;
955 for (
int i=0; i<sdofs.Size(); i++)
957 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
958 if (ldsize[ind] == 0) { nnz_offd++; }
965 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
966 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
967 real_t *d_diag = Memory<real_t>(ltdof);
970 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
971 HYPRE_Int *j_offd = Memory<HYPRE_Int>(nnz_offd);
972 real_t *d_offd = Memory<real_t>(nnz_offd);
980 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
982 i_diag[0] = i_offd[0] = 0;
983 diag_counter = offd_counter = 0;
984 int offd_col_counter = 0;
985 for (
int i = 0; i < ldof; i++)
990 j_diag[diag_counter] = ltdofi;
991 d_diag[diag_counter++] = 1.0;
998 cmap_j_offd[offd_col_counter].two = offd_counter;
1005 cmap_j_offd[offd_col_counter].two = offd_counter;
1008 i_diag[i+1] = diag_counter;
1009 i_offd[i+1] = offd_counter;
1012 cmap_j_offd[offd_col_counter].two = offd_counter;
1017 i_diag[i+1] = diag_counter;
1018 i_offd[i+1] = offd_counter;
1023 for (
int i = 0; i < nnz_offd; i++)
1029 for (
int i = 0; i < offd_col_counter; i++)
1031 cmap[i] = cmap_j_offd[i].one;
1032 j_offd[cmap_j_offd[i].two] = i;
1035 for (
int i = 0; i < ldof; i++)
1037 if (i_offd[i+1] == i_offd[i] + 1)
1039 d_offd[i_offd[i]] = 1.0;
1041 else if (i_offd[i+1] == i_offd[i] + 2)
1045 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
1046 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
1048 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
1049 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
1050 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
1054 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
1055 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
1056 offd_col_counter, cmap);
1068 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
1069 P_pc_col_starts, NULL,
true);
1078 for (
int i = 0; i < ldof_group.
Size(); i++)
1082 if (ldof_ltdof[i] >= 0)
1100 gc->
Create(pNURBSext()->ldof_group);
1104 GetGroupComm(*gc, 0);
1114 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1118 gcomm->
Bcast(ldof_marker);
1123 int component)
const
1135 int component)
const
1156 const int *ess_dofs_data = ess_dofs.
HostRead();
1157 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1159 const int *ted = true_ess_dofs.
HostRead();
1160 std::string error_msg =
"failed dof: ";
1161 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1163 if (
bool(ted[i]) !=
bool(true_ess_dofs2[i]))
1165 error_msg += std::to_string(i) +=
"(R ";
1166 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1167 error_msg += std::to_string(
bool(true_ess_dofs2[i])) +=
") ";
1176 MFEM_ASSERT(R->
Width() == ess_dofs.
Size(),
"!");
1177 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1178 <<
", rank = " << MyRank <<
", " << error_msg);
1189 int component)
const
1192 "GetEssentialTrueDofsVar is only for variable-order spaces");
1196 const int ntdofs = tdof2ldof.
Size();
1198 vdim * ntdofs == true_ess_dofs.
Size(),
"");
1204 const int vdim_factor = bynodes ? 1 :
vdim;
1206 const int tdof_stride = bynodes ? num_true_dofs : 1;
1209 for (
int l=0; l<
ndofs; ++l)
1211 const int tdof = ldof_ltdof[l];
1212 if (tdof >= 0 && ess_dofs[l])
1214 for (
int vd = 0; vd <
vdim; vd++)
1216 if (component >= 0 && vd != component) {
continue; }
1217 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1218 true_ess_dofs[vtdof] = 1;
1224 std::set<int> edges, faces;
1228 for (
int tdof=0; tdof<ntdofs; ++tdof)
1231 if (!tdof2ldof[tdof].set) {
continue; }
1233 const bool edge = tdof2ldof[tdof].isEdge;
1234 const int index = tdof2ldof[tdof].idx;
1236 const bool bdry = edge ? edges.count(
index) > 0 : faces.count(
index) > 0;
1237 if (!bdry) {
continue; }
1239 for (
int vd = 0; vd <
vdim; vd++)
1241 if (component >= 0 && vd != component) {
continue; }
1242 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1243 true_ess_dofs[vtdof] = 1;
1249 int component)
const
1259 int component)
const
1271 const int *ext_dofs_data = ext_dofs.
HostRead();
1272 Pt->BooleanMult(1, ext_dofs_data, 0, true_ext_dofs2);
1274 const int *ted = true_ext_dofs.
HostRead();
1275 std::string error_msg =
"failed dof: ";
1276 for (
int i = 0; i < true_ext_dofs.
Size(); i++)
1278 if (
bool(ted[i]) !=
bool(true_ext_dofs2[i]))
1280 error_msg += std::to_string(i) +=
"(R ";
1281 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1282 error_msg += std::to_string(
bool(true_ext_dofs2[i])) +=
") ";
1288 MFEM_ASSERT(R->
Width() == ext_dofs.
Size(),
"!");
1289 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1290 <<
", rank = " << MyRank <<
", " << error_msg);
1302 return ldof_ltdof[ldof];
1306 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1308 return ldof_ltdof[ldof];
1321 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1327 if (HYPRE_AssumedPartitionCheck())
1329 return ldof_ltdof[ldof] +
1330 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1334 return ldof_ltdof[ldof] +
1344 MFEM_ABORT(
"Not implemented for NC mesh.");
1347 if (HYPRE_AssumedPartitionCheck())
1351 return ldof_ltdof[sldof] +
1353 ldof_group[sldof])] /
vdim;
1357 return (ldof_ltdof[sldof*
vdim] +
1358 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1365 return ldof_ltdof[sldof] +
1367 ldof_group[sldof])] /
vdim;
1371 return (ldof_ltdof[sldof*
vdim] +
1372 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1379 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1384 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1391 if (Pconf) {
return Pconf; }
1422 if (Rconf) {
return Rconf; }
1459 if (num_face_nbrs == 0)
1465 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1466 MPI_Request *send_requests = requests;
1467 MPI_Request *recv_requests = requests + num_face_nbrs;
1468 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1474 Table send_nbr_elem_dof;
1481 for (
int fn = 0; fn < num_face_nbrs; fn++)
1486 for (
int i = 0; i < num_my_elems; i++)
1489 for (
int j = 0; j < ldofs.
Size(); j++)
1491 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1493 if (ldof_marker[ldof] != fn)
1495 ldof_marker[ldof] = fn;
1505 MyComm, &send_requests[fn]);
1508 MyComm, &recv_requests[fn]);
1511 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1516 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1524 int *send_I = send_nbr_elem_dof.
GetI();
1526 for (
int fn = 0; fn < num_face_nbrs; fn++)
1530 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1531 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1533 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1534 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1537 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1538 send_nbr_elem_dof.
MakeJ();
1542 for (
int fn = 0; fn < num_face_nbrs; fn++)
1547 for (
int i = 0; i < num_my_elems; i++)
1550 for (
int j = 0; j < ldofs.
Size(); j++)
1552 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1554 if (ldof_marker[ldof] != fn)
1556 ldof_marker[ldof] = fn;
1561 send_el_off[fn] + i, ldofs, ldofs.
Size());
1568 int *send_J = send_nbr_elem_dof.
GetJ();
1569 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1573 int j_end = send_I[send_el_off[fn+1]];
1575 for (
int i = 0; i < num_ldofs; i++)
1577 int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
1578 ldof_marker[ldof] = i;
1581 for ( ; j < j_end; j++)
1583 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1584 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1588 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1595 for (
int fn = 0; fn < num_face_nbrs; fn++)
1600 MPI_Isend(send_J + send_I[send_el_off[fn]],
1601 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1602 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1604 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1605 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1606 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1609 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1612 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1615 int j_end = recv_I[recv_el_off[fn+1]];
1617 for ( ; j < j_end; j++)
1630 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1634 for (
int fn = 0; fn < num_face_nbrs; fn++)
1641 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1645 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1648 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1649 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1656 for (
int fn = 0; fn < num_face_nbrs; fn++)
1661 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1662 MyComm, &send_requests[fn]);
1664 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1665 MyComm, &recv_requests[fn]);
1668 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1672 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1686 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1723 int el1, el2, inf1, inf2;
1736 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1738 for (
int j = 0; j < vdofs.
Size(); j++)
1740 const int ldof = vdofs[j];
1741 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1749 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
1750 " does not support NURBS!");
1759 const int ndofs_order = FE->
GetDof();
1760 if (ndofs_order ==
ndofs)
1764 else if (ndofs_order >
ndofs)
1766 MFEM_ABORT(
"Finite element order not found in GetFaceNbrFE");
1792#if MFEM_HYPRE_VERSION <= 22200
1793 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1794 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1795 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1804void ParFiniteElementSpace::ConstructTrueDofs()
1811 GetGroupComm(*gcomm, 1, &ldof_sign);
1821 for (gr = 1; gr < group_ldof.
Size(); gr++)
1823 const int *ldofs = group_ldof.
GetRow(gr);
1824 const int nldofs = group_ldof.
RowSize(gr);
1825 for (i = 0; i < nldofs; i++)
1827 ldof_group[ldofs[i]] = gr;
1832 for (i = 0; i < nldofs; i++)
1834 ldof_ltdof[ldofs[i]] = -2;
1841 for (i = 0; i < n; i++)
1843 if (ldof_ltdof[i] == -1)
1845 ldof_ltdof[i] = ltdof_size++;
1851 gcomm->
Bcast(ldof_ltdof);
1854void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1857 GroupTopology > = pNURBSext()->
gtopo;
1858 gcomm =
new GroupCommunicator(gt);
1863 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1867 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1869 for (
int i = 0; i < n; i++)
1871 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1875 gcomm->
Create(ldof_group);
1883 for (
int i = 0; i < n; i++)
1885 if (gt.IAmMaster(ldof_group[i]))
1887 ldof_ltdof[i] = ltdof_size;
1898 gcomm->
Bcast(ldof_ltdof);
1901void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1902 Array<int> &dofs)
const
1906 for (
int j = 0; j < nv; j++)
1908 dofs[j] =
ndofs + nv*
id.index + j;
1912static const char* msg_orders_changed =
1913 "Element orders changed, you need to Update() the space first.";
1915void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1916 Array<int> &dofs,
int variant)
const
1920 int order, ne, base;
1923 const int edge = edge_id.index;
1926 base = beg[variant];
1927 ne = beg[variant+1] - base;
1938 base = (edge_id.index - pncmesh->
GetNEdges())*ne;
1943 dofs.SetSize(2*nv + ne);
1948 for (
int i = 0; i < 2; i++)
1950 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1951 for (
int j = 0; j < nv; j++)
1953 dofs[i*nv + j] = k++;
1957 int k =
ndofs + ngvdofs + base;
1958 for (
int j = 0; j < ne; j++)
1960 dofs[2*nv + j] = k++;
1964void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1965 Array<int> &dofs)
const
1969 int nfv, V[4], E[4], Eo[4];
1976 int nf = (nfv == 3) ? nf_tri : nf_quad;
1978 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1980 Array<int> evar(nfv);
1985 const int face = face_id.index;
1987 constexpr int variant = 0;
1989 base = beg[variant];
1990 nf = beg[variant+1] - base;
1997 for (
int i = 0; i < nfv; i++)
2013 MFEM_VERIFY(eo == fo,
"Edge must have same order as face");
2016 const int ne_i = ebeg[evar[i] + 1] - ebeg[evar[i]];
2020 dofs.SetSize((nfv * nv) + allne + nf);
2024 base = nf_quad * ghost_face_index;
2028 dofs.SetSize(nfv*(nv + ne) + nf);
2032 for (
int i = 0; i < nfv; i++)
2035 const int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
2036 for (
int j = 0; j < nv; j++)
2038 dofs[offset++] = first + j;
2042 for (
int i = 0; i < nfv; i++)
2047 const int variant = evar[i];
2050 int ebase = beg[variant];
2051 ne = beg[variant+1] - ebase;
2053 MFEM_ASSERT(ebase ==
FindEdgeDof(E[i], ne),
"sanity check?");
2055 const int first = (E[i] < ghost) ?
nvdofs + ebase
2063 for (
int j = 0; j < ne; j++)
2065 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
2066 : (-1 - (first + (-1 - ind[j])));
2071 const int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
2072 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
2074 for (
int j = 0; j < ne; j++)
2076 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
2077 : (-1 - (first + (-1 - ind[j])));
2082 const int first =
ndofs + ngvdofs + ngedofs + base;
2083 for (
int j = 0; j < nf; j++)
2085 dofs[offset++] = first + j;
2089void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
2090 Array<int> &dofs,
int var)
const
2095 case 0: GetGhostVertexDofs(
id, dofs);
break;
2096 case 1: GetGhostEdgeDofs(
id, dofs, var);
break;
2097 case 2: GetGhostFaceDofs(
id, dofs);
break;
2101void ParFiniteElementSpace::GetBareDofsVar(
int entity,
int index,
2102 Array<int> &dofs)
const
2104 int ned, ghost, first;
2110 first = (
index < ghost)
2119 ned = rowNext[0] - row[0];
2120 first = (
index < ghost)
2136 first =
ndofs + ngvdofs + ngedofs + row0 -
nfdofs;
2143 for (
int i = 0; i < ned; i++)
2145 dofs[i] = first + i;
2149void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
2150 Array<int> &dofs)
const
2154 GetBareDofsVar(entity,
index, dofs);
2158 int ned, ghost, first;
2164 first = (
index < ghost)
2172 first = (
index < ghost)
2193 first =
ndofs + ngvdofs + ngedofs +
index*stride;
2199 for (
int i = 0; i < ned; i++)
2201 dofs[i] = first + i;
2205int ParFiniteElementSpace::PackDofVar(
int entity,
int index,
int edof,
2216 return (
index < ghost)
2225 const int d = row[var] + edof;
2249static int bisect(
const int* array,
int size,
int value)
2251 const int* end = array + size;
2252 const int* pos = std::upper_bound(array, end, value);
2253 MFEM_VERIFY(pos != array,
"value not found");
2256 MFEM_VERIFY(*(array+size - 1) == value,
"Last entry must be exact")
2258 return pos - array - 1;
2261void ParFiniteElementSpace::UnpackDofVar(
int dof,
int &entity,
int &
index,
2262 int &edof,
int &order)
const
2265 MFEM_ASSERT(dof >= 0,
"");
2271 entity = 0,
index = dof / nv, edof = dof % nv;
2278 index = var_edge_dofmap[dof].index;
2279 edof = var_edge_dofmap[dof].edof;
2284 const int edge =
index;
2286 for (
int v=0; v<nvar; ++v)
2290 if (edof < os + dofs)
2299 MFEM_ASSERT(order >= 0,
"");
2308 index = var_face_dofmap[dof].index;
2309 edof = var_face_dofmap[dof].edof;
2314 const int face =
index;
2317 for (
int v=0; v<nvar; ++v)
2321 if (edof < os + dofs)
2330 MFEM_ASSERT(order >= 0,
"");
2335 MFEM_ABORT(
"Cannot unpack internal DOF");
2352 edof = var_edge_dofmap[dof +
nedofs].edof;
2361 edof = var_face_dofmap[dof +
nfdofs].edof;
2364 MFEM_ABORT(
"Out of range DOF.");
2368int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof,
2373 return PackDofVar(entity,
index, edof, var);
2386 return (
index < ghost)
2394 return (
index < ghost)
2396 :
ndofs + ngvdofs + (
index - ghost)*ned + edof;
2410 return ndofs + ngvdofs + ngedofs +
index*stride + edof;
2418void ParFiniteElementSpace::UnpackDof(
int dof,
2419 int &entity,
int &
index,
2420 int &edof,
int &order)
const
2426 UnpackDofVar(dof, entity,
index, edof, order);
2430 MFEM_ASSERT(dof >= 0,
"");
2436 entity = 0,
index = dof / nv, edof = dof % nv;
2443 entity = 1,
index = dof / ne, edof = dof % ne;
2452 index = dof / nf, edof = dof % nf;
2458 MFEM_ASSERT(table.Size() > 0,
"");
2459 int jpos =
bisect(table.GetJ(), table.Size_of_connections(), dof);
2461 edof = dof - table.GetRow(
index)[0];
2466 MFEM_ABORT(
"Cannot unpack internal DOF");
2481 entity = 1,
index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
2488 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
2492 MFEM_ABORT(
"Out of range DOF.");
2500struct PMatrixElement
2506 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
2507 : column(col), stride(str), value(val) {}
2509 bool operator<(
const PMatrixElement &other)
const
2510 {
return column < other.column; }
2512 typedef std::vector<PMatrixElement> List;
2520 PMatrixElement::List elems;
2523 void AddRow(
const PMatrixRow &other,
real_t coef)
2525 elems.reserve(elems.size() + other.elems.size());
2526 for (
const PMatrixElement &oei : other.elems)
2528 elems.emplace_back(oei.column, oei.stride, coef * oei.value);
2535 if (!elems.size()) {
return; }
2536 std::sort(elems.begin(), elems.end());
2539 for (
unsigned i = 1; i < elems.size(); i++)
2541 if (elems[j].column == elems[i].column)
2543 elems[j].value += elems[i].value;
2547 elems[++j] = elems[i];
2553 void write(std::ostream &os,
real_t sign)
const
2556 for (
unsigned i = 0; i < elems.size(); i++)
2558 const PMatrixElement &e = elems[i];
2565 void read(std::istream &is,
real_t sign)
2568 for (
unsigned i = 0; i < elems.size(); i++)
2570 PMatrixElement &e = elems[i];
2578class NeighborOrderMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ORDER_VM>
2581 typedef NCMesh::MeshId MeshId;
2586 int entity,
index, order;
2589 OrderInfo(
int ent,
int idx,
int p, GroupId grp)
2590 : entity(ent),
index(idx), order(
p), group(grp) {}
2593 NeighborOrderMessage() : pncmesh(NULL) {}
2595 void AddOrder(
int ent,
int idx,
int p, GroupId grp)
2597 msgs.emplace_back(ent, idx,
p, grp);
2600 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2602 const std::vector<OrderInfo>& GetMsgs()
const {
return msgs; }
2604 typedef std::map<int, NeighborOrderMessage> Map;
2607 std::vector<OrderInfo> msgs;
2612 void Encode(
int rank)
override;
2614 void Decode(
int rank)
override;
2617void NeighborOrderMessage::Encode(
int rank)
2619 std::ostringstream stream;
2621 Array<MeshId> ent_ids[3];
2622 Array<GroupId> group_ids[3];
2623 Array<int> row_idx[3];
2626 for (
unsigned i = 0; i < msgs.size(); i++)
2628 const OrderInfo &ri = msgs[i];
2630 ent_ids[ri.entity].Append(
id);
2631 row_idx[ri.entity].Append(i);
2632 group_ids[ri.entity].Append(ri.group);
2635 Array<GroupId> all_group_ids;
2636 all_group_ids.Reserve(msgs.size());
2637 for (
int i = 0; i < 3; i++)
2639 all_group_ids.Append(group_ids[i]);
2647 for (
int ent = 0; ent < 3; ent++)
2649 for (
int i = 0; i < ent_ids[ent].Size(); i++)
2651 const OrderInfo &ri = msgs[row_idx[ent][i]];
2652 MFEM_ASSERT(ent == ri.entity,
"");
2659 stream.str().swap(
data);
2662void NeighborOrderMessage::Decode(
int rank)
2664 std::istringstream stream(
data);
2666 Array<MeshId> ent_ids[3];
2667 Array<GroupId> group_ids;
2673 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2674 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2677 msgs.reserve(nrows);
2680 for (
int ent = 1, gi = 0; ent < 3; ent++)
2683 const Array<MeshId> &ids = ent_ids[ent];
2684 for (
int i = 0; i < ids.Size(); i++)
2686 const MeshId &
id = ids[i];
2692 msgs.emplace_back(ent,
id.
index, order_i, group_ids[gi++]);
2700class NeighborRowMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ROW_VM>
2703 typedef NCMesh::MeshId MeshId;
2707 int entity,
index, edof, var;
2711 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row,
2713 : entity(ent),
index(idx), edof(edof), var(v), group(grp), row(row) {}
2715 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
int v = 0)
2716 : entity(ent),
index(idx), edof(edof), var(v), group(grp) {}
2719 NeighborRowMessage() : pncmesh(NULL) {}
2721 void AddRow(
int entity,
int index,
int edof, GroupId group,
2722 const PMatrixRow &row,
int order)
2725 if (varOrder && entity == 1)
2731 MFEM_ASSERT(order_v >= 0,
"");
2732 if (order == order_v)
2746 else if (varOrder && entity == 2)
2752 MFEM_ASSERT(order_v >= 0,
"");
2753 if (order == order_v)
2769 rows.emplace_back(entity,
index, edof, group, row, var);
2772 const std::vector<RowInfo>& GetRows()
const {
return rows; }
2774 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2775 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
2776 void SetSpace(
const ParFiniteElementSpace* fes_)
2782 typedef std::map<int, NeighborRowMessage> Map;
2785 std::vector<RowInfo> rows;
2788 const FiniteElementCollection* fec;
2789 const ParFiniteElementSpace* fes;
2791 bool varOrder =
false;
2793 int GetEdgeVarOffset(
int edge,
int var);
2794 int GetFaceVarOffset(
int face,
int var);
2797 void Encode(
int rank)
override;
2799 void Decode(
int rank)
override;
2802void NeighborRowMessage::Encode(
int rank)
2804 std::ostringstream stream;
2806 Array<MeshId> ent_ids[3];
2807 Array<GroupId> group_ids[3];
2808 Array<int> row_idx[3];
2811 for (
unsigned i = 0; i < rows.size(); i++)
2813 const RowInfo &ri = rows[i];
2815 ent_ids[ri.entity].Append(
id);
2816 row_idx[ri.entity].Append(i);
2817 group_ids[ri.entity].Append(ri.group);
2820 Array<GroupId> all_group_ids;
2821 all_group_ids.Reserve(
static_cast<int>(rows.size()));
2822 for (
int i = 0; i < 3; i++)
2824 all_group_ids.Append(group_ids[i]);
2832 for (
int ent = 0; ent < 3; ent++)
2834 const Array<MeshId> &ids = ent_ids[ent];
2835 for (
int i = 0; i < ids.Size(); i++)
2837 const MeshId &
id = ids[i];
2838 const RowInfo &ri = rows[row_idx[ent][i]];
2839 MFEM_ASSERT(ent == ri.entity,
"");
2841#ifdef MFEM_DEBUG_PMATRIX
2843 <<
": ent " << ri.entity <<
", index " << ri.index
2844 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
2845 << int(
id.local) <<
")" << std::endl;
2856 const int *ind =
nullptr;
2868 if (ind && (edof = ind[edof]) < 0)
2877 if (ent == 2 && varOrder)
2885 ri.row.write(stream, s);
2890 stream.str().swap(
data);
2893int NeighborRowMessage::GetEdgeVarOffset(
int edge,
int var)
2896 for (
int v=0; v<var; ++v)
2906int NeighborRowMessage::GetFaceVarOffset(
int face,
int var)
2910 for (
int v=0; v<var; ++v)
2913 const int dofs = fec->
GetNumDof(geom, fo);
2920void NeighborRowMessage::Decode(
int rank)
2922 std::istringstream stream(
data);
2924 Array<MeshId> ent_ids[3];
2925 Array<GroupId> group_ids;
2931 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2932 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2935 rows.reserve(nrows);
2938 for (
int ent = 0, gi = 0; ent < 3; ent++)
2941 const Array<MeshId> &ids = ent_ids[ent];
2942 for (
int i = 0; i < ids.Size(); i++)
2944 const MeshId &
id = ids[i];
2948 MFEM_ASSERT(order_i >= 0,
"");
2955 const int *ind =
nullptr;
2977 if (order == order_i)
2992 RowInfo tmprow(1, 0, 0, 0);
2993 tmprow.row.read(stream, 1.0);
3011 "Only quadrilateral faces are supported in "
3012 "variable-order spaces");
3025 if (order == order_i)
3040 RowInfo tmprow(1, 0, 0, 0);
3041 tmprow.row.read(stream, 1.0);
3058 const bool process_dof_pairs = (ent == 2 &&
3062#ifdef MFEM_DEBUG_PMATRIX
3064 <<
": ent " << ent <<
", index " <<
id.index
3065 <<
", edof " << edof <<
" (id " <<
id.element <<
"/"
3066 << int(
id.local) <<
")" << std::endl;
3070 real_t s = (edof < 0) ? -1.0 : 1.0;
3071 edof = (edof < 0) ? -1 - edof : edof;
3072 if (ind && (edof = ind[edof]) < 0)
3082 rows.emplace_back(ent,
id.
index, edof, group_ids[gi++], var);
3083 rows.back().row.read(stream, s);
3085#ifdef MFEM_DEBUG_PMATRIX
3087 <<
": ent " << rows.back().entity <<
", index "
3088 << rows.back().index <<
", edof " << rows.back().edof
3092 if (process_dof_pairs)
3112 auto &first_row = rows.back().row;
3114 const auto initial_first_row = first_row;
3117 const MeshId &next_id = ids[++i];
3124 s = (edof < 0) ? -1.0 : 1.0;
3125 edof = (edof < 0) ? -1 - edof : edof;
3126 if (ind && (edof = ind[edof]) < 0)
3132 rows.emplace_back(ent, next_id.index, edof, group_ids[gi++]);
3133 rows.back().row.read(stream, s);
3134 auto &second_row = rows.back().row;
3137 const auto initial_second_row = second_row;
3155 MFEM_ASSERT(fo != 2 &&
3156 fo != 4,
"This code branch is ambiguous for face orientations 2 and 4."
3157 " Please report this mesh for further testing.\n");
3159 first_row.AddRow(initial_first_row, T[0] - 1.0);
3160 first_row.AddRow(initial_second_row, T[2]);
3161 second_row.AddRow(initial_first_row, T[1]);
3162 second_row.AddRow(initial_second_row, T[3] - 1.0);
3164 first_row.Collapse();
3165 second_row.Collapse();
3172ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
3174 NeighborRowMessage::Map &send_msg)
const
3176 int ent, idx, edof, order;
3177 UnpackDof(dof, ent, idx, edof, order);
3179 for (
const auto &rank : pncmesh->GetGroup(group_id))
3183 NeighborRowMessage &msg = send_msg[rank];
3185 msg.AddRow(ent, idx, edof, group_id, row, order);
3186 msg.SetNCMesh(pncmesh);
3188#ifdef MFEM_PMATRIX_STATS
3195void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
3196 GroupId group_sent_id, GroupId group_id,
3197 NeighborRowMessage::Map &send_msg)
const
3199 int ent, idx, edof, order;
3200 UnpackDof(dof, ent, idx, edof, order);
3203 for (
unsigned i = 0; i < group.size(); i++)
3205 int rank = group[i];
3206 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
3208 NeighborRowMessage &msg = send_msg[rank];
3209 GroupId invalid = -1;
3211 msg.AddRow(ent, idx, edof, invalid, row, order);
3212 msg.SetNCMesh(pncmesh);
3214#ifdef MFEM_PMATRIX_STATS
3217#ifdef MFEM_DEBUG_PMATRIX
3219 << rank <<
": ent " << ent <<
", index" << idx
3220 <<
", edof " << edof << std::endl;
3226#ifdef MFEM_DEBUG_PMATRIX
3227void ParFiniteElementSpace
3228::DebugDumpDOFs(std::ostream &os,
3229 const SparseMatrix &deps,
3230 const Array<GroupId> &dof_group,
3231 const Array<GroupId> &dof_owner,
3232 const Array<bool> &finalized)
const
3234 for (
int i = 0; i < dof_group.Size(); i++)
3237 if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
3240 UnpackDof(i, ent, idx, edof);
3242 os << edof <<
" @ ";
3243 if (i > ndofs) { os <<
"ghost "; }
3246 case 0: os <<
"vertex ";
break;
3247 case 1: os <<
"edge ";
break;
3248 default: os <<
"face ";
break;
3252 if (i < deps.Height() && deps.RowSize(i))
3254 os <<
"depends on ";
3255 for (
int j = 0; j < deps.RowSize(i); j++)
3257 os << deps.GetRowColumns(i)[j] <<
" ("
3258 << deps.GetRowEntries(i)[j] <<
")";
3259 if (j < deps.RowSize(i)-1) { os <<
", "; }
3268 os <<
"group " << dof_group[i] <<
" (";
3270 for (
unsigned j = 0; j < g.size(); j++)
3272 if (j) { os <<
", "; }
3276 os <<
"), owner " << dof_owner[i] <<
" (rank "
3277 << pncmesh->GetGroup(dof_owner[i])[0] <<
"); "
3278 << (finalized[i] ?
"finalized" :
"NOT finalized");
3289void ParFiniteElementSpace::ScheduleSendOrder(
3290 int ent,
int idx,
int order, GroupId group_id,
3291 NeighborOrderMessage::Map &send_msg)
const
3293 for (
const auto &rank : pncmesh->GetGroup(group_id))
3297 NeighborOrderMessage &msg = send_msg[rank];
3298 msg.AddOrder(ent, idx, order, group_id);
3299 msg.SetNCMesh(pncmesh);
3305 const std::set<int> &edges,
const std::set<int> &faces,
3309 bool changed = edges.size() > 0 || faces.size() > 0;
3313 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3319 NeighborOrderMessage::Map send_msg;
3322 for (
int entity = 1; entity <= 2; ++entity)
3324 const std::set<int> &indices = entity == 1 ? edges : faces;
3326 for (
auto idx : indices)
3332 ScheduleSendOrder(entity, idx,
MinOrder(orders[idx]),
3341 MPI_Barrier(MyComm);
3343 NeighborOrderMessage recv_msg;
3344 recv_msg.SetNCMesh(pncmesh);
3351 recv_msg.Recv(rank, size, MyComm);
3353 for (
const auto &ri : recv_msg.GetMsgs())
3359 edge_orders[ri.index] |= mask;
3360 if (edge_orders[ri.index] != initOrders)
3365 else if (ri.entity == 2)
3368 face_orders[ri.index] |= mask;
3369 if (face_orders[ri.index] != initOrders)
3376 MFEM_ABORT(
"Invalid entity type");
3385 recv_msg.RecvDrop(rank, size, MyComm);
3392 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3401 MFEM_VERIFY(intermediate.
Size() ==
ndofs,
"");
3406 for (
int e=0; e<n; ++e)
3409 for (
int var = 1; var < nvar - 1; ++var)
3414 for (
auto dof : dofs)
3418 intermediate[dof] =
true;
3425void ParFiniteElementSpace::SetVarDofMap(
const Table & dofs,
3428 if (dofs.
Size() < 1)
3434 MFEM_ASSERT(dofs.
RowSize(dofs.
Size() - 1) == 1,
"");
3435 const int* rowLast = dofs.
GetRow(dofs.
Size() - 1);
3436 const int ndofs = rowLast[0];
3440 for (
int r = 0; r < dofs.
Size() - 1; ++r)
3442 const int* row = dofs.
GetRow(r);
3443 const int* row1 = dofs.
GetRow(r+1);
3445 for (
int d=row[0]; d<row1[0]; ++d)
3448 dmap[d].edof = d - row[0];
3453void ParFiniteElementSpace::SetTDOF2LDOFinfo(
int ntdofs,
int vdim_factor,
3454 int dof_stride,
int allnedofs)
3459 for (
int i=0; i<ntdofs; ++i)
3461 tdof2ldof[i].set =
false;
3467 for (
int entity = 1; entity < pmesh->
Dimension(); entity++)
3470 const int num_ent = (entity == 1) ? pmesh->
GetNEdges() :
3472 MFEM_ASSERT(ent_dofs.Size() >= num_ent+1,
"");
3474 for (
int idx = 0; idx < num_ent; idx++)
3476 if (ent_dofs.RowSize(idx) == 0) {
continue; }
3486 const int order0 =
GetEntityDofs(entity, idx, dofs, geom, 0);
3493 numVert = verts.Size();
3494 MFEM_VERIFY(numVert == 4,
"Only quadrilateral faces are supported");
3502 constexpr int vd = 0;
3503 for (
int i=idof0; i<dofs.Size(); ++i)
3505 const int dof_i = dofs[i];
3506 const int vdof_i = dof_i*vdim_factor + vd*dof_stride;
3507 const int tdof = ldof_ltdof[vdof_i];
3508 if (tdof < 0) {
continue; }
3510 MFEM_ASSERT(!tdof2ldof[tdof].set,
"");
3512 tdof2ldof[tdof].set =
true;
3513 tdof2ldof[tdof].minOrder = minOrder;
3514 tdof2ldof[tdof].isEdge = (entity == 1);
3515 tdof2ldof[tdof].idx = idx;
3521void ParFiniteElementSpace
3522::SetRestrictionMatrixEdgesFaces(
int vdim_factor,
int dof_stride,
3523 int tdof_stride,
const Array<int> &dof_tdof,
3524 const Array<HYPRE_BigInt> &dof_offs)
3526 MFEM_VERIFY(IsVariableOrder(),
"");
3528 const int ntdofs = tdof2ldof.Size();
3529 MFEM_VERIFY(vdim * ntdofs == R->NumRows(),
"");
3531 int prevEntity = -1;
3535 Array<int> ldofs, tdofs;
3538 for (
int tdof=0; tdof<ntdofs; ++tdof)
3540 if (!tdof2ldof[tdof].set) {
continue; }
3542 const int minOrder = tdof2ldof[tdof].minOrder;
3543 const bool edge = tdof2ldof[tdof].isEdge;
3544 const int index = tdof2ldof[tdof].idx;
3545 const int entity = edge ? 1 : 2;
3546 MFEM_ASSERT(!pncmesh->IsGhost(entity,
index),
3547 "True DOFs are not defined on ghost entities");
3549 if (entity != prevEntity ||
index != prevIndex)
3558 prevEntity = entity;
3566 const FiniteElement *feT = fec->FiniteElementForGeometry(geom);
3567 const FiniteElement *feL = fec->FiniteElementForGeometry(geom);
3572 tdofOrder = GetEdgeOrder(
index, 0);
3573 GetEdgeDofs(
index, tdofs, 0);
3574 for (
int var=0; ; ++var)
3576 const int order_var = GetEdgeOrder(
index, var);
3577 if (order_var == minOrder)
3579 GetEdgeDofs(
index, ldofs, var);
3586 tdofOrder = GetFaceOrder(
index, 0);
3587 GetFaceDofs(
index, tdofs, 0);
3588 for (
int var=0; ; ++var)
3590 const int order_var = GetFaceOrder(
index, var);
3591 if (order_var == minOrder)
3593 GetFaceDofs(
index, ldofs, var);
3599 MFEM_VERIFY(tdofs.Size() > 0 && ldofs.Size() > 0,
"");
3602 idof0 = GetNumBorderDofs(geom, tdofOrder);
3604 feT = fec->GetFE(geom, tdofOrder);
3605 feL = fec->GetFE(geom, minOrder);
3607 MFEM_VERIFY(feT && feL,
"");
3609 IsoparametricTransformation T;
3615 default: MFEM_ABORT(
"unsupported geometry");
3619 T.SetIdentityTransformation(geom);
3620 feT->GetTransferMatrix(*feL, T, I);
3623 for (
int ldi=0; ldi<ldofs.Size(); ++ldi)
3625 const real_t value = I(tdi + idof0, ldi);
3626 if (std::abs(value) > 1e-12)
3628 const int ldof = all2local[ldofs[ldi]];
3629 for (
int vd = 0; vd < vdim; vd++)
3631 const int vdof = ldof*vdim_factor + vd*dof_stride;
3632 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
3633 R->Add(vtdof, vdof, value);
3640int ParFiniteElementSpace
3641::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
3642 Array<HYPRE_BigInt> &dof_offs,
3643 Array<HYPRE_BigInt> &tdof_offs,
3644 Array<int> *dof_tdof,
3647 const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
3648 const bool H1var = IsVariableOrderH1();
3650#ifdef MFEM_PMATRIX_STATS
3651 n_msgs_sent = n_msgs_recv = 0;
3652 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
3657 const int total_dofs = ndofs + ngdofs;
3658 SparseMatrix deps(ndofs, total_dofs);
3660 if (!dg && !partial)
3662 VariableOrderMinimumRule(deps);
3664 Array<int> master_dofs, slave_dofs;
3667 for (
int entity = 0; entity <= 2; entity++)
3669 const NCMesh::NCList &list = pncmesh->GetNCList(entity);
3670 if (list.masters.Size() == 0) {
continue; }
3672 IsoparametricTransformation T;
3676 for (
const auto &mf : list.masters)
3679 if (entity == 1 && skip_edge.Size() > 0)
3681 if (skip_edge[mf.index])
3686 else if (entity == 2 && skip_face.Size() > 0)
3688 if (skip_face[mf.index])
3694 if (pncmesh->IsGhost(entity, mf.index))
3696 GetGhostDofs(entity, mf, master_dofs, 0);
3700 GetEntityDofs(entity, mf.index, master_dofs, mf.Geom(), 0);
3703 if (master_dofs.Size() == 0) {
continue; }
3705 const FiniteElement *fe = fec->FiniteElementForGeometry(mf.Geom());
3707 if (IsVariableOrder())
3710 if (entity == 1) { mfOrder = GetEdgeOrder(mf.index, 0); }
3711 else if (entity == 2) { mfOrder = GetFaceOrder(mf.index, 0); }
3713 if (entity != 0) { fe = fec->GetFE(mf.Geom(), mfOrder); }
3716 if (fe ==
nullptr) {
continue; }
3723 default: MFEM_ABORT(
"unsupported geometry");
3727 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
3729 const NCMesh::Slave &sf = list.slaves[si];
3730 if (pncmesh->IsGhost(entity, sf.index)) {
continue; }
3732 constexpr int variant = 0;
3733 const int q = GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
3734 if (q < 0) {
break; }
3736 list.OrientedPointMatrix(sf, T.GetPointMat());
3738 const auto *slave_fe = fec->GetFE(mf.Geom(), q);
3739 slave_fe->GetTransferMatrix(*fe, T, I);
3742 AddDependencies(deps, master_dofs, slave_dofs, I);
3752 Array<GroupId> dof_group(total_dofs);
3753 Array<GroupId> dof_owner(total_dofs);
3761 auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
3762 this](
int entity,
const MeshId &id)
3764 if (
id.
index < 0) {
return; }
3766 GroupId owner = pncmesh->GetEntityOwnerId(entity,
id.
index);
3767 GroupId group = pncmesh->GetEntityGroupId(entity,
id.
index);
3769 GetBareDofs(entity,
id.
index, dofs);
3771 for (
auto dof : dofs)
3773 dof_owner[dof] = owner;
3774 dof_group[dof] = group;
3779 for (
int entity : {0,1,2})
3781 for (
const auto &
id : pncmesh->GetNCList(entity).conforming)
3783 initialize_group_and_owner(entity,
id);
3785 for (
const auto &
id : pncmesh->GetNCList(entity).masters)
3787 initialize_group_and_owner(entity,
id);
3789 for (
const auto &
id : pncmesh->GetNCList(entity).slaves)
3791 initialize_group_and_owner(entity,
id);
3798 Array<bool> finalized(total_dofs);
3802 int num_true_dofs = 0;
3803 for (
int i = 0; i < ndofs; ++i)
3805 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
3808 finalized[i] =
true;
3812#ifdef MFEM_DEBUG_PMATRIX
3814 auto dof_diagnostics = [&](
int dof,
bool print_diagnostic)
3816 const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
3817 std::stringstream msg;
3818 msg << std::boolalpha;
3820 <<
" owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] <<
" CommGroup {";
3821 for (
const auto &x : comm_group)
3825 msg <<
"} finalized " << finalized[dof];
3831 deps.GetRow(dof, cols, row);
3832 msg <<
" deps cols {";
3833 for (
const auto &x : cols)
3840 int entity,
index, edof;
3841 UnpackDof(dof, entity,
index, edof);
3842 msg <<
" entity " << entity <<
" index " <<
index <<
" edof " << edof;
3849 HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
3850 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
3851 pmesh->GenerateOffsets(2, loc_sizes, offsets);
3855 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
3860 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
3864 dof_tdof->SetSize(ndofs*vdim);
3868 std::vector<PMatrixRow> pmatrix(total_dofs);
3871 const int vdim_factor = bynodes ? 1 : vdim;
3872 const int dof_stride = bynodes ? ndofs : 1;
3873 const int tdof_stride = bynodes ? num_true_dofs : 1;
3876 std::list<NeighborRowMessage::Map> send_msg;
3877 send_msg.emplace_back();
3880 for (
int dof = 0, tdof = 0; dof < ndofs; dof++)
3884 pmatrix[dof].elems.emplace_back(
3885 my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.);
3888 if (dof_group[dof] != 0)
3890 MFEM_VERIFY(!send_msg.empty(),
"");
3891 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
3894 for (
int vd = 0; vd < vdim; vd++)
3896 const int vdof = dof*vdim_factor + vd*dof_stride;
3897 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
3899 if (R_ && !H1var) { (*R_)->Add(vtdof, vdof, 1.0); }
3900 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
3907 MFEM_VERIFY(!send_msg.empty(),
"");
3909#ifdef MFEM_PMATRIX_STATS
3910 n_msgs_sent += send_msg.back().size();
3913 if (R_ && !H1var) { (*R_)->Finalize(); }
3918 NeighborRowMessage recv_msg;
3919 recv_msg.SetNCMesh(pncmesh);
3920 recv_msg.SetSpace(
this);
3921 recv_msg.SetFEC(fec);
3923 int num_finalized = num_true_dofs;
3925 buffer.elems.reserve(1024);
3931 Array<bool> intermediate(ndofs);
3932 intermediate =
false;
3934 MarkIntermediateEntityDofs(1, intermediate);
3935 MarkIntermediateEntityDofs(2, intermediate);
3937 while (num_finalized < ndofs)
3940 MFEM_VERIFY(!send_msg.empty(),
"");
3941 if (send_msg.back().size())
3943 send_msg.emplace_back();
3951 recv_msg.Recv(rank, size, MyComm);
3953#ifdef MFEM_PMATRIX_STATS
3955 n_rows_recv += recv_msg.GetRows().size();
3958 for (
const auto &ri : recv_msg.GetRows())
3960 const int dof = PackDof(ri.entity, ri.index, ri.edof, ri.var);
3961 pmatrix[dof] = ri.row;
3963 if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
3964 finalized[dof] =
true;
3966 if (ri.group >= 0 && dof_group[dof] != ri.group)
3969 MFEM_VERIFY(!send_msg.empty(),
"");
3970 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
3980 for (
int dof = 0; dof < ndofs; dof++)
3982 const bool owned = (dof_owner[dof] == 0);
3984 && (owned || intermediate[dof])
3985 && DofFinalizable(dof, finalized, deps))
3987 const int* dep_col = deps.GetRowColumns(dof);
3988 const real_t* dep_coef = deps.GetRowEntries(dof);
3991 buffer.elems.clear();
3992 for (
int j = 0; j < deps.RowSize(dof); j++)
3994 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
3997 pmatrix[dof] = buffer;
3999 finalized[dof] =
true;
4004 const bool shared = (dof_group[dof] != 0);
4007 MFEM_VERIFY(!send_msg.empty(),
"");
4008 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
4015#ifdef MFEM_DEBUG_PMATRIX
4016 static int dump = 0;
4020 snprintf(fname, 100,
"dofs%02d.txt", MyRank);
4021 std::ofstream
f(fname);
4022 DebugDumpDOFs(
f, deps, dof_group, dof_owner, finalized);
4028 MFEM_VERIFY(!send_msg.empty(),
"");
4030#ifdef MFEM_PMATRIX_STATS
4031 n_msgs_sent += send_msg.back().size();
4037 const int allnedofs = nedofs;
4040 SetVarOrderLocalDofs();
4042 const int ldof_stride = bynodes ? ndofs : 1;
4047 Array<HYPRE_BigInt>* offsets[1] = { &dof_offs };
4048 pmesh->GenerateOffsets(1, loc_sizes, offsets);
4053 std::vector<PMatrixRow> pmatrix_new(ndofs);
4056 bool validMap =
true;
4057 for (
int i=0; i<all2local.Size(); ++i)
4059 if (all2local[i] >= 0)
4061 if (all2local[i] - dofnew != 1)
4066 dofnew = all2local[i];
4068 pmatrix_new[all2local[i]] = pmatrix[i];
4071 MFEM_VERIFY(validMap && dofnew == ndofs - 1,
"");
4076 *P_ = MakeVDimHypreMatrix(pmatrix_new, ndofs, num_true_dofs,
4077 dof_offs, tdof_offs);
4082 MFEM_VERIFY(R_ && nedofs == lnedofs,
"");
4084 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
4088 for (
int dof = 0; dof < nvdofs; dof++)
4090 for (
int vd = 0; vd < vdim; vd++)
4092 const int valldof = dof*vdim_factor + vd*dof_stride;
4093 const int vdof = dof*vdim_factor + vd*ldof_stride;
4094 const int vtdof = (*dof_tdof)[valldof];
4095 if (vtdof >= 0) { (*R_)->Add(vtdof, vdof, 1.0); }
4101 SetTDOF2LDOFinfo(num_true_dofs, vdim_factor, dof_stride, allnedofs);
4103 Array<HYPRE_BigInt> all_dof_offs(NRanks);
4104 MPI_Allgather(&dof_offs[0], 1, HYPRE_MPI_BIG_INT, all_dof_offs.GetData(),
4105 1, HYPRE_MPI_BIG_INT, MyComm);
4107 SetRestrictionMatrixEdgesFaces(vdim_factor, ldof_stride, tdof_stride,
4108 *dof_tdof, all_dof_offs);
4114 const int nalldofs = dof_tdof->Size() / vdim;
4115 MFEM_VERIFY(nalldofs * vdim == dof_tdof->Size(),
"");
4116 for (
int edof=0; edof<nbdofs; ++edof)
4118 const int dof = ndofs - nbdofs + edof;
4119 const int alldof = nalldofs - nbdofs + edof;
4120 for (
int vd = 0; vd < vdim; vd++)
4122 const int valldof = alldof*vdim_factor + vd*dof_stride;
4123 const int vdof = dof*vdim_factor + vd*ldof_stride;
4124 const int vtdof = (*dof_tdof)[valldof];
4125 (*R_)->Add(vtdof, vdof, 1.0);
4130 for (
int tdof=0; tdof<num_true_dofs; ++tdof)
4132 if ((*R_)->RowSize(tdof) == 0)
4134 MFEM_ABORT(
"Empty row of R");
4141 Array<int> dof_tdof_new(ndofs * vdim);
4142 for (
int i=0; i<all2local.Size(); ++i)
4144 if (all2local[i] >= 0)
4146 for (
int vd = 0; vd < vdim; vd++)
4148 const int vdof = i*vdim_factor + vd*dof_stride;
4149 const int ldof = all2local[i]*vdim_factor + vd*ldof_stride;
4150 dof_tdof_new[ldof] = (*dof_tdof)[vdof];
4155 Swap(dof_tdof_new, *dof_tdof);
4160 MFEM_VERIFY(var_edge_dofs.Size() - 1 == pncmesh->GetNEdges() +
4161 pncmesh->GetNGhostEdges(),
"");
4162 MFEM_VERIFY(var_face_dofs.Size() == -1 ||
4163 var_face_dofs.Size() - 1 == pncmesh->GetNFaces() + pncmesh->GetNGhostFaces(),
4166 ghost_edge_orders.SetSize(pncmesh->GetNGhostEdges());
4167 ghost_face_orders.SetSize(pncmesh->GetNGhostFaces());
4169 for (
int i=0; i<pncmesh->GetNGhostEdges(); ++i)
4171 ghost_edge_orders[i] = GetEdgeOrder(pncmesh->GetNEdges() + i);
4174 if (pmesh->Dimension() > 2)
4176 for (
int i=0; i<pncmesh->GetNGhostFaces(); ++i)
4178 ghost_face_orders[i] = GetFaceOrder(pncmesh->GetNFaces() + i);
4183 var_edge_dofs.Swap(loc_var_edge_dofs);
4184 loc_var_edge_dofs.Clear();
4186 var_face_dofs.Swap(loc_var_face_dofs);
4187 loc_var_face_dofs.Clear();
4189 Swap(var_edge_orders, loc_var_edge_orders);
4190 Swap(var_face_orders, loc_var_face_orders);
4192 loc_var_edge_orders.SetSize(0);
4193 loc_var_face_orders.SetSize(0);
4197 *P_ = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
4198 dof_offs, tdof_offs);
4206 recv_msg.RecvDrop(rank, size, MyComm);
4210 for (
auto &msg : send_msg)
4215#ifdef MFEM_PMATRIX_STATS
4216 int n_rounds = send_msg.size();
4217 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
4218 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
4220 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
4221 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4222 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4223 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4224 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4225 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
4229 mfem::out <<
"P matrix stats (avg per rank): "
4230 <<
real_t(glob_rounds)/NRanks <<
" rounds, "
4231 <<
real_t(glob_msgs_sent)/NRanks <<
" msgs sent, "
4232 <<
real_t(glob_msgs_recv)/NRanks <<
" msgs recv, "
4233 <<
real_t(glob_rows_sent)/NRanks <<
" rows sent, "
4234 <<
real_t(glob_rows_recv)/NRanks <<
" rows recv, "
4235 <<
real_t(glob_rows_fwd)/NRanks <<
" rows forwarded."
4240 return num_true_dofs*vdim;
4243HypreParMatrix* ParFiniteElementSpace
4244::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
4245 int local_rows,
int local_cols,
4246 Array<HYPRE_BigInt> &row_starts,
4247 Array<HYPRE_BigInt> &col_starts)
const
4249 bool assumed = HYPRE_AssumedPartitionCheck();
4252 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
4253 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
4256 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
4257 std::map<HYPRE_BigInt, int> col_map;
4258 for (
int i = 0; i < local_rows; i++)
4260 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4262 const PMatrixElement &elem = rows[i].elems[j];
4264 if (col >= first_col && col < next_col)
4271 for (
int vd = 0; vd < vdim; vd++)
4281 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(
static_cast<int>(col_map.size()));
4283 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
4285 cmap[offd_col] = it->first;
4286 it->second = offd_col++;
4289 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
4290 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
4292 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
4293 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
4295 real_t *A_diag = Memory<real_t>(nnz_diag);
4296 real_t *A_offd = Memory<real_t>(nnz_offd);
4298 int vdim1 = bynodes ? vdim : 1;
4299 int vdim2 = bynodes ? 1 : vdim;
4300 int vdim_offset = bynodes ? local_cols : 1;
4303 nnz_diag = nnz_offd = 0;
4305 for (
int vd1 = 0; vd1 < vdim1; vd1++)
4307 for (
int i = 0; i < local_rows; i++)
4309 for (
int vd2 = 0; vd2 < vdim2; vd2++)
4311 I_diag[vrow] = nnz_diag;
4312 I_offd[vrow++] = nnz_offd;
4314 int vd = bynodes ? vd1 : vd2;
4315 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4317 const PMatrixElement &elem = rows[i].elems[j];
4318 if (elem.column >= first_col && elem.column < next_col)
4320 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
4321 A_diag[nnz_diag++] = elem.value;
4325 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
4326 A_offd[nnz_offd++] = elem.value;
4332 MFEM_ASSERT(vrow == vdim*local_rows,
"");
4333 I_diag[vrow] = nnz_diag;
4334 I_offd[vrow] = nnz_offd;
4336 return new HypreParMatrix(MyComm,
4337 row_starts.Last(), col_starts.Last(),
4338 row_starts.GetData(), col_starts.GetData(),
4339 I_diag, J_diag, A_diag,
4340 I_offd, J_offd, A_offd,
4341 static_cast<HYPRE_Int
>(col_map.size()), cmap);
4344template <
typename int_type>
4345static int_type* make_i_array(
int nrows)
4347 int_type *I = Memory<int_type>(nrows+1);
4348 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
4352template <
typename int_type>
4353static int_type* make_j_array(int_type* I,
int nrows)
4356 for (
int i = 0; i < nrows; i++)
4358 if (I[i] >= 0) { nnz++; }
4360 int_type *J = Memory<int_type>(nnz);
4363 for (
int i = 0, k = 0; i <= nrows; i++)
4365 int_type col = I[i];
4367 if (col >= 0) { J[k++] = col; }
4373ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
4374 const Table* old_elem_dof,
4375 const Table* old_elem_fos)
4377 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
4378 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
4379 "be called before ParFiniteElementSpace::RebalanceMatrix");
4381 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4382 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4385 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4391 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
4392 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
4393 "Mesh::Rebalance was not called before "
4394 "ParFiniteElementSpace::RebalanceMatrix");
4397 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
4398 for (
int i = 0; i < pmesh->
GetNE(); i++)
4400 if (old_index[i] >= 0)
4402 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
4405 for (
int vd = 0; vd <
vdim; vd++)
4407 for (
int j = 0; j < dofs.Size(); j++)
4410 if (row < 0) { row = -1 - row; }
4412 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
4413 if (col < 0) { col = -1 - col; }
4420 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
4423 Array<int> new_elements;
4424 Array<long> old_remote_dofs;
4425 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
4428 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
4429 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
4432 const long* old_dofs = &old_remote_dofs[pos];
4433 pos += dofs.Size() *
vdim;
4435 for (
int vd = 0; vd <
vdim; vd++)
4437 for (
int j = 0; j < dofs.Size(); j++)
4440 if (row < 0) { row = -1 - row; }
4442 if (i_diag[row] == i_diag[row+1])
4444 i_offd[row] = old_dofs[j + vd * dofs.Size()];
4451#ifndef HYPRE_MIXEDINT
4452 HYPRE_Int *i_offd_hi = i_offd;
4455 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
4456 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
4457 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
4461 int offd_cols = i_offd_hi[vsize];
4462 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
4463 for (
int i = 0; i < offd_cols; i++)
4465 cmap_offd[i].one = j_offd[i];
4466 cmap_offd[i].two = i;
4469#ifndef HYPRE_MIXEDINT
4470 HYPRE_Int *j_offd_hi = j_offd;
4472 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
4473 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
4479 for (
int i = 0; i < offd_cols; i++)
4481 cmap[i] = cmap_offd[i].one;
4482 j_offd_hi[cmap_offd[i].two] = i;
4486 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
4487 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
4492ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
4493 const Table* old_elem_dof,
4494 const Table *old_elem_fos)
4496 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
4498 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
4499 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
4502 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
4503 "Previous space is not finer.");
4511 Mesh::GeometryList elem_geoms(*
mesh);
4513 Array<int> dofs, old_dofs, old_vdofs;
4516 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4523 for (
int i = 0; i < elem_geoms.Size(); i++)
4529 const CoarseFineTransformations &dtrans =
4530 old_pncmesh->GetDerefinementTransforms();
4531 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
4535 std::map<int, std::vector<HYPRE_BigInt>> to_send;
4536 std::map<int, std::vector<HYPRE_BigInt>> to_recv;
4539 std::unordered_map<int, std::array<size_t, 2>> recv_messages;
4541 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4542 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4546 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4548 const Embedding &emb = dtrans.embeddings[k];
4550 int fine_rank = old_ranks[k];
4551 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
4552 : old_pncmesh->ElementRank(emb.parent);
4554 if (coarse_rank != MyRank && fine_rank == MyRank)
4556 old_elem_dof->GetRow(k, dofs);
4559 std::vector<HYPRE_BigInt>& send_buf = to_send[coarse_rank];
4560 auto pos = send_buf.size();
4561 send_buf.resize(pos + dofs.Size());
4562 for (
int i = 0; i < dofs.Size(); i++)
4564 send_buf[pos + i] = old_offset + dofs[i];
4567 else if (coarse_rank == MyRank && fine_rank != MyRank)
4569 MFEM_ASSERT(emb.parent >= 0,
"");
4572 std::vector<HYPRE_BigInt>& recv_buf = to_recv[fine_rank];
4573 auto& msg = recv_messages[k];
4574 msg[0] = recv_buf.size();
4575 recv_buf.resize(recv_buf.size() + ldof[geom] *
vdim);
4576 msg[1] = recv_buf.size();
4582 std::vector<MPI_Request> requests;
4583 requests.reserve(to_send.size() + to_recv.size());
4585 for (
auto &v : to_recv)
4587 requests.emplace_back();
4588 MPI_Irecv(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
4593 for (
auto &v : to_send)
4595 requests.emplace_back();
4596 MPI_Isend(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
4602 for (
int i = 0; i < elem_geoms.Size(); i++)
4608 SparseMatrix *diag =
new SparseMatrix(
ndofs*
vdim, old_ndofs*
vdim);
4610 Array<char> mark(diag->Height());
4615 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4617 const Embedding &emb = dtrans.embeddings[k];
4618 if (emb.parent < 0) {
continue; }
4620 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
4621 int fine_rank = old_ranks[k];
4623 if (coarse_rank == MyRank && fine_rank == MyRank)
4626 DenseMatrix &lR = localR[geom](emb.matrix);
4629 old_elem_dof->GetRow(k, old_dofs);
4631 for (
int vd = 0; vd <
vdim; vd++)
4633 old_dofs.Copy(old_vdofs);
4636 for (
int i = 0; i < lR.Height(); i++)
4638 if (!std::isfinite(lR(i, 0))) {
continue; }
4641 int m = (r >= 0) ? r : (-1 - r);
4643 if (is_dg || !mark[m])
4646 diag->SetRow(r, old_vdofs, row);
4656 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
4659 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
4661 std::map<HYPRE_BigInt, int> col_map;
4662 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4664 const Embedding &emb = dtrans.embeddings[k];
4665 if (emb.parent < 0) {
continue; }
4667 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
4668 int fine_rank = old_ranks[k];
4670 if (coarse_rank == MyRank && fine_rank != MyRank)
4673 DenseMatrix &lR = localR[geom](emb.matrix);
4677 auto& odofs = to_recv.at(fine_rank);
4678 auto &msg = recv_messages[k];
4679 MFEM_ASSERT(msg[1] > msg[0],
"");
4681 for (
int vd = 0; vd <
vdim; vd++)
4683 MFEM_ASSERT(ldof[geom],
"");
4684 HYPRE_BigInt *remote_dofs = odofs.data() + msg[0] + vd * ldof[geom];
4686 for (
int i = 0; i < lR.Height(); i++)
4688 if (!std::isfinite(lR(i, 0))) {
continue; }
4691 int m = (r >= 0) ? r : (-1 - r);
4693 if (is_dg || !mark[m])
4696 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
4697 for (
int j = 0; j < ldof[geom]; j++)
4699 if (row[j] == 0.0) {
continue; }
4700 int &lcol = col_map[remote_dofs[j]];
4701 if (!lcol) { lcol =
static_cast<int>(col_map.size()); }
4702 offd->_Set_(m, lcol-1, row[j]);
4712 offd->SetWidth(
static_cast<int>(col_map.size()));
4715 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
4716 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
4718 cmap[it->second-1] = it->first;
4725 int width = offd->Width();
4726 Array<Pair<HYPRE_BigInt, int> > reorder(width);
4727 for (
int i = 0; i < width; i++)
4729 reorder[i].one = cmap[i];
4734 Array<int> reindex(width);
4735 for (
int i = 0; i < width; i++)
4737 reindex[reorder[i].two] = i;
4738 cmap[i] = reorder[i].one;
4741 int *J = offd->GetJ();
4742 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
4744 J[i] = reindex[J[i]];
4746 offd->SortColumnIndices();
4749 HypreParMatrix* new_R;
4750 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
4751 dof_offsets, old_dof_offsets, diag, offd, cmap,
4754 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
4759void ParFiniteElementSpace::Destroy()
4770 delete Pconf; Pconf = NULL;
4771 delete Rconf; Rconf = NULL;
4774 delete gcomm; gcomm = NULL;
4783void ParFiniteElementSpace::CopyProlongationAndRestriction(
4784 const FiniteElementSpace &fes,
const Array<int> *perm)
4788 MFEM_VERIFY(pfes != NULL,
"");
4789 MFEM_VERIFY(P == NULL,
"");
4790 MFEM_VERIFY(R == NULL,
"");
4793 pfes->Dof_TrueDof_Matrix();
4795 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
4801 int n = perm->Size();
4802 perm_mat =
new SparseMatrix(n, fes.GetVSize());
4803 for (
int i=0; i<n; ++i)
4807 perm_mat->Set(i, j, s);
4809 perm_mat->Finalize();
4813 if (pfes->P != NULL)
4816 else { P =
new HypreParMatrix(*pfes->P); }
4819 else if (perm != NULL)
4825 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
4826 col_starts, perm_mat);
4829 if (pfes->R != NULL)
4831 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
4832 else { R =
new SparseMatrix(*pfes->R); }
4834 else if (perm != NULL)
4855 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
4866 false, Tgf.OwnsOperator(),
false));
4867 Tgf.SetOperatorOwner(
false);
4877 MPI_Allreduce(MPI_IN_PLACE, &int_orders_changed, 1, MPI_INT,
4882 MPI_Allreduce(MPI_IN_PLACE, &var, 1, MPI_INT, MPI_MAX, MyComm);
4899 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
4900 "each mesh modification.");
4909 Table* old_elem_dof = NULL;
4910 Table* old_elem_fos = NULL;
4921 Swap(dof_offsets, old_dof_offsets);
4946 old_elem_fos, old_ndofs));
4949 old_elem_dof = NULL;
4950 old_elem_fos = NULL;
4963 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
4973 false,
false,
true));
4980 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
4988 delete old_elem_dof;
4989 delete old_elem_fos;
4997 "p-refinement is not supported in this space");
5002 for (
int i = 0; i<pmesh->
GetNE(); i++)
5006 pfes_prev->Update(
false);
5009 for (
auto ref : refs)
5024void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
5027 MFEM_VERIFY(new_pmesh != NULL,
5028 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
5038 MPI_Allreduce(MPI_IN_PLACE, &order, 1, MPI_INT, MPI_MAX, MyComm);
5061 const int npref = ghost_orders.Size();
5062 for (
int i=0; i<npref; ++i)
5064 const int elem = ghost_orders[i].element;
5065 const int order = ghost_orders[i].order;
5071 for (
auto edge : edges) { edge_orders[edge] |= mask; }
5078 for (
auto face : faces) { face_orders[face] |= mask; }
5090 const int face = pncmesh->
GetNFaces() + i;
5093 if (orders == 0) {
continue; }
5097 for (
int order = 0; orders != 0; order++, orders >>= 1)
5106 MFEM_VERIFY(orderV0 > 0,
"");
5113 for (
auto edge : edges)
5115 edge_orders[edge] |= mask;
5122 : gc(gc_), local(local_)
5127 for (
int g=1; g<group_ldof.
Size(); ++g)
5131 n_external += group_ldof.
RowSize(g);
5134 int tsize = lsize - n_external;
5140 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5158 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
5160 gc(pfes.GroupComm()),
5166 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5185 for ( ; j < end; j++)
5191 for ( ; j <
Height(); j++)
5209 const int in_layout = 2;
5220 for (
int i = 0; i < m; i++)
5223 if (end > j) { std::copy(xdata+j-i, xdata+end-i, ydata+j); }
5226 if (
Width() > (j-m)) { std::copy(xdata+j-m, xdata+
Width(), ydata+j); }
5228 const int out_layout = 0;
5251 for (
int i = 0; i < m; i++)
5254 if (end > j) { std::copy(xdata+j, xdata+end, ydata+j-i); }
5257 if (
Height() > j) { std::copy(xdata+j, xdata+
Height(), ydata+j-m); }
5259 const int out_layout = 2;
5269 mpi_gpu_aware(
Device::GetGPUAwareMPI())
5272 const int tdofs = R->
Height();
5273 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
5288 unique_ltdof.
Sort();
5291 for (
int i = 0; i < shared_ltdof.
Size(); i++)
5293 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
5294 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
5323 int req_counter = 0;
5328 if (send_size > 0) { req_counter++; }
5332 if (recv_size > 0) { req_counter++; }
5334 requests =
new MPI_Request[req_counter];
5340 pfes.GetRestrictionMatrix(),
5343 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
5347static void ExtractSubVector(
const Array<int> &indices,
5350 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
5351 auto y = vout.
Write();
5352 const auto x = vin.
Read();
5353 const auto I = indices.
Read();
5371static void SetSubVector(
const Array<int> &indices,
5374 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
5377 const auto x = vin.
Read();
5378 const auto I = indices.
Read();
5405 int req_counter = 0;
5444 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
5475static void AddSubVector(
const Array<int> &unique_dst_indices,
5482 const auto x = src.
Read();
5483 const auto DST_I = unique_dst_indices.
Read();
5484 const auto SRC_O = unique_to_src_offsets.
Read();
5485 const auto SRC_I = unique_to_src_indices.
Read();
5488 const int dst_idx = DST_I[i];
5490 const int end = SRC_O[i+1];
5491 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
5507 int req_counter = 0;
5536 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
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. Warning: this method casts away constness.
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.
static MemoryType GetDeviceMemoryType()
Get the current Device MemoryType. This is the MemoryType used by most MFEM classes when allocating m...
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].
const int * GetDofOrdering(Geometry::Type geom, int p, int ori) const
Variable order version of DofOrderForOrientation().
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
int GetNumDof(Geometry::Type geom, int p) const
Variable order version of DofForGeometry().
const FiniteElement * GetFE(Geometry::Type geom, int p) const
Variable order version of FiniteElementForGeometry().
@ DISCONTINUOUS
Field is discontinuous across element interfaces.
@ TANGENTIAL
Tangential components of vector field.
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.
bool IsVariableOrderH1() const
Returns true if the space is H1 and has variable-order elements.
Array< int > face_min_nghb_order
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...
std::shared_ptr< PRefinementTransferOperator > PTh
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...
virtual void GetExteriorVDofs(Array< int > &exterior_vdofs, int component=-1) const
Mark degrees of freedom associated with exterior faces of the mesh. For spaces with 'vdim' > 1,...
int GetEdgeOrder(int edge, int variant=0) const
int GetFaceOrder(int face, int variant=0) const
Returns the polynomial degree of the i'th face finite element.
int GetNumBorderDofs(Geometry::Type geom, int order) const
static int MinOrder(VarOrderBits bits)
Return the minimum order (least significant bit set) in the bit mask.
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
bool orders_changed
True if at least one element order changed (variable-order space only).
friend class PRefinementTransferOperator
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
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.).
Array< char > var_edge_orders
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
void UpdateElementOrders()
Resize the elem_order array on mesh change.
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 GetNE() const
Returns number of elements in the mesh.
int vdim
Vector dimension (number of unknowns per degree of freedom).
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
bool lastUpdatePRef
Flag to indicate whether the last update was for p-refinement.
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 GetEssentialBdrEdgesFaces(const Array< int > &bdr_attr_is_ess, std::set< int > &edges, std::set< int > &faces) const
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)
std::uint64_t VarOrderBits
Bit-mask representing a set of orders needed by an edge/face.
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.
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Mesh * mesh
The mesh that FE space lives on (not owned).
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
const FiniteElementCollection * FEColl() const
int GetNVariants(int entity, int index) const
Return number of possible DOF variants for edge/face (var. order spaces).
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)
Array< int > edge_min_nghb_order
Minimum order among neighboring elements.
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
bool PRefinementSupported()
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.
int FindEdgeDof(int edge, int ndof) const
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.
Operator that extracts face degrees of freedom for L2 interface spaces.
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.
int GetNEdges() const
Return the number of edges.
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 GetNFaces() const
Return the number of faces in a 3D mesh.
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
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
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 GetTypicalFaceGeometry() const
If the local mesh is not empty, return GetFaceGeometry(0); otherwise return a typical face geometry p...
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.
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
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.
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
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
int GetMaxElementOrder() const override
Returns the maximum polynomial order over all elements globally.
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()
void GetExteriorTrueDofs(Array< int > &ext_tdof_list, int component=-1) const override
void GetExteriorVDofs(Array< int > &ext_dofs, int component=-1) const override
Determine the external degrees of freedom.
HYPRE_BigInt GlobalVSize() const
const Operator * GetRestrictionOperator() const override
int GetLocalTDofNumber(int ldof) const
friend struct ParDerefineMatrixOp
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.
void ApplyGhostElementOrdersToEdgesAndFaces(Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const override
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.
const FiniteElement * GetFaceNbrFE(int i, int ndofs=0) const
void GhostFaceOrderToEdges(const Array< VarOrderBits > &face_orders, Array< VarOrderBits > &edge_orders) const override
HYPRE_BigInt GetMyDofOffset() const
HYPRE_BigInt * GetDofOffsets() const
Array< HYPRE_BigInt > face_nbr_glob_dof_map
bool Nonconforming() const
void GetEssentialTrueDofsVar(const Array< int > &bdr_attr_is_ess, const Array< int > &ess_dofs, Array< int > &true_ess_dofs, int component) const
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
const Operator * GetProlongationMatrix() const override
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
bool OrderPropagation(const std::set< int > &edges, const std::set< int > &faces, Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const override
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-provided DofTransformation object.
void PRefineAndUpdate(const Array< pRefinement > &refs, bool want_transfer=true) override
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 MarkIntermediateEntityDofs(int entity, Array< bool > &intermediate) const
void Lose_Dof_TrueDof_Matrix()
const FiniteElement * GetFaceNbrFaceFE(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-provided 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
Accessors for entities within a shared group structure.
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.
GroupId GetEntityGroupId(int entity, int index)
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.
void FindEdgesOfGhostElement(int elem, Array< int > &edges)
void FindFacesOfGhostElement(int elem, Array< int > &faces)
int GetNGhostFaces() const
void EncodeGroups(std::ostream &os, const Array< GroupId > &ids)
void FindEdgesOfGhostFace(int face, Array< int > &edges)
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 GetMyRank() const
Return the MPI rank for this process.
int GetNGhostVertices() const
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
void CommunicateGhostData(const Array< VarOrderElemInfo > &sendData, Array< VarOrderElemInfo > &recvData)
Parallel version of NURBSExtension.
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
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
void LoseData()
Releases ownership of and null-ifies the data.
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)
int Size() const
Returns the number of TYPE I elements.
int Size_of_connections() const
Returns the number of connections in the table.
void AddColumnsInRow(int r, int ncol)
Memory< int > & GetJMemory()
void AddAColumnInRow(int r)
void SetDims(int rows, int nnz)
Set the rows and the number of all connections for the table.
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.
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
BiLinear2DFiniteElement QuadrilateralFE
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
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
@ DEREFINEMENT_MATRIX_CONSTRUCTION_DATA
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)