36 ParInit(pmesh ? pmesh : orig.pmesh);
52 f ?
f : global_fes->FEColl(),
53 global_fes->GetVDim(), global_fes->GetOrdering())
72 int dim,
int ordering)
82 if (globNURBSext == NULL) {
return NULL; }
83 const ParNURBSExtension *pNURBSext =
84 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
85 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
87 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
89 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
92void ParFiniteElementSpace::ParInit(ParMesh *pm)
115 MFEM_ASSERT(
own_ext,
"internal error");
117 ParNURBSExtension *pNe =
new ParNURBSExtension(
136void ParFiniteElementSpace::CommunicateGhostOrder()
141 "Variable-order space requires a nonconforming mesh.");
148 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
149 "each mesh modification.");
159 int global_orders_changed = 0;
161 MPI_Allreduce(&local_orders_changed, &global_orders_changed, 1, MPI_INT,
164 if ((global_orders_changed == 0 && !href) || NRanks == 1)
171 Array<ParNCMesh::VarOrderElemInfo> localOrders(
mesh->
GetNE());
174 ParNCMesh::VarOrderElemInfo order_i{(
unsigned int) i,
elem_order[i]};
175 localOrders[i] = order_i;
181void ParFiniteElementSpace::Construct()
185 ConstructTrueNURBSDofs();
186 GenerateGlobalOffsets();
191 GenerateGlobalOffsets();
204 ngedofs = ngfdofs = 0;
220 const int ghostEdge = pncmesh->
GetNEdges() + i;
222 for (
int var=0; var<nvar; ++var)
247 const int ghostFace = pncmesh->
GetNFaces() + i;
249 for (
int var=0; var<nvar; ++var)
267 ngdofs = ngvdofs + ngedofs + ngfdofs;
277 ltdof_size = BuildParallelConformingInterpolation(
278 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
288 long long ltdofs = ltdof_size;
289 long long min_ltdofs, max_ltdofs, sum_ltdofs;
291 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
292 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
293 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
298 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
299 <<
", avg " << std::fixed << std::setprecision(1) << avg
300 <<
", max " << max_ltdofs
301 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
309 mfem::out <<
"True DOFs by rank: " << ltdofs;
310 for (
int i = 1; i < NRanks; i++)
313 MPI_Recv(<dofs, 1, MPI_LONG_LONG, i, 123, MyComm, &status);
320 MPI_Send(<dofs, 1, MPI_LONG_LONG, 0, 123, MyComm);
325void ParFiniteElementSpace::GetGroupComm(
330 int nvd, ned, ntd = 0, nqd = 0;
333 int group_ldof_counter;
358 group_ldof_counter = 0;
359 for (gr = 1; gr < ng; gr++)
362 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
368 group_ldof_counter *=
vdim;
371 group_ldof.
SetDims(ng, group_ldof_counter);
374 group_ldof_counter = 0;
375 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
376 for (gr = 1; gr < ng; gr++)
378 int j, k, l, m, o, nv, ne, nt, nq;
389 for (j = 0; j < nv; j++)
395 for (l = 0; l < nvd; l++, m++)
405 for (l = 0; l < dofs.
Size(); l++)
407 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
415 for (j = 0; j < ne; j++)
422 for (l = 0; l < ned; l++)
426 dofs[l] = m + (-1-ind[l]);
429 (*g_ldof_sign)[dofs[l]] = -1;
434 dofs[l] = m + ind[l];
443 for (l = 0; l < dofs.
Size(); l++)
445 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
453 for (j = 0; j < nt; j++)
460 for (l = 0; l < ntd; l++)
464 dofs[l] = m + (-1-ind[l]);
467 (*g_ldof_sign)[dofs[l]] = -1;
472 dofs[l] = m + ind[l];
481 for (l = 0; l < dofs.
Size(); l++)
483 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
491 for (j = 0; j < nq; j++)
498 for (l = 0; l < nqd; l++)
502 dofs[l] = m + (-1-ind[l]);
505 (*g_ldof_sign)[dofs[l]] = -1;
510 dofs[l] = m + ind[l];
519 for (l = 0; l < dofs.
Size(); l++)
521 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
526 group_ldof.
GetI()[gr+1] = group_ldof_counter;
532void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
536 for (
int i = 0; i < dofs.Size(); i++)
540 if (ldof_sign[-1-dofs[i]] < 0)
542 dofs[i] = -1-dofs[i];
547 if (ldof_sign[dofs[i]] < 0)
549 dofs[i] = -1-dofs[i];
555void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
557 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
558 ApplyLDofSigns(all_dofs);
582 ApplyLDofSigns(dofs);
607 ApplyLDofSigns(dofs);
614 if (
face_dof !=
nullptr && variant == 0)
622 ApplyLDofSigns(dofs);
640 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
641 auto itr =
L2F.find(key);
642 if (itr !=
L2F.end())
684 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
685 pmesh->
GroupEdge(group, ei, l_edge, ori);
695 for (
int i = 0; i < dofs.
Size(); i++)
697 const int di = dofs[i];
698 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
707 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
708 "invalid triangular face index");
719 for (
int i = 0; i < dofs.
Size(); i++)
721 const int di = dofs[i];
722 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
731 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
732 "invalid quadrilateral face index");
743 for (
int i = 0; i < dofs.
Size(); i++)
745 const int di = dofs[i];
746 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
751void ParFiniteElementSpace::GenerateGlobalOffsets()
const
763 if (HYPRE_AssumedPartitionCheck())
766 GroupTopology > = GetGroupTopo();
767 int nsize = gt.GetNumNeighbors()-1;
768 MPI_Request *requests =
new MPI_Request[2*nsize];
769 MPI_Status *statuses =
new MPI_Status[2*nsize];
770 tdof_nb_offsets.
SetSize(nsize+1);
771 tdof_nb_offsets[0] = tdof_offsets[0];
774 int request_counter = 0;
775 for (
int i = 1; i <= nsize; i++)
777 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
778 gt.GetNeighborRank(i), 5365, MyComm,
779 &requests[request_counter++]);
781 for (
int i = 1; i <= nsize; i++)
783 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
784 gt.GetNeighborRank(i), 5365, MyComm,
785 &requests[request_counter++]);
787 MPI_Waitall(request_counter, requests, statuses);
794void ParFiniteElementSpace::CheckNDSTriaDofs()
797 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
818 for (
int g = 1; g < ngrps; g++)
825 int loc_nd_strias = strias ? 1 : 0;
826 int glb_nd_strias = 0;
827 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1, MPI_INT, MPI_SUM, MyComm);
828 nd_strias = glb_nd_strias > 0;
831void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
843 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
844 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
847 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
848 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
856 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
858 i_diag[0] = i_offd[0] = 0;
859 diag_counter = offd_counter = 0;
860 for (
int i = 0; i < ldof; i++)
865 j_diag[diag_counter++] = ltdof_i;
870 cmap_j_offd[offd_counter].two = offd_counter;
873 i_diag[i+1] = diag_counter;
874 i_offd[i+1] = offd_counter;
879 for (
int i = 0; i < offd_counter; i++)
881 cmap[i] = cmap_j_offd[i].one;
882 j_offd[cmap_j_offd[i].two] = i;
885 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
886 i_diag, j_diag, i_offd, j_offd,
898 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
899 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
906 Array<int> ldsize(ldof); ldsize = 0;
907 Array<int> ltori(ldof); ltori = 0;
912 for (
int g = 1; g < ngrps; g++)
921 for (
int i=0; i<sdofs.Size(); i++)
923 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
924 if (ldsize[ind] == 0) { nnz_offd++; }
930 int face, ori, info1, info2;
934 for (
int i=0; i<3*
nedofs; i++)
936 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
937 if (ldsize[ind] == 0) { nnz_offd++; }
940 for (
int i=3*
nedofs; i<sdofs.Size(); i++)
942 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
943 ldsize[sdofs[i]] = 2;
944 ltori[sdofs[i]] = info2 % 64;
950 for (
int i=0; i<sdofs.Size(); i++)
952 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
953 if (ldsize[ind] == 0) { nnz_offd++; }
960 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
961 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
962 real_t *d_diag = Memory<real_t>(ltdof);
965 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
966 HYPRE_Int *j_offd = Memory<HYPRE_Int>(nnz_offd);
967 real_t *d_offd = Memory<real_t>(nnz_offd);
975 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
977 i_diag[0] = i_offd[0] = 0;
978 diag_counter = offd_counter = 0;
979 int offd_col_counter = 0;
980 for (
int i = 0; i < ldof; i++)
985 j_diag[diag_counter] = ltdofi;
986 d_diag[diag_counter++] = 1.0;
993 cmap_j_offd[offd_col_counter].two = offd_counter;
1000 cmap_j_offd[offd_col_counter].two = offd_counter;
1003 i_diag[i+1] = diag_counter;
1004 i_offd[i+1] = offd_counter;
1007 cmap_j_offd[offd_col_counter].two = offd_counter;
1012 i_diag[i+1] = diag_counter;
1013 i_offd[i+1] = offd_counter;
1018 for (
int i = 0; i < nnz_offd; i++)
1024 for (
int i = 0; i < offd_col_counter; i++)
1026 cmap[i] = cmap_j_offd[i].one;
1027 j_offd[cmap_j_offd[i].two] = i;
1030 for (
int i = 0; i < ldof; i++)
1032 if (i_offd[i+1] == i_offd[i] + 1)
1034 d_offd[i_offd[i]] = 1.0;
1036 else if (i_offd[i+1] == i_offd[i] + 2)
1040 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
1041 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
1043 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
1044 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
1045 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
1049 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
1050 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
1051 offd_col_counter, cmap);
1063 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
1064 P_pc_col_starts, NULL,
true);
1073 for (
int i = 0; i < ldof_group.
Size(); i++)
1077 if (ldof_ltdof[i] >= 0)
1095 gc->
Create(pNURBSext()->ldof_group);
1099 GetGroupComm(*gc, 0);
1109 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1113 gcomm->
Bcast(ldof_marker);
1118 int component)
const
1130 int component)
const
1151 const int *ess_dofs_data = ess_dofs.
HostRead();
1152 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1154 const int *ted = true_ess_dofs.
HostRead();
1155 std::string error_msg =
"failed dof: ";
1156 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1158 if (
bool(ted[i]) !=
bool(true_ess_dofs2[i]))
1160 error_msg += std::to_string(i) +=
"(R ";
1161 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1162 error_msg += std::to_string(
bool(true_ess_dofs2[i])) +=
") ";
1171 MFEM_ASSERT(R->
Width() == ess_dofs.
Size(),
"!");
1172 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1173 <<
", rank = " << MyRank <<
", " << error_msg);
1184 int component)
const
1187 "GetEssentialTrueDofsVar is only for variable-order spaces");
1191 const int ntdofs = tdof2ldof.
Size();
1193 vdim * ntdofs == true_ess_dofs.
Size(),
"");
1199 const int vdim_factor = bynodes ? 1 :
vdim;
1201 const int tdof_stride = bynodes ? num_true_dofs : 1;
1204 for (
int l=0; l<
ndofs; ++l)
1206 const int tdof = ldof_ltdof[l];
1207 if (tdof >= 0 && ess_dofs[l])
1209 for (
int vd = 0; vd <
vdim; vd++)
1211 if (component >= 0 && vd != component) {
continue; }
1212 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1213 true_ess_dofs[vtdof] = 1;
1219 std::set<int> edges, faces;
1223 for (
int tdof=0; tdof<ntdofs; ++tdof)
1226 if (!tdof2ldof[tdof].set) {
continue; }
1228 const bool edge = tdof2ldof[tdof].isEdge;
1229 const int index = tdof2ldof[tdof].idx;
1231 const bool bdry = edge ? edges.count(
index) > 0 : faces.count(
index) > 0;
1232 if (!bdry) {
continue; }
1234 for (
int vd = 0; vd <
vdim; vd++)
1236 if (component >= 0 && vd != component) {
continue; }
1237 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1238 true_ess_dofs[vtdof] = 1;
1244 int component)
const
1254 int component)
const
1266 const int *ext_dofs_data = ext_dofs.
HostRead();
1267 Pt->BooleanMult(1, ext_dofs_data, 0, true_ext_dofs2);
1269 const int *ted = true_ext_dofs.
HostRead();
1270 std::string error_msg =
"failed dof: ";
1271 for (
int i = 0; i < true_ext_dofs.
Size(); i++)
1273 if (
bool(ted[i]) !=
bool(true_ext_dofs2[i]))
1275 error_msg += std::to_string(i) +=
"(R ";
1276 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1277 error_msg += std::to_string(
bool(true_ext_dofs2[i])) +=
") ";
1283 MFEM_ASSERT(R->
Width() == ext_dofs.
Size(),
"!");
1284 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1285 <<
", rank = " << MyRank <<
", " << error_msg);
1297 return ldof_ltdof[ldof];
1301 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1303 return ldof_ltdof[ldof];
1316 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1322 if (HYPRE_AssumedPartitionCheck())
1324 return ldof_ltdof[ldof] +
1325 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1329 return ldof_ltdof[ldof] +
1339 MFEM_ABORT(
"Not implemented for NC mesh.");
1342 if (HYPRE_AssumedPartitionCheck())
1346 return ldof_ltdof[sldof] +
1348 ldof_group[sldof])] /
vdim;
1352 return (ldof_ltdof[sldof*
vdim] +
1353 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1360 return ldof_ltdof[sldof] +
1362 ldof_group[sldof])] /
vdim;
1366 return (ldof_ltdof[sldof*
vdim] +
1367 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1374 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1379 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1386 if (Pconf) {
return Pconf; }
1417 if (Rconf) {
return Rconf; }
1454 if (num_face_nbrs == 0)
1460 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1461 MPI_Request *send_requests = requests;
1462 MPI_Request *recv_requests = requests + num_face_nbrs;
1463 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1469 Table send_nbr_elem_dof;
1476 for (
int fn = 0; fn < num_face_nbrs; fn++)
1481 for (
int i = 0; i < num_my_elems; i++)
1484 for (
int j = 0; j < ldofs.
Size(); j++)
1486 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1488 if (ldof_marker[ldof] != fn)
1490 ldof_marker[ldof] = fn;
1500 MyComm, &send_requests[fn]);
1503 MyComm, &recv_requests[fn]);
1506 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1511 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1519 int *send_I = send_nbr_elem_dof.
GetI();
1521 for (
int fn = 0; fn < num_face_nbrs; fn++)
1525 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1526 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1528 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1529 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1532 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1533 send_nbr_elem_dof.
MakeJ();
1537 for (
int fn = 0; fn < num_face_nbrs; fn++)
1542 for (
int i = 0; i < num_my_elems; i++)
1545 for (
int j = 0; j < ldofs.
Size(); j++)
1547 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1549 if (ldof_marker[ldof] != fn)
1551 ldof_marker[ldof] = fn;
1556 send_el_off[fn] + i, ldofs, ldofs.
Size());
1563 int *send_J = send_nbr_elem_dof.
GetJ();
1564 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1568 int j_end = send_I[send_el_off[fn+1]];
1570 for (
int i = 0; i < num_ldofs; i++)
1572 int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
1573 ldof_marker[ldof] = i;
1576 for ( ; j < j_end; j++)
1578 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1579 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1583 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1590 for (
int fn = 0; fn < num_face_nbrs; fn++)
1595 MPI_Isend(send_J + send_I[send_el_off[fn]],
1596 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1597 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1599 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1600 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1601 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1604 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1607 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1610 int j_end = recv_I[recv_el_off[fn+1]];
1612 for ( ; j < j_end; j++)
1625 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1629 for (
int fn = 0; fn < num_face_nbrs; fn++)
1636 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1640 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1643 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1644 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1651 for (
int fn = 0; fn < num_face_nbrs; fn++)
1656 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1657 MyComm, &send_requests[fn]);
1659 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1660 MyComm, &recv_requests[fn]);
1663 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1667 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1681 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1717 int el1, el2, inf1, inf2;
1730 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1732 for (
int j = 0; j < vdofs.
Size(); j++)
1734 const int ldof = vdofs[j];
1735 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1743 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
1744 " does not support NURBS!");
1753 const int ndofs_order = FE->
GetDof();
1754 if (ndofs_order ==
ndofs)
1758 else if (ndofs_order >
ndofs)
1760 MFEM_ABORT(
"Finite element order not found in GetFaceNbrFE");
1786#if MFEM_HYPRE_VERSION <= 22200
1787 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1788 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1789 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1798void ParFiniteElementSpace::ConstructTrueDofs()
1805 GetGroupComm(*gcomm, 1, &ldof_sign);
1815 for (gr = 1; gr < group_ldof.
Size(); gr++)
1817 const int *ldofs = group_ldof.
GetRow(gr);
1818 const int nldofs = group_ldof.
RowSize(gr);
1819 for (i = 0; i < nldofs; i++)
1821 ldof_group[ldofs[i]] = gr;
1826 for (i = 0; i < nldofs; i++)
1828 ldof_ltdof[ldofs[i]] = -2;
1835 for (i = 0; i < n; i++)
1837 if (ldof_ltdof[i] == -1)
1839 ldof_ltdof[i] = ltdof_size++;
1845 gcomm->
Bcast(ldof_ltdof);
1848void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1851 GroupTopology > = pNURBSext()->
gtopo;
1852 gcomm =
new GroupCommunicator(gt);
1857 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1861 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1863 for (
int i = 0; i < n; i++)
1865 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1869 gcomm->
Create(ldof_group);
1877 for (
int i = 0; i < n; i++)
1879 if (gt.IAmMaster(ldof_group[i]))
1881 ldof_ltdof[i] = ltdof_size;
1892 gcomm->
Bcast(ldof_ltdof);
1895void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1896 Array<int> &dofs)
const
1900 for (
int j = 0; j < nv; j++)
1902 dofs[j] =
ndofs + nv*
id.index + j;
1906static const char* msg_orders_changed =
1907 "Element orders changed, you need to Update() the space first.";
1909void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1910 Array<int> &dofs,
int variant)
const
1914 int order, ne, base;
1917 const int edge = edge_id.index;
1920 base = beg[variant];
1921 ne = beg[variant+1] - base;
1932 base = (edge_id.index - pncmesh->
GetNEdges())*ne;
1937 dofs.SetSize(2*nv + ne);
1942 for (
int i = 0; i < 2; i++)
1944 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1945 for (
int j = 0; j < nv; j++)
1947 dofs[i*nv + j] = k++;
1951 int k =
ndofs + ngvdofs + base;
1952 for (
int j = 0; j < ne; j++)
1954 dofs[2*nv + j] = k++;
1958void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1959 Array<int> &dofs)
const
1963 int nfv, V[4], E[4], Eo[4];
1970 int nf = (nfv == 3) ? nf_tri : nf_quad;
1972 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1974 Array<int> evar(nfv);
1979 const int face = face_id.index;
1981 constexpr int variant = 0;
1983 base = beg[variant];
1984 nf = beg[variant+1] - base;
1991 for (
int i = 0; i < nfv; i++)
2007 MFEM_VERIFY(eo == fo,
"Edge must have same order as face");
2010 const int ne_i = ebeg[evar[i] + 1] - ebeg[evar[i]];
2014 dofs.SetSize((nfv * nv) + allne + nf);
2018 base = nf_quad * ghost_face_index;
2022 dofs.SetSize(nfv*(nv + ne) + nf);
2026 for (
int i = 0; i < nfv; i++)
2029 const int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
2030 for (
int j = 0; j < nv; j++)
2032 dofs[offset++] = first + j;
2036 for (
int i = 0; i < nfv; i++)
2041 const int variant = evar[i];
2044 int ebase = beg[variant];
2045 ne = beg[variant+1] - ebase;
2047 MFEM_ASSERT(ebase ==
FindEdgeDof(E[i], ne),
"sanity check?");
2049 const int first = (E[i] < ghost) ?
nvdofs + ebase
2057 for (
int j = 0; j < ne; j++)
2059 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
2060 : (-1 - (first + (-1 - ind[j])));
2065 const int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
2066 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
2068 for (
int j = 0; j < ne; j++)
2070 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
2071 : (-1 - (first + (-1 - ind[j])));
2076 const int first =
ndofs + ngvdofs + ngedofs + base;
2077 for (
int j = 0; j < nf; j++)
2079 dofs[offset++] = first + j;
2083void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
2084 Array<int> &dofs,
int var)
const
2089 case 0: GetGhostVertexDofs(
id, dofs);
break;
2090 case 1: GetGhostEdgeDofs(
id, dofs, var);
break;
2091 case 2: GetGhostFaceDofs(
id, dofs);
break;
2095void ParFiniteElementSpace::GetBareDofsVar(
int entity,
int index,
2096 Array<int> &dofs)
const
2098 int ned, ghost, first;
2104 first = (
index < ghost)
2113 ned = rowNext[0] - row[0];
2114 first = (
index < ghost)
2130 first =
ndofs + ngvdofs + ngedofs + row0 -
nfdofs;
2137 for (
int i = 0; i < ned; i++)
2139 dofs[i] = first + i;
2143void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
2144 Array<int> &dofs)
const
2148 GetBareDofsVar(entity,
index, dofs);
2152 int ned, ghost, first;
2158 first = (
index < ghost)
2166 first = (
index < ghost)
2187 first =
ndofs + ngvdofs + ngedofs +
index*stride;
2193 for (
int i = 0; i < ned; i++)
2195 dofs[i] = first + i;
2199int ParFiniteElementSpace::PackDofVar(
int entity,
int index,
int edof,
2210 return (
index < ghost)
2219 const int d = row[var] + edof;
2243static int bisect(
const int* array,
int size,
int value)
2245 const int* end = array + size;
2246 const int* pos = std::upper_bound(array, end, value);
2247 MFEM_VERIFY(pos != array,
"value not found");
2250 MFEM_VERIFY(*(array+size - 1) == value,
"Last entry must be exact")
2252 return pos - array - 1;
2255void ParFiniteElementSpace::UnpackDofVar(
int dof,
int &entity,
int &
index,
2256 int &edof,
int &order)
const
2259 MFEM_ASSERT(dof >= 0,
"");
2265 entity = 0,
index = dof / nv, edof = dof % nv;
2272 index = var_edge_dofmap[dof].index;
2273 edof = var_edge_dofmap[dof].edof;
2278 const int edge =
index;
2280 for (
int v=0; v<nvar; ++v)
2284 if (edof < os + dofs)
2293 MFEM_ASSERT(order >= 0,
"");
2302 index = var_face_dofmap[dof].index;
2303 edof = var_face_dofmap[dof].edof;
2308 const int face =
index;
2311 for (
int v=0; v<nvar; ++v)
2315 if (edof < os + dofs)
2324 MFEM_ASSERT(order >= 0,
"");
2329 MFEM_ABORT(
"Cannot unpack internal DOF");
2346 edof = var_edge_dofmap[dof +
nedofs].edof;
2355 edof = var_face_dofmap[dof +
nfdofs].edof;
2358 MFEM_ABORT(
"Out of range DOF.");
2362int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof,
2367 return PackDofVar(entity,
index, edof, var);
2380 return (
index < ghost)
2388 return (
index < ghost)
2390 :
ndofs + ngvdofs + (
index - ghost)*ned + edof;
2404 return ndofs + ngvdofs + ngedofs +
index*stride + edof;
2412void ParFiniteElementSpace::UnpackDof(
int dof,
2413 int &entity,
int &
index,
2414 int &edof,
int &order)
const
2420 UnpackDofVar(dof, entity,
index, edof, order);
2424 MFEM_ASSERT(dof >= 0,
"");
2430 entity = 0,
index = dof / nv, edof = dof % nv;
2437 entity = 1,
index = dof / ne, edof = dof % ne;
2446 index = dof / nf, edof = dof % nf;
2452 MFEM_ASSERT(table.Size() > 0,
"");
2453 int jpos =
bisect(table.GetJ(), table.Size_of_connections(), dof);
2455 edof = dof - table.GetRow(
index)[0];
2460 MFEM_ABORT(
"Cannot unpack internal DOF");
2475 entity = 1,
index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
2482 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
2486 MFEM_ABORT(
"Out of range DOF.");
2494struct PMatrixElement
2500 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
2501 : column(col), stride(str), value(val) {}
2503 bool operator<(
const PMatrixElement &other)
const
2504 {
return column < other.column; }
2506 typedef std::vector<PMatrixElement> List;
2514 PMatrixElement::List elems;
2517 void AddRow(
const PMatrixRow &other,
real_t coef)
2519 elems.reserve(elems.size() + other.elems.size());
2520 for (
const PMatrixElement &oei : other.elems)
2522 elems.emplace_back(oei.column, oei.stride, coef * oei.value);
2529 if (!elems.size()) {
return; }
2530 std::sort(elems.begin(), elems.end());
2533 for (
unsigned i = 1; i < elems.size(); i++)
2535 if (elems[j].column == elems[i].column)
2537 elems[j].value += elems[i].value;
2541 elems[++j] = elems[i];
2547 void write(std::ostream &os,
real_t sign)
const
2550 for (
unsigned i = 0; i < elems.size(); i++)
2552 const PMatrixElement &e = elems[i];
2559 void read(std::istream &is,
real_t sign)
2562 for (
unsigned i = 0; i < elems.size(); i++)
2564 PMatrixElement &e = elems[i];
2572class NeighborOrderMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ORDER_VM>
2575 typedef NCMesh::MeshId MeshId;
2580 int entity,
index, order;
2583 OrderInfo(
int ent,
int idx,
int p, GroupId grp)
2584 : entity(ent),
index(idx), order(
p), group(grp) {}
2587 NeighborOrderMessage() : pncmesh(NULL) {}
2589 void AddOrder(
int ent,
int idx,
int p, GroupId grp)
2591 msgs.emplace_back(ent, idx,
p, grp);
2594 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2596 const std::vector<OrderInfo>& GetMsgs()
const {
return msgs; }
2598 typedef std::map<int, NeighborOrderMessage> Map;
2601 std::vector<OrderInfo> msgs;
2606 void Encode(
int rank)
override;
2608 void Decode(
int rank)
override;
2611void NeighborOrderMessage::Encode(
int rank)
2613 std::ostringstream stream;
2615 Array<MeshId> ent_ids[3];
2616 Array<GroupId> group_ids[3];
2617 Array<int> row_idx[3];
2620 for (
unsigned i = 0; i < msgs.size(); i++)
2622 const OrderInfo &ri = msgs[i];
2624 ent_ids[ri.entity].Append(
id);
2625 row_idx[ri.entity].Append(i);
2626 group_ids[ri.entity].Append(ri.group);
2629 Array<GroupId> all_group_ids;
2630 all_group_ids.Reserve(msgs.size());
2631 for (
int i = 0; i < 3; i++)
2633 all_group_ids.Append(group_ids[i]);
2641 for (
int ent = 0; ent < 3; ent++)
2643 for (
int i = 0; i < ent_ids[ent].Size(); i++)
2645 const OrderInfo &ri = msgs[row_idx[ent][i]];
2646 MFEM_ASSERT(ent == ri.entity,
"");
2653 stream.str().swap(
data);
2656void NeighborOrderMessage::Decode(
int rank)
2658 std::istringstream stream(
data);
2660 Array<MeshId> ent_ids[3];
2661 Array<GroupId> group_ids;
2667 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2668 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2671 msgs.reserve(nrows);
2674 for (
int ent = 1, gi = 0; ent < 3; ent++)
2677 const Array<MeshId> &ids = ent_ids[ent];
2678 for (
int i = 0; i < ids.Size(); i++)
2680 const MeshId &
id = ids[i];
2686 msgs.emplace_back(ent,
id.
index, order_i, group_ids[gi++]);
2694class NeighborRowMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ROW_VM>
2697 typedef NCMesh::MeshId MeshId;
2701 int entity,
index, edof, var;
2705 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row,
2707 : entity(ent),
index(idx), edof(edof), var(v), group(grp), row(row) {}
2709 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
int v = 0)
2710 : entity(ent),
index(idx), edof(edof), var(v), group(grp) {}
2713 NeighborRowMessage() : pncmesh(NULL) {}
2715 void AddRow(
int entity,
int index,
int edof, GroupId group,
2716 const PMatrixRow &row,
int order)
2719 if (varOrder && entity == 1)
2725 MFEM_ASSERT(order_v >= 0,
"");
2726 if (order == order_v)
2740 else if (varOrder && entity == 2)
2746 MFEM_ASSERT(order_v >= 0,
"");
2747 if (order == order_v)
2763 rows.emplace_back(entity,
index, edof, group, row, var);
2766 const std::vector<RowInfo>& GetRows()
const {
return rows; }
2768 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2769 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
2770 void SetSpace(
const ParFiniteElementSpace* fes_)
2776 typedef std::map<int, NeighborRowMessage> Map;
2779 std::vector<RowInfo> rows;
2782 const FiniteElementCollection* fec;
2783 const ParFiniteElementSpace* fes;
2785 bool varOrder =
false;
2787 int GetEdgeVarOffset(
int edge,
int var);
2788 int GetFaceVarOffset(
int face,
int var);
2791 void Encode(
int rank)
override;
2793 void Decode(
int rank)
override;
2796void NeighborRowMessage::Encode(
int rank)
2798 std::ostringstream stream;
2800 Array<MeshId> ent_ids[3];
2801 Array<GroupId> group_ids[3];
2802 Array<int> row_idx[3];
2805 for (
unsigned i = 0; i < rows.size(); i++)
2807 const RowInfo &ri = rows[i];
2809 ent_ids[ri.entity].Append(
id);
2810 row_idx[ri.entity].Append(i);
2811 group_ids[ri.entity].Append(ri.group);
2814 Array<GroupId> all_group_ids;
2815 all_group_ids.Reserve(
static_cast<int>(rows.size()));
2816 for (
int i = 0; i < 3; i++)
2818 all_group_ids.Append(group_ids[i]);
2826 for (
int ent = 0; ent < 3; ent++)
2828 const Array<MeshId> &ids = ent_ids[ent];
2829 for (
int i = 0; i < ids.Size(); i++)
2831 const MeshId &
id = ids[i];
2832 const RowInfo &ri = rows[row_idx[ent][i]];
2833 MFEM_ASSERT(ent == ri.entity,
"");
2835#ifdef MFEM_DEBUG_PMATRIX
2837 <<
": ent " << ri.entity <<
", index " << ri.index
2838 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
2839 << int(
id.local) <<
")" << std::endl;
2850 const int *ind =
nullptr;
2862 if (ind && (edof = ind[edof]) < 0)
2871 if (ent == 2 && varOrder)
2879 ri.row.write(stream, s);
2884 stream.str().swap(
data);
2887int NeighborRowMessage::GetEdgeVarOffset(
int edge,
int var)
2890 for (
int v=0; v<var; ++v)
2900int NeighborRowMessage::GetFaceVarOffset(
int face,
int var)
2904 for (
int v=0; v<var; ++v)
2907 const int dofs = fec->
GetNumDof(geom, fo);
2914void NeighborRowMessage::Decode(
int rank)
2916 std::istringstream stream(
data);
2918 Array<MeshId> ent_ids[3];
2919 Array<GroupId> group_ids;
2925 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2926 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2929 rows.reserve(nrows);
2932 for (
int ent = 0, gi = 0; ent < 3; ent++)
2935 const Array<MeshId> &ids = ent_ids[ent];
2936 for (
int i = 0; i < ids.Size(); i++)
2938 const MeshId &
id = ids[i];
2942 MFEM_ASSERT(order_i >= 0,
"");
2949 const int *ind =
nullptr;
2971 if (order == order_i)
2986 RowInfo tmprow(1, 0, 0, 0);
2987 tmprow.row.read(stream, 1.0);
3005 "Only quadrilateral faces are supported in "
3006 "variable-order spaces");
3019 if (order == order_i)
3034 RowInfo tmprow(1, 0, 0, 0);
3035 tmprow.row.read(stream, 1.0);
3052 const bool process_dof_pairs = (ent == 2 &&
3056#ifdef MFEM_DEBUG_PMATRIX
3058 <<
": ent " << ent <<
", index " <<
id.index
3059 <<
", edof " << edof <<
" (id " <<
id.element <<
"/"
3060 << int(
id.local) <<
")" << std::endl;
3064 real_t s = (edof < 0) ? -1.0 : 1.0;
3065 edof = (edof < 0) ? -1 - edof : edof;
3066 if (ind && (edof = ind[edof]) < 0)
3076 rows.emplace_back(ent,
id.
index, edof, group_ids[gi++], var);
3077 rows.back().row.read(stream, s);
3079#ifdef MFEM_DEBUG_PMATRIX
3081 <<
": ent " << rows.back().entity <<
", index "
3082 << rows.back().index <<
", edof " << rows.back().edof
3086 if (process_dof_pairs)
3106 auto &first_row = rows.back().row;
3108 const auto initial_first_row = first_row;
3111 const MeshId &next_id = ids[++i];
3118 s = (edof < 0) ? -1.0 : 1.0;
3119 edof = (edof < 0) ? -1 - edof : edof;
3120 if (ind && (edof = ind[edof]) < 0)
3126 rows.emplace_back(ent, next_id.index, edof, group_ids[gi++]);
3127 rows.back().row.read(stream, s);
3128 auto &second_row = rows.back().row;
3131 const auto initial_second_row = second_row;
3149 MFEM_ASSERT(fo != 2 &&
3150 fo != 4,
"This code branch is ambiguous for face orientations 2 and 4."
3151 " Please report this mesh for further testing.\n");
3153 first_row.AddRow(initial_first_row, T[0] - 1.0);
3154 first_row.AddRow(initial_second_row, T[2]);
3155 second_row.AddRow(initial_first_row, T[1]);
3156 second_row.AddRow(initial_second_row, T[3] - 1.0);
3158 first_row.Collapse();
3159 second_row.Collapse();
3166ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
3168 NeighborRowMessage::Map &send_msg)
const
3170 int ent, idx, edof, order;
3171 UnpackDof(dof, ent, idx, edof, order);
3173 for (
const auto &rank : pncmesh->GetGroup(group_id))
3177 NeighborRowMessage &msg = send_msg[rank];
3179 msg.AddRow(ent, idx, edof, group_id, row, order);
3180 msg.SetNCMesh(pncmesh);
3182#ifdef MFEM_PMATRIX_STATS
3189void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
3190 GroupId group_sent_id, GroupId group_id,
3191 NeighborRowMessage::Map &send_msg)
const
3193 int ent, idx, edof, order;
3194 UnpackDof(dof, ent, idx, edof, order);
3197 for (
unsigned i = 0; i < group.size(); i++)
3199 int rank = group[i];
3200 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
3202 NeighborRowMessage &msg = send_msg[rank];
3203 GroupId invalid = -1;
3205 msg.AddRow(ent, idx, edof, invalid, row, order);
3206 msg.SetNCMesh(pncmesh);
3208#ifdef MFEM_PMATRIX_STATS
3211#ifdef MFEM_DEBUG_PMATRIX
3213 << rank <<
": ent " << ent <<
", index" << idx
3214 <<
", edof " << edof << std::endl;
3220#ifdef MFEM_DEBUG_PMATRIX
3221void ParFiniteElementSpace
3222::DebugDumpDOFs(std::ostream &os,
3223 const SparseMatrix &deps,
3224 const Array<GroupId> &dof_group,
3225 const Array<GroupId> &dof_owner,
3226 const Array<bool> &finalized)
const
3228 for (
int i = 0; i < dof_group.Size(); i++)
3231 if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
3234 UnpackDof(i, ent, idx, edof);
3236 os << edof <<
" @ ";
3237 if (i > ndofs) { os <<
"ghost "; }
3240 case 0: os <<
"vertex ";
break;
3241 case 1: os <<
"edge ";
break;
3242 default: os <<
"face ";
break;
3246 if (i < deps.Height() && deps.RowSize(i))
3248 os <<
"depends on ";
3249 for (
int j = 0; j < deps.RowSize(i); j++)
3251 os << deps.GetRowColumns(i)[j] <<
" ("
3252 << deps.GetRowEntries(i)[j] <<
")";
3253 if (j < deps.RowSize(i)-1) { os <<
", "; }
3262 os <<
"group " << dof_group[i] <<
" (";
3264 for (
unsigned j = 0; j < g.size(); j++)
3266 if (j) { os <<
", "; }
3270 os <<
"), owner " << dof_owner[i] <<
" (rank "
3271 << pncmesh->GetGroup(dof_owner[i])[0] <<
"); "
3272 << (finalized[i] ?
"finalized" :
"NOT finalized");
3283void ParFiniteElementSpace::ScheduleSendOrder(
3284 int ent,
int idx,
int order, GroupId group_id,
3285 NeighborOrderMessage::Map &send_msg)
const
3287 for (
const auto &rank : pncmesh->GetGroup(group_id))
3291 NeighborOrderMessage &msg = send_msg[rank];
3292 msg.AddOrder(ent, idx, order, group_id);
3293 msg.SetNCMesh(pncmesh);
3299 const std::set<int> &edges,
const std::set<int> &faces,
3303 bool changed = edges.size() > 0 || faces.size() > 0;
3307 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3313 NeighborOrderMessage::Map send_msg;
3316 for (
int entity = 1; entity <= 2; ++entity)
3318 const std::set<int> &indices = entity == 1 ? edges : faces;
3320 for (
auto idx : indices)
3326 ScheduleSendOrder(entity, idx,
MinOrder(orders[idx]),
3335 MPI_Barrier(MyComm);
3337 NeighborOrderMessage recv_msg;
3338 recv_msg.SetNCMesh(pncmesh);
3345 recv_msg.Recv(rank, size, MyComm);
3347 for (
const auto &ri : recv_msg.GetMsgs())
3353 edge_orders[ri.index] |= mask;
3354 if (edge_orders[ri.index] != initOrders)
3359 else if (ri.entity == 2)
3362 face_orders[ri.index] |= mask;
3363 if (face_orders[ri.index] != initOrders)
3370 MFEM_ABORT(
"Invalid entity type");
3379 recv_msg.RecvDrop(rank, size, MyComm);
3386 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3395 MFEM_VERIFY(intermediate.
Size() ==
ndofs,
"");
3400 for (
int e=0; e<n; ++e)
3403 for (
int var = 1; var < nvar - 1; ++var)
3408 for (
auto dof : dofs)
3412 intermediate[dof] =
true;
3419void ParFiniteElementSpace::SetVarDofMap(
const Table & dofs,
3422 if (dofs.
Size() < 1)
3428 MFEM_ASSERT(dofs.
RowSize(dofs.
Size() - 1) == 1,
"");
3429 const int* rowLast = dofs.
GetRow(dofs.
Size() - 1);
3430 const int ndofs = rowLast[0];
3434 for (
int r = 0; r < dofs.
Size() - 1; ++r)
3436 const int* row = dofs.
GetRow(r);
3437 const int* row1 = dofs.
GetRow(r+1);
3439 for (
int d=row[0]; d<row1[0]; ++d)
3442 dmap[d].edof = d - row[0];
3447void ParFiniteElementSpace::SetTDOF2LDOFinfo(
int ntdofs,
int vdim_factor,
3448 int dof_stride,
int allnedofs)
3453 for (
int i=0; i<ntdofs; ++i)
3455 tdof2ldof[i].set =
false;
3461 for (
int entity = 1; entity < pmesh->
Dimension(); entity++)
3464 const int num_ent = (entity == 1) ? pmesh->
GetNEdges() :
3466 MFEM_ASSERT(ent_dofs.Size() >= num_ent+1,
"");
3468 for (
int idx = 0; idx < num_ent; idx++)
3470 if (ent_dofs.RowSize(idx) == 0) {
continue; }
3480 const int order0 =
GetEntityDofs(entity, idx, dofs, geom, 0);
3487 numVert = verts.Size();
3488 MFEM_VERIFY(numVert == 4,
"Only quadrilateral faces are supported");
3496 constexpr int vd = 0;
3497 for (
int i=idof0; i<dofs.Size(); ++i)
3499 const int dof_i = dofs[i];
3500 const int vdof_i = dof_i*vdim_factor + vd*dof_stride;
3501 const int tdof = ldof_ltdof[vdof_i];
3502 if (tdof < 0) {
continue; }
3504 MFEM_ASSERT(!tdof2ldof[tdof].set,
"");
3506 tdof2ldof[tdof].set =
true;
3507 tdof2ldof[tdof].minOrder = minOrder;
3508 tdof2ldof[tdof].isEdge = (entity == 1);
3509 tdof2ldof[tdof].idx = idx;
3515void ParFiniteElementSpace
3516::SetRestrictionMatrixEdgesFaces(
int vdim_factor,
int dof_stride,
3517 int tdof_stride,
const Array<int> &dof_tdof,
3518 const Array<HYPRE_BigInt> &dof_offs)
3520 MFEM_VERIFY(IsVariableOrder(),
"");
3522 const int ntdofs = tdof2ldof.Size();
3523 MFEM_VERIFY(vdim * ntdofs == R->NumRows(),
"");
3525 int prevEntity = -1;
3529 Array<int> ldofs, tdofs;
3532 for (
int tdof=0; tdof<ntdofs; ++tdof)
3534 if (!tdof2ldof[tdof].set) {
continue; }
3536 const int minOrder = tdof2ldof[tdof].minOrder;
3537 const bool edge = tdof2ldof[tdof].isEdge;
3538 const int index = tdof2ldof[tdof].idx;
3539 const int entity = edge ? 1 : 2;
3540 MFEM_ASSERT(!pncmesh->IsGhost(entity,
index),
3541 "True DOFs are not defined on ghost entities");
3543 if (entity != prevEntity ||
index != prevIndex)
3552 prevEntity = entity;
3560 const FiniteElement *feT = fec->FiniteElementForGeometry(geom);
3561 const FiniteElement *feL = fec->FiniteElementForGeometry(geom);
3566 tdofOrder = GetEdgeOrder(
index, 0);
3567 GetEdgeDofs(
index, tdofs, 0);
3568 for (
int var=0; ; ++var)
3570 const int order_var = GetEdgeOrder(
index, var);
3571 if (order_var == minOrder)
3573 GetEdgeDofs(
index, ldofs, var);
3580 tdofOrder = GetFaceOrder(
index, 0);
3581 GetFaceDofs(
index, tdofs, 0);
3582 for (
int var=0; ; ++var)
3584 const int order_var = GetFaceOrder(
index, var);
3585 if (order_var == minOrder)
3587 GetFaceDofs(
index, ldofs, var);
3593 MFEM_VERIFY(tdofs.Size() > 0 && ldofs.Size() > 0,
"");
3596 idof0 = GetNumBorderDofs(geom, tdofOrder);
3598 feT = fec->GetFE(geom, tdofOrder);
3599 feL = fec->GetFE(geom, minOrder);
3601 MFEM_VERIFY(feT && feL,
"");
3603 IsoparametricTransformation T;
3609 default: MFEM_ABORT(
"unsupported geometry");
3613 T.SetIdentityTransformation(geom);
3614 feT->GetTransferMatrix(*feL, T, I);
3617 for (
int ldi=0; ldi<ldofs.Size(); ++ldi)
3619 const real_t value = I(tdi + idof0, ldi);
3620 if (std::abs(value) > 1e-12)
3622 const int ldof = all2local[ldofs[ldi]];
3623 for (
int vd = 0; vd < vdim; vd++)
3625 const int vdof = ldof*vdim_factor + vd*dof_stride;
3626 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
3627 R->Add(vtdof, vdof, value);
3634int ParFiniteElementSpace
3635::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
3636 Array<HYPRE_BigInt> &dof_offs,
3637 Array<HYPRE_BigInt> &tdof_offs,
3638 Array<int> *dof_tdof,
3641 const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
3642 const bool H1var = IsVariableOrderH1();
3644#ifdef MFEM_PMATRIX_STATS
3645 n_msgs_sent = n_msgs_recv = 0;
3646 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
3651 const int total_dofs = ndofs + ngdofs;
3652 SparseMatrix deps(ndofs, total_dofs);
3654 if (!dg && !partial)
3656 VariableOrderMinimumRule(deps);
3658 Array<int> master_dofs, slave_dofs;
3661 for (
int entity = 0; entity <= 2; entity++)
3663 const NCMesh::NCList &list = pncmesh->GetNCList(entity);
3664 if (list.masters.Size() == 0) {
continue; }
3666 IsoparametricTransformation T;
3670 for (
const auto &mf : list.masters)
3673 if (entity == 1 && skip_edge.Size() > 0)
3675 if (skip_edge[mf.index])
3680 else if (entity == 2 && skip_face.Size() > 0)
3682 if (skip_face[mf.index])
3688 if (pncmesh->IsGhost(entity, mf.index))
3690 GetGhostDofs(entity, mf, master_dofs, 0);
3694 GetEntityDofs(entity, mf.index, master_dofs, mf.Geom(), 0);
3697 if (master_dofs.Size() == 0) {
continue; }
3699 const FiniteElement *fe = fec->FiniteElementForGeometry(mf.Geom());
3701 if (IsVariableOrder())
3704 if (entity == 1) { mfOrder = GetEdgeOrder(mf.index, 0); }
3705 else if (entity == 2) { mfOrder = GetFaceOrder(mf.index, 0); }
3707 if (entity != 0) { fe = fec->GetFE(mf.Geom(), mfOrder); }
3710 if (fe ==
nullptr) {
continue; }
3717 default: MFEM_ABORT(
"unsupported geometry");
3721 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
3723 const NCMesh::Slave &sf = list.slaves[si];
3724 if (pncmesh->IsGhost(entity, sf.index)) {
continue; }
3726 constexpr int variant = 0;
3727 const int q = GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
3728 if (q < 0) {
break; }
3730 list.OrientedPointMatrix(sf, T.GetPointMat());
3732 const auto *slave_fe = fec->GetFE(mf.Geom(), q);
3733 slave_fe->GetTransferMatrix(*fe, T, I);
3736 AddDependencies(deps, master_dofs, slave_dofs, I);
3746 Array<GroupId> dof_group(total_dofs);
3747 Array<GroupId> dof_owner(total_dofs);
3755 auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
3756 this](
int entity,
const MeshId &id)
3758 if (
id.
index < 0) {
return; }
3760 GroupId owner = pncmesh->GetEntityOwnerId(entity,
id.
index);
3761 GroupId group = pncmesh->GetEntityGroupId(entity,
id.
index);
3763 GetBareDofs(entity,
id.
index, dofs);
3765 for (
auto dof : dofs)
3767 dof_owner[dof] = owner;
3768 dof_group[dof] = group;
3773 for (
int entity : {0,1,2})
3775 for (
const auto &
id : pncmesh->GetNCList(entity).conforming)
3777 initialize_group_and_owner(entity,
id);
3779 for (
const auto &
id : pncmesh->GetNCList(entity).masters)
3781 initialize_group_and_owner(entity,
id);
3783 for (
const auto &
id : pncmesh->GetNCList(entity).slaves)
3785 initialize_group_and_owner(entity,
id);
3792 Array<bool> finalized(total_dofs);
3796 int num_true_dofs = 0;
3797 for (
int i = 0; i < ndofs; ++i)
3799 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
3802 finalized[i] =
true;
3806#ifdef MFEM_DEBUG_PMATRIX
3808 auto dof_diagnostics = [&](
int dof,
bool print_diagnostic)
3810 const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
3811 std::stringstream msg;
3812 msg << std::boolalpha;
3814 <<
" owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] <<
" CommGroup {";
3815 for (
const auto &x : comm_group)
3819 msg <<
"} finalized " << finalized[dof];
3825 deps.GetRow(dof, cols, row);
3826 msg <<
" deps cols {";
3827 for (
const auto &x : cols)
3834 int entity,
index, edof;
3835 UnpackDof(dof, entity,
index, edof);
3836 msg <<
" entity " << entity <<
" index " <<
index <<
" edof " << edof;
3843 HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
3844 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
3845 pmesh->GenerateOffsets(2, loc_sizes, offsets);
3849 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
3854 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
3858 dof_tdof->SetSize(ndofs*vdim);
3862 std::vector<PMatrixRow> pmatrix(total_dofs);
3865 const int vdim_factor = bynodes ? 1 : vdim;
3866 const int dof_stride = bynodes ? ndofs : 1;
3867 const int tdof_stride = bynodes ? num_true_dofs : 1;
3870 std::list<NeighborRowMessage::Map> send_msg;
3871 send_msg.emplace_back();
3874 for (
int dof = 0, tdof = 0; dof < ndofs; dof++)
3878 pmatrix[dof].elems.emplace_back(
3879 my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.);
3882 if (dof_group[dof] != 0)
3884 MFEM_VERIFY(!send_msg.empty(),
"");
3885 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
3888 for (
int vd = 0; vd < vdim; vd++)
3890 const int vdof = dof*vdim_factor + vd*dof_stride;
3891 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
3893 if (R_ && !H1var) { (*R_)->Add(vtdof, vdof, 1.0); }
3894 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
3901 MFEM_VERIFY(!send_msg.empty(),
"");
3903#ifdef MFEM_PMATRIX_STATS
3904 n_msgs_sent += send_msg.back().size();
3907 if (R_ && !H1var) { (*R_)->Finalize(); }
3912 NeighborRowMessage recv_msg;
3913 recv_msg.SetNCMesh(pncmesh);
3914 recv_msg.SetSpace(
this);
3915 recv_msg.SetFEC(fec);
3917 int num_finalized = num_true_dofs;
3919 buffer.elems.reserve(1024);
3925 Array<bool> intermediate(ndofs);
3926 intermediate =
false;
3928 MarkIntermediateEntityDofs(1, intermediate);
3929 MarkIntermediateEntityDofs(2, intermediate);
3931 while (num_finalized < ndofs)
3934 MFEM_VERIFY(!send_msg.empty(),
"");
3935 if (send_msg.back().size())
3937 send_msg.emplace_back();
3945 recv_msg.Recv(rank, size, MyComm);
3947#ifdef MFEM_PMATRIX_STATS
3949 n_rows_recv += recv_msg.GetRows().size();
3952 for (
const auto &ri : recv_msg.GetRows())
3954 const int dof = PackDof(ri.entity, ri.index, ri.edof, ri.var);
3955 pmatrix[dof] = ri.row;
3957 if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
3958 finalized[dof] =
true;
3960 if (ri.group >= 0 && dof_group[dof] != ri.group)
3963 MFEM_VERIFY(!send_msg.empty(),
"");
3964 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
3974 for (
int dof = 0; dof < ndofs; dof++)
3976 const bool owned = (dof_owner[dof] == 0);
3978 && (owned || intermediate[dof])
3979 && DofFinalizable(dof, finalized, deps))
3981 const int* dep_col = deps.GetRowColumns(dof);
3982 const real_t* dep_coef = deps.GetRowEntries(dof);
3985 buffer.elems.clear();
3986 for (
int j = 0; j < deps.RowSize(dof); j++)
3988 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
3991 pmatrix[dof] = buffer;
3993 finalized[dof] =
true;
3998 const bool shared = (dof_group[dof] != 0);
4001 MFEM_VERIFY(!send_msg.empty(),
"");
4002 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
4009#ifdef MFEM_DEBUG_PMATRIX
4010 static int dump = 0;
4014 snprintf(fname, 100,
"dofs%02d.txt", MyRank);
4015 std::ofstream
f(fname);
4016 DebugDumpDOFs(
f, deps, dof_group, dof_owner, finalized);
4022 MFEM_VERIFY(!send_msg.empty(),
"");
4024#ifdef MFEM_PMATRIX_STATS
4025 n_msgs_sent += send_msg.back().size();
4031 const int allnedofs = nedofs;
4034 SetVarOrderLocalDofs();
4036 const int ldof_stride = bynodes ? ndofs : 1;
4041 Array<HYPRE_BigInt>* offsets[1] = { &dof_offs };
4042 pmesh->GenerateOffsets(1, loc_sizes, offsets);
4047 std::vector<PMatrixRow> pmatrix_new(ndofs);
4050 bool validMap =
true;
4051 for (
int i=0; i<all2local.Size(); ++i)
4053 if (all2local[i] >= 0)
4055 if (all2local[i] - dofnew != 1)
4060 dofnew = all2local[i];
4062 pmatrix_new[all2local[i]] = pmatrix[i];
4065 MFEM_VERIFY(validMap && dofnew == ndofs - 1,
"");
4070 *P_ = MakeVDimHypreMatrix(pmatrix_new, ndofs, num_true_dofs,
4071 dof_offs, tdof_offs);
4076 MFEM_VERIFY(R_ && nedofs == lnedofs,
"");
4078 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
4082 for (
int dof = 0; dof < nvdofs; dof++)
4084 for (
int vd = 0; vd < vdim; vd++)
4086 const int valldof = dof*vdim_factor + vd*dof_stride;
4087 const int vdof = dof*vdim_factor + vd*ldof_stride;
4088 const int vtdof = (*dof_tdof)[valldof];
4089 if (vtdof >= 0) { (*R_)->Add(vtdof, vdof, 1.0); }
4095 SetTDOF2LDOFinfo(num_true_dofs, vdim_factor, dof_stride, allnedofs);
4097 Array<HYPRE_BigInt> all_dof_offs(NRanks);
4098 MPI_Allgather(&dof_offs[0], 1, HYPRE_MPI_BIG_INT, all_dof_offs.GetData(),
4099 1, HYPRE_MPI_BIG_INT, MyComm);
4101 SetRestrictionMatrixEdgesFaces(vdim_factor, ldof_stride, tdof_stride,
4102 *dof_tdof, all_dof_offs);
4108 const int nalldofs = dof_tdof->Size() / vdim;
4109 MFEM_VERIFY(nalldofs * vdim == dof_tdof->Size(),
"");
4110 for (
int edof=0; edof<nbdofs; ++edof)
4112 const int dof = ndofs - nbdofs + edof;
4113 const int alldof = nalldofs - nbdofs + edof;
4114 for (
int vd = 0; vd < vdim; vd++)
4116 const int valldof = alldof*vdim_factor + vd*dof_stride;
4117 const int vdof = dof*vdim_factor + vd*ldof_stride;
4118 const int vtdof = (*dof_tdof)[valldof];
4119 (*R_)->Add(vtdof, vdof, 1.0);
4124 for (
int tdof=0; tdof<num_true_dofs; ++tdof)
4126 if ((*R_)->RowSize(tdof) == 0)
4128 MFEM_ABORT(
"Empty row of R");
4135 Array<int> dof_tdof_new(ndofs * vdim);
4136 for (
int i=0; i<all2local.Size(); ++i)
4138 if (all2local[i] >= 0)
4140 for (
int vd = 0; vd < vdim; vd++)
4142 const int vdof = i*vdim_factor + vd*dof_stride;
4143 const int ldof = all2local[i]*vdim_factor + vd*ldof_stride;
4144 dof_tdof_new[ldof] = (*dof_tdof)[vdof];
4149 Swap(dof_tdof_new, *dof_tdof);
4154 MFEM_VERIFY(var_edge_dofs.Size() - 1 == pncmesh->GetNEdges() +
4155 pncmesh->GetNGhostEdges(),
"");
4156 MFEM_VERIFY(var_face_dofs.Size() == -1 ||
4157 var_face_dofs.Size() - 1 == pncmesh->GetNFaces() + pncmesh->GetNGhostFaces(),
4160 ghost_edge_orders.SetSize(pncmesh->GetNGhostEdges());
4161 ghost_face_orders.SetSize(pncmesh->GetNGhostFaces());
4163 for (
int i=0; i<pncmesh->GetNGhostEdges(); ++i)
4165 ghost_edge_orders[i] = GetEdgeOrder(pncmesh->GetNEdges() + i);
4168 if (pmesh->Dimension() > 2)
4170 for (
int i=0; i<pncmesh->GetNGhostFaces(); ++i)
4172 ghost_face_orders[i] = GetFaceOrder(pncmesh->GetNFaces() + i);
4177 var_edge_dofs.Swap(loc_var_edge_dofs);
4178 loc_var_edge_dofs.Clear();
4180 var_face_dofs.Swap(loc_var_face_dofs);
4181 loc_var_face_dofs.Clear();
4183 Swap(var_edge_orders, loc_var_edge_orders);
4184 Swap(var_face_orders, loc_var_face_orders);
4186 loc_var_edge_orders.SetSize(0);
4187 loc_var_face_orders.SetSize(0);
4191 *P_ = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
4192 dof_offs, tdof_offs);
4200 recv_msg.RecvDrop(rank, size, MyComm);
4204 for (
auto &msg : send_msg)
4209#ifdef MFEM_PMATRIX_STATS
4210 int n_rounds = send_msg.size();
4211 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
4212 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
4214 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
4215 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4216 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4217 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4218 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4219 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
4223 mfem::out <<
"P matrix stats (avg per rank): "
4224 <<
real_t(glob_rounds)/NRanks <<
" rounds, "
4225 <<
real_t(glob_msgs_sent)/NRanks <<
" msgs sent, "
4226 <<
real_t(glob_msgs_recv)/NRanks <<
" msgs recv, "
4227 <<
real_t(glob_rows_sent)/NRanks <<
" rows sent, "
4228 <<
real_t(glob_rows_recv)/NRanks <<
" rows recv, "
4229 <<
real_t(glob_rows_fwd)/NRanks <<
" rows forwarded."
4234 return num_true_dofs*vdim;
4237HypreParMatrix* ParFiniteElementSpace
4238::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
4239 int local_rows,
int local_cols,
4240 Array<HYPRE_BigInt> &row_starts,
4241 Array<HYPRE_BigInt> &col_starts)
const
4243 bool assumed = HYPRE_AssumedPartitionCheck();
4246 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
4247 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
4250 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
4251 std::map<HYPRE_BigInt, int> col_map;
4252 for (
int i = 0; i < local_rows; i++)
4254 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4256 const PMatrixElement &elem = rows[i].elems[j];
4258 if (col >= first_col && col < next_col)
4265 for (
int vd = 0; vd < vdim; vd++)
4275 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(
static_cast<int>(col_map.size()));
4277 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
4279 cmap[offd_col] = it->first;
4280 it->second = offd_col++;
4283 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
4284 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
4286 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
4287 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
4289 real_t *A_diag = Memory<real_t>(nnz_diag);
4290 real_t *A_offd = Memory<real_t>(nnz_offd);
4292 int vdim1 = bynodes ? vdim : 1;
4293 int vdim2 = bynodes ? 1 : vdim;
4294 int vdim_offset = bynodes ? local_cols : 1;
4297 nnz_diag = nnz_offd = 0;
4299 for (
int vd1 = 0; vd1 < vdim1; vd1++)
4301 for (
int i = 0; i < local_rows; i++)
4303 for (
int vd2 = 0; vd2 < vdim2; vd2++)
4305 I_diag[vrow] = nnz_diag;
4306 I_offd[vrow++] = nnz_offd;
4308 int vd = bynodes ? vd1 : vd2;
4309 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4311 const PMatrixElement &elem = rows[i].elems[j];
4312 if (elem.column >= first_col && elem.column < next_col)
4314 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
4315 A_diag[nnz_diag++] = elem.value;
4319 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
4320 A_offd[nnz_offd++] = elem.value;
4326 MFEM_ASSERT(vrow == vdim*local_rows,
"");
4327 I_diag[vrow] = nnz_diag;
4328 I_offd[vrow] = nnz_offd;
4330 return new HypreParMatrix(MyComm,
4331 row_starts.Last(), col_starts.Last(),
4332 row_starts.GetData(), col_starts.GetData(),
4333 I_diag, J_diag, A_diag,
4334 I_offd, J_offd, A_offd,
4335 static_cast<HYPRE_Int
>(col_map.size()), cmap);
4338template <
typename int_type>
4339static int_type* make_i_array(
int nrows)
4341 int_type *I = Memory<int_type>(nrows+1);
4342 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
4346template <
typename int_type>
4347static int_type* make_j_array(int_type* I,
int nrows)
4350 for (
int i = 0; i < nrows; i++)
4352 if (I[i] >= 0) { nnz++; }
4354 int_type *J = Memory<int_type>(nnz);
4357 for (
int i = 0, k = 0; i <= nrows; i++)
4359 int_type col = I[i];
4361 if (col >= 0) { J[k++] = col; }
4367ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
4368 const Table* old_elem_dof,
4369 const Table* old_elem_fos)
4371 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
4372 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
4373 "be called before ParFiniteElementSpace::RebalanceMatrix");
4375 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4376 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4379 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4385 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
4386 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
4387 "Mesh::Rebalance was not called before "
4388 "ParFiniteElementSpace::RebalanceMatrix");
4391 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
4392 for (
int i = 0; i < pmesh->
GetNE(); i++)
4394 if (old_index[i] >= 0)
4396 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
4399 for (
int vd = 0; vd <
vdim; vd++)
4401 for (
int j = 0; j < dofs.Size(); j++)
4404 if (row < 0) { row = -1 - row; }
4406 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
4407 if (col < 0) { col = -1 - col; }
4414 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
4417 Array<int> new_elements;
4418 Array<long> old_remote_dofs;
4419 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
4422 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
4423 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
4426 const long* old_dofs = &old_remote_dofs[pos];
4427 pos += dofs.Size() *
vdim;
4429 for (
int vd = 0; vd <
vdim; vd++)
4431 for (
int j = 0; j < dofs.Size(); j++)
4434 if (row < 0) { row = -1 - row; }
4436 if (i_diag[row] == i_diag[row+1])
4438 i_offd[row] = old_dofs[j + vd * dofs.Size()];
4445#ifndef HYPRE_MIXEDINT
4446 HYPRE_Int *i_offd_hi = i_offd;
4449 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
4450 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
4451 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
4455 int offd_cols = i_offd_hi[vsize];
4456 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
4457 for (
int i = 0; i < offd_cols; i++)
4459 cmap_offd[i].one = j_offd[i];
4460 cmap_offd[i].two = i;
4463#ifndef HYPRE_MIXEDINT
4464 HYPRE_Int *j_offd_hi = j_offd;
4466 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
4467 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
4473 for (
int i = 0; i < offd_cols; i++)
4475 cmap[i] = cmap_offd[i].one;
4476 j_offd_hi[cmap_offd[i].two] = i;
4480 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
4481 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
4486struct DerefDofMessage
4488 std::vector<HYPRE_BigInt> dofs;
4489 MPI_Request request;
4493ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
4494 const Table* old_elem_dof,
4495 const Table *old_elem_fos)
4497 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
4499 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
4500 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
4503 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
4504 "Previous space is not finer.");
4512 Mesh::GeometryList elem_geoms(*
mesh);
4514 Array<int> dofs, old_dofs, old_vdofs;
4517 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4524 for (
int i = 0; i < elem_geoms.Size(); i++)
4530 const CoarseFineTransformations &dtrans =
4531 old_pncmesh->GetDerefinementTransforms();
4532 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
4534 std::map<int, DerefDofMessage> messages;
4536 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4537 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4541 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4543 const Embedding &emb = dtrans.embeddings[k];
4545 int fine_rank = old_ranks[k];
4546 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
4547 : old_pncmesh->ElementRank(emb.parent);
4549 if (coarse_rank != MyRank && fine_rank == MyRank)
4551 old_elem_dof->GetRow(k, dofs);
4554 DerefDofMessage &msg = messages[k];
4555 msg.dofs.resize(dofs.Size());
4556 for (
int i = 0; i < dofs.Size(); i++)
4558 msg.dofs[i] = old_offset + dofs[i];
4561 MPI_Isend(&msg.dofs[0],
static_cast<int>(msg.dofs.size()), HYPRE_MPI_BIG_INT,
4562 coarse_rank, 291, MyComm, &msg.request);
4564 else if (coarse_rank == MyRank && fine_rank != MyRank)
4566 MFEM_ASSERT(emb.parent >= 0,
"");
4569 DerefDofMessage &msg = messages[k];
4570 msg.dofs.resize(ldof[geom]*
vdim);
4572 MPI_Irecv(&msg.dofs[0], ldof[geom]*
vdim, HYPRE_MPI_BIG_INT,
4573 fine_rank, 291, MyComm, &msg.request);
4581 for (
int i = 0; i < elem_geoms.Size(); i++)
4587 SparseMatrix *diag =
new SparseMatrix(
ndofs*
vdim, old_ndofs*
vdim);
4589 Array<char> mark(diag->Height());
4594 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4596 const Embedding &emb = dtrans.embeddings[k];
4597 if (emb.parent < 0) {
continue; }
4599 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
4600 int fine_rank = old_ranks[k];
4602 if (coarse_rank == MyRank && fine_rank == MyRank)
4605 DenseMatrix &lR = localR[geom](emb.matrix);
4608 old_elem_dof->GetRow(k, old_dofs);
4610 for (
int vd = 0; vd <
vdim; vd++)
4612 old_dofs.Copy(old_vdofs);
4615 for (
int i = 0; i < lR.Height(); i++)
4617 if (!std::isfinite(lR(i, 0))) {
continue; }
4620 int m = (r >= 0) ? r : (-1 - r);
4622 if (is_dg || !mark[m])
4625 diag->SetRow(r, old_vdofs, row);
4635 for (
auto it = messages.begin(); it != messages.end(); ++it)
4637 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
4641 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
4643 std::map<HYPRE_BigInt, int> col_map;
4644 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4646 const Embedding &emb = dtrans.embeddings[k];
4647 if (emb.parent < 0) {
continue; }
4649 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
4650 int fine_rank = old_ranks[k];
4652 if (coarse_rank == MyRank && fine_rank != MyRank)
4655 DenseMatrix &lR = localR[geom](emb.matrix);
4659 DerefDofMessage &msg = messages[k];
4660 MFEM_ASSERT(msg.dofs.size(),
"");
4662 for (
int vd = 0; vd <
vdim; vd++)
4664 MFEM_ASSERT(ldof[geom],
"");
4667 for (
int i = 0; i < lR.Height(); i++)
4669 if (!std::isfinite(lR(i, 0))) {
continue; }
4672 int m = (r >= 0) ? r : (-1 - r);
4674 if (is_dg || !mark[m])
4677 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
4678 for (
int j = 0; j < ldof[geom]; j++)
4680 if (row[j] == 0.0) {
continue; }
4681 int &lcol = col_map[remote_dofs[j]];
4682 if (!lcol) { lcol =
static_cast<int>(col_map.size()); }
4683 offd->_Set_(m, lcol-1, row[j]);
4694 offd->SetWidth(
static_cast<int>(col_map.size()));
4697 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
4698 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
4700 cmap[it->second-1] = it->first;
4707 int width = offd->Width();
4708 Array<Pair<HYPRE_BigInt, int> > reorder(width);
4709 for (
int i = 0; i < width; i++)
4711 reorder[i].one = cmap[i];
4716 Array<int> reindex(width);
4717 for (
int i = 0; i < width; i++)
4719 reindex[reorder[i].two] = i;
4720 cmap[i] = reorder[i].one;
4723 int *J = offd->GetJ();
4724 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
4726 J[i] = reindex[J[i]];
4728 offd->SortColumnIndices();
4731 HypreParMatrix* new_R;
4732 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
4733 dof_offsets, old_dof_offsets, diag, offd, cmap,
4736 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
4741void ParFiniteElementSpace::Destroy()
4752 delete Pconf; Pconf = NULL;
4753 delete Rconf; Rconf = NULL;
4756 delete gcomm; gcomm = NULL;
4765void ParFiniteElementSpace::CopyProlongationAndRestriction(
4766 const FiniteElementSpace &fes,
const Array<int> *perm)
4770 MFEM_VERIFY(pfes != NULL,
"");
4771 MFEM_VERIFY(P == NULL,
"");
4772 MFEM_VERIFY(R == NULL,
"");
4775 pfes->Dof_TrueDof_Matrix();
4777 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
4783 int n = perm->Size();
4784 perm_mat =
new SparseMatrix(n, fes.GetVSize());
4785 for (
int i=0; i<n; ++i)
4789 perm_mat->Set(i, j, s);
4791 perm_mat->Finalize();
4795 if (pfes->P != NULL)
4798 else { P =
new HypreParMatrix(*pfes->P); }
4801 else if (perm != NULL)
4807 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
4808 col_starts, perm_mat);
4811 if (pfes->R != NULL)
4813 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
4814 else { R =
new SparseMatrix(*pfes->R); }
4816 else if (perm != NULL)
4837 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
4848 false, Tgf.OwnsOperator(),
false));
4849 Tgf.SetOperatorOwner(
false);
4859 MPI_Allreduce(MPI_IN_PLACE, &int_orders_changed, 1, MPI_INT,
4864 MPI_Allreduce(MPI_IN_PLACE, &var, 1, MPI_INT, MPI_MAX, MyComm);
4881 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
4882 "each mesh modification.");
4891 Table* old_elem_dof = NULL;
4892 Table* old_elem_fos = NULL;
4903 Swap(dof_offsets, old_dof_offsets);
4928 old_elem_fos, old_ndofs));
4931 old_elem_dof = NULL;
4932 old_elem_fos = NULL;
4944 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
4950 false,
false,
true));
4957 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
4965 delete old_elem_dof;
4966 delete old_elem_fos;
4974 "p-refinement is not supported in this space");
4979 for (
int i = 0; i<pmesh->
GetNE(); i++)
4983 pfes_prev->Update(
false);
4986 for (
auto ref : refs)
5001void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
5004 MFEM_VERIFY(new_pmesh != NULL,
5005 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
5015 MPI_Allreduce(MPI_IN_PLACE, &order, 1, MPI_INT, MPI_MAX, MyComm);
5038 const int npref = ghost_orders.Size();
5039 for (
int i=0; i<npref; ++i)
5041 const int elem = ghost_orders[i].element;
5042 const int order = ghost_orders[i].order;
5048 for (
auto edge : edges) { edge_orders[edge] |= mask; }
5055 for (
auto face : faces) { face_orders[face] |= mask; }
5067 const int face = pncmesh->
GetNFaces() + i;
5070 if (orders == 0) {
continue; }
5074 for (
int order = 0; orders != 0; order++, orders >>= 1)
5083 MFEM_VERIFY(orderV0 > 0,
"");
5090 for (
auto edge : edges)
5092 edge_orders[edge] |= mask;
5099 : gc(gc_), local(local_)
5104 for (
int g=1; g<group_ldof.
Size(); ++g)
5108 n_external += group_ldof.
RowSize(g);
5111 int tsize = lsize - n_external;
5117 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5135 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
5137 gc(pfes.GroupComm()),
5143 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5162 for ( ; j < end; j++)
5168 for ( ; j <
Height(); j++)
5186 const int in_layout = 2;
5197 for (
int i = 0; i < m; i++)
5200 std::copy(xdata+j-i, xdata+end-i, ydata+j);
5203 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
5205 const int out_layout = 0;
5228 for (
int i = 0; i < m; i++)
5231 std::copy(xdata+j, xdata+end, ydata+j-i);
5234 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
5236 const int out_layout = 2;
5246 mpi_gpu_aware(
Device::GetGPUAwareMPI())
5249 const int tdofs = R->
Height();
5250 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
5264 unique_ltdof.
Sort();
5267 for (
int i = 0; i < shared_ltdof.
Size(); i++)
5269 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
5270 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
5295 int req_counter = 0;
5300 if (send_size > 0) { req_counter++; }
5304 if (recv_size > 0) { req_counter++; }
5306 requests =
new MPI_Request[req_counter];
5312 pfes.GetRestrictionMatrix(),
5315 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
5319static void ExtractSubVector(
const Array<int> &indices,
5322 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
5323 auto y = vout.
Write();
5324 const auto x = vin.
Read();
5325 const auto I = indices.
Read();
5343static void SetSubVector(
const Array<int> &indices,
5346 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
5349 const auto x = vin.
Read();
5350 const auto I = indices.
Read();
5377 int req_counter = 0;
5416 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
5447static void AddSubVector(
const Array<int> &unique_dst_indices,
5454 const auto x = src.
Read();
5455 const auto DST_I = unique_dst_indices.
Read();
5456 const auto SRC_O = unique_to_src_offsets.
Read();
5457 const auto SRC_I = unique_to_src_indices.
Read();
5460 const int dst_idx = DST_I[i];
5462 const int end = SRC_O[i+1];
5463 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
5479 int req_counter = 0;
5508 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.
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
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-allocated DofTransformation object. doftrans must be al...
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-allocated DofTransformation object....
Operator that extracts Face degrees of freedom in parallel.
Class for parallel meshes.
int GroupNQuadrilaterals(int group) const
Table send_face_nbr_elements
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
int GroupVertex(int group, int i) const
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 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
void LoseData()
Call this if data has been stolen.
void AddConnections(int r, const int *c, int nc)
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
void AddConnection(int r, int c)
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
int Size() const
Returns the number of TYPE I elements.
int Size_of_connections() const
void AddColumnsInRow(int r, int ncol)
Memory< int > & GetJMemory()
void AddAColumnInRow(int r)
void SetDims(int rows, int nnz)
Memory< int > & GetIMemory()
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
General triple product operator x -> A*B*C*x, with ownership of the factors.
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
virtual const real_t * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
virtual real_t * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
int Size() const
Returns the size of the vector.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
void SetSize(int s)
Resize the vector to size s.
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
virtual real_t * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
int index(int i, int j, int nx, int ny)
void write(std::ostream &os, T value)
Write 'value' to stream.
T read(std::istream &is)
Read a value from the stream and return it.
Linear1DFiniteElement SegmentFE
void Swap(Array< T > &, Array< T > &)
void mfem_error(const char *msg)
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
BiLinear2DFiniteElement QuadrilateralFE
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
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)