12 #include "../config/config.hpp" 18 #include "../general/forall.hpp" 19 #include "../general/sort_pairs.hpp" 20 #include "../mesh/mesh_headers.hpp" 21 #include "../general/binaryio.hpp" 34 ParInit(pmesh ? pmesh : orig.pmesh);
50 f ?
f : global_fes->FEColl(),
51 global_fes->GetVDim(), global_fes->GetOrdering())
70 int dim,
int ordering)
80 if (globNURBSext == NULL) {
return NULL; }
81 const ParNURBSExtension *pNURBSext =
82 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
83 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
85 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
87 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
90 void ParFiniteElementSpace::ParInit(ParMesh *pm)
114 MFEM_ASSERT(
own_ext,
"internal error");
116 ParNURBSExtension *pNe =
new ParNURBSExtension(
135 void ParFiniteElementSpace::Construct()
138 " for ParFiniteElementSpace yet.");
142 ConstructTrueNURBSDofs();
143 GenerateGlobalOffsets();
148 GenerateGlobalOffsets();
161 ngedofs = ngfdofs = 0;
181 ngdofs = ngvdofs + ngedofs + ngfdofs;
185 ltdof_size = BuildParallelConformingInterpolation(
186 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
196 long long ltdofs = ltdof_size;
197 long long min_ltdofs, max_ltdofs, sum_ltdofs;
199 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
200 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
201 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
205 double avg = double(sum_ltdofs) / NRanks;
206 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
207 <<
", avg " << std::fixed << std::setprecision(1) << avg
208 <<
", max " << max_ltdofs
209 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
217 mfem::out <<
"True DOFs by rank: " << ltdofs;
218 for (
int i = 1; i < NRanks; i++)
221 MPI_Recv(<dofs, 1, MPI_LONG_LONG, i, 123, MyComm, &status);
228 MPI_Send(<dofs, 1, MPI_LONG_LONG, 0, 123, MyComm);
233 void ParFiniteElementSpace::GetGroupComm(
238 int nvd, ned, ntd = 0, nqd = 0;
241 int group_ldof_counter;
266 group_ldof_counter = 0;
267 for (gr = 1; gr < ng; gr++)
270 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
276 group_ldof_counter *=
vdim;
279 group_ldof.
SetDims(ng, group_ldof_counter);
282 group_ldof_counter = 0;
283 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
284 for (gr = 1; gr < ng; gr++)
286 int j, k, l, m, o, nv, ne, nt, nq;
297 for (j = 0; j < nv; j++)
303 for (l = 0; l < nvd; l++, m++)
313 for (l = 0; l < dofs.
Size(); l++)
315 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
323 for (j = 0; j < ne; j++)
330 for (l = 0; l < ned; l++)
334 dofs[l] = m + (-1-ind[l]);
337 (*g_ldof_sign)[dofs[l]] = -1;
342 dofs[l] = m + ind[l];
351 for (l = 0; l < dofs.
Size(); l++)
353 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
361 for (j = 0; j < nt; j++)
368 for (l = 0; l < ntd; l++)
372 dofs[l] = m + (-1-ind[l]);
375 (*g_ldof_sign)[dofs[l]] = -1;
380 dofs[l] = m + ind[l];
389 for (l = 0; l < dofs.
Size(); l++)
391 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
399 for (j = 0; j < nq; j++)
406 for (l = 0; l < nqd; l++)
410 dofs[l] = m + (-1-ind[l]);
413 (*g_ldof_sign)[dofs[l]] = -1;
418 dofs[l] = m + ind[l];
427 for (l = 0; l < dofs.
Size(); l++)
429 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
434 group_ldof.
GetI()[gr+1] = group_ldof_counter;
440 void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const 444 for (
int i = 0; i < dofs.Size(); i++)
448 if (ldof_sign[-1-dofs[i]] < 0)
450 dofs[i] = -1-dofs[i];
455 if (ldof_sign[dofs[i]] < 0)
457 dofs[i] = -1-dofs[i];
463 void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const 465 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
466 ApplyLDofSigns(all_dofs);
488 ApplyLDofSigns(dofs);
513 ApplyLDofSigns(dofs);
529 ApplyLDofSigns(dofs);
547 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
548 auto itr =
L2F.find(key);
549 if (itr !=
L2F.end())
587 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
588 pmesh->
GroupEdge(group, ei, l_edge, ori);
598 for (
int i = 0; i < dofs.
Size(); i++)
600 const int di = dofs[i];
601 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
610 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
611 "invalid triangular face index");
622 for (
int i = 0; i < dofs.
Size(); i++)
624 const int di = dofs[i];
625 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
634 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
635 "invalid quadrilateral face index");
646 for (
int i = 0; i < dofs.
Size(); i++)
648 const int di = dofs[i];
649 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
654 void ParFiniteElementSpace::GenerateGlobalOffsets()
const 666 if (HYPRE_AssumedPartitionCheck())
669 GroupTopology > = GetGroupTopo();
670 int nsize = gt.GetNumNeighbors()-1;
671 MPI_Request *requests =
new MPI_Request[2*nsize];
672 MPI_Status *statuses =
new MPI_Status[2*nsize];
673 tdof_nb_offsets.
SetSize(nsize+1);
674 tdof_nb_offsets[0] = tdof_offsets[0];
677 int request_counter = 0;
678 for (
int i = 1; i <= nsize; i++)
680 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
681 gt.GetNeighborRank(i), 5365, MyComm,
682 &requests[request_counter++]);
684 for (
int i = 1; i <= nsize; i++)
686 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
687 gt.GetNeighborRank(i), 5365, MyComm,
688 &requests[request_counter++]);
690 MPI_Waitall(request_counter, requests, statuses);
697 void ParFiniteElementSpace::CheckNDSTriaDofs()
700 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
721 for (
int g = 1; g < ngrps; g++)
728 int loc_nd_strias = strias ? 1 : 0;
729 int glb_nd_strias = 0;
730 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1,
731 MPI_INTEGER, MPI_SUM, MyComm);
732 nd_strias = glb_nd_strias > 0;
735 void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
747 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
748 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
751 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
752 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
760 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
762 i_diag[0] = i_offd[0] = 0;
763 diag_counter = offd_counter = 0;
764 for (
int i = 0; i < ldof; i++)
769 j_diag[diag_counter++] = ltdof_i;
774 cmap_j_offd[offd_counter].two = offd_counter;
777 i_diag[i+1] = diag_counter;
778 i_offd[i+1] = offd_counter;
781 SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_counter);
783 for (
int i = 0; i < offd_counter; i++)
785 cmap[i] = cmap_j_offd[i].one;
786 j_offd[cmap_j_offd[i].two] = i;
789 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
790 i_diag, j_diag, i_offd, j_offd,
802 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
803 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
810 Array<int> ldsize(ldof); ldsize = 0;
811 Array<int> ltori(ldof); ltori = 0;
816 for (
int g = 1; g < ngrps; g++)
825 for (
int i=0; i<sdofs.Size(); i++)
827 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
828 if (ldsize[ind] == 0) { nnz_offd++; }
834 int face, ori, info1, info2;
838 for (
int i=0; i<3*
nedofs; i++)
840 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
841 if (ldsize[ind] == 0) { nnz_offd++; }
844 for (
int i=3*
nedofs; i<sdofs.Size(); i++)
846 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
847 ldsize[sdofs[i]] = 2;
848 ltori[sdofs[i]] = info2 % 64;
854 for (
int i=0; i<sdofs.Size(); i++)
856 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
857 if (ldsize[ind] == 0) { nnz_offd++; }
864 HYPRE_Int *i_diag =
new HYPRE_Int[ldof+1];
865 HYPRE_Int *j_diag =
new HYPRE_Int[ltdof];
866 double *d_diag =
new double[ltdof];
869 HYPRE_Int *i_offd =
new HYPRE_Int[ldof+1];
870 HYPRE_Int *j_offd =
new HYPRE_Int[nnz_offd];
871 double *d_offd =
new double[nnz_offd];
879 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
881 i_diag[0] = i_offd[0] = 0;
882 diag_counter = offd_counter = 0;
883 int offd_col_counter = 0;
884 for (
int i = 0; i < ldof; i++)
889 j_diag[diag_counter] = ltdofi;
890 d_diag[diag_counter++] = 1.0;
897 cmap_j_offd[offd_col_counter].two = offd_counter;
904 cmap_j_offd[offd_col_counter].two = offd_counter;
907 i_diag[i+1] = diag_counter;
908 i_offd[i+1] = offd_counter;
911 cmap_j_offd[offd_col_counter].two = offd_counter;
916 i_diag[i+1] = diag_counter;
917 i_offd[i+1] = offd_counter;
920 SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_col_counter);
922 for (
int i = 0; i < nnz_offd; i++)
928 for (
int i = 0; i < offd_col_counter; i++)
930 cmap[i] = cmap_j_offd[i].one;
931 j_offd[cmap_j_offd[i].two] = i;
934 for (
int i = 0; i < ldof; i++)
936 if (i_offd[i+1] == i_offd[i] + 1)
938 d_offd[i_offd[i]] = 1.0;
940 else if (i_offd[i+1] == i_offd[i] + 2)
944 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
945 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
947 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
948 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
949 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
953 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
954 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
955 offd_col_counter, cmap);
967 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
968 P_pc_col_starts, NULL,
true);
977 for (
int i = 0; i < ldof_group.
Size(); i++)
981 if (ldof_ltdof[i] >= 0)
999 gc->
Create(pNURBSext()->ldof_group);
1003 GetGroupComm(*gc, 0);
1013 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1017 gcomm->
Bcast(ldof_marker);
1022 int component)
const 1045 const int *ess_dofs_data = ess_dofs.
HostRead();
1046 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1049 const int *ted = true_ess_dofs.
HostRead();
1050 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1052 if (
bool(ted[i]) != bool(true_ess_dofs2[i])) { counter++; }
1054 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1055 <<
", rank = " << MyRank);
1067 return ldof_ltdof[ldof];
1071 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1073 return ldof_ltdof[ldof];
1086 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1092 if (HYPRE_AssumedPartitionCheck())
1094 return ldof_ltdof[ldof] +
1095 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1099 return ldof_ltdof[ldof] +
1109 MFEM_ABORT(
"Not implemented for NC mesh.");
1112 if (HYPRE_AssumedPartitionCheck())
1116 return ldof_ltdof[sldof] +
1118 ldof_group[sldof])] /
vdim;
1122 return (ldof_ltdof[sldof*
vdim] +
1123 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1130 return ldof_ltdof[sldof] +
1132 ldof_group[sldof])] /
vdim;
1136 return (ldof_ltdof[sldof*
vdim] +
1137 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1144 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1149 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1156 if (Pconf) {
return Pconf; }
1187 if (Rconf) {
return Rconf; }
1224 if (num_face_nbrs == 0)
1230 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1231 MPI_Request *send_requests = requests;
1232 MPI_Request *recv_requests = requests + num_face_nbrs;
1233 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1239 Table send_nbr_elem_dof;
1246 for (
int fn = 0; fn < num_face_nbrs; fn++)
1251 for (
int i = 0; i < num_my_elems; i++)
1254 for (
int j = 0; j < ldofs.
Size(); j++)
1256 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1258 if (ldof_marker[ldof] != fn)
1260 ldof_marker[ldof] = fn;
1270 MyComm, &send_requests[fn]);
1273 MyComm, &recv_requests[fn]);
1276 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1281 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1289 int *send_I = send_nbr_elem_dof.
GetI();
1291 for (
int fn = 0; fn < num_face_nbrs; fn++)
1295 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1296 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1298 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1299 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1302 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1303 send_nbr_elem_dof.
MakeJ();
1307 for (
int fn = 0; fn < num_face_nbrs; fn++)
1312 for (
int i = 0; i < num_my_elems; i++)
1315 for (
int j = 0; j < ldofs.
Size(); j++)
1317 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1319 if (ldof_marker[ldof] != fn)
1321 ldof_marker[ldof] = fn;
1326 send_el_off[fn] + i, ldofs, ldofs.
Size());
1333 int *send_J = send_nbr_elem_dof.
GetJ();
1334 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1338 int j_end = send_I[send_el_off[fn+1]];
1340 for (
int i = 0; i < num_ldofs; i++)
1342 int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
1343 ldof_marker[ldof] = i;
1346 for ( ; j < j_end; j++)
1348 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1349 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1353 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1360 for (
int fn = 0; fn < num_face_nbrs; fn++)
1365 MPI_Isend(send_J + send_I[send_el_off[fn]],
1366 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1367 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1369 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1370 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1371 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1374 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1377 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1380 int j_end = recv_I[recv_el_off[fn+1]];
1382 for ( ; j < j_end; j++)
1395 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1399 for (
int fn = 0; fn < num_face_nbrs; fn++)
1406 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1410 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1413 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1414 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1421 for (
int fn = 0; fn < num_face_nbrs; fn++)
1426 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1427 MyComm, &send_requests[fn]);
1429 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1430 MyComm, &recv_requests[fn]);
1433 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1437 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1451 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1471 if (
vdim == 1 || doftrans == NULL)
1488 int el1, el2, inf1, inf2;
1501 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1503 for (
int j = 0; j < vdofs.
Size(); j++)
1505 const int ldof = vdofs[j];
1506 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1518 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE" 1519 " does not support NURBS!");
1539 #if MFEM_HYPRE_VERSION <= 22200 1540 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1541 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1542 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1551 void ParFiniteElementSpace::ConstructTrueDofs()
1558 GetGroupComm(*gcomm, 1, &ldof_sign);
1568 for (gr = 1; gr < group_ldof.
Size(); gr++)
1570 const int *ldofs = group_ldof.
GetRow(gr);
1571 const int nldofs = group_ldof.
RowSize(gr);
1572 for (i = 0; i < nldofs; i++)
1574 ldof_group[ldofs[i]] = gr;
1579 for (i = 0; i < nldofs; i++)
1581 ldof_ltdof[ldofs[i]] = -2;
1588 for (i = 0; i < n; i++)
1590 if (ldof_ltdof[i] == -1)
1592 ldof_ltdof[i] = ltdof_size++;
1598 gcomm->
Bcast(ldof_ltdof);
1601 void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1604 GroupTopology > = pNURBSext()->
gtopo;
1605 gcomm =
new GroupCommunicator(gt);
1610 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1614 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1616 for (
int i = 0; i < n; i++)
1618 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1622 gcomm->
Create(ldof_group);
1630 for (
int i = 0; i < n; i++)
1632 if (gt.IAmMaster(ldof_group[i]))
1634 ldof_ltdof[i] = ltdof_size;
1645 gcomm->
Bcast(ldof_ltdof);
1648 void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1649 Array<int> &dofs)
const 1653 for (
int j = 0; j < nv; j++)
1655 dofs[j] =
ndofs + nv*
id.index + j;
1659 void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1660 Array<int> &dofs)
const 1664 dofs.SetSize(2*nv + ne);
1669 for (
int i = 0; i < 2; i++)
1671 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1672 for (
int j = 0; j < nv; j++)
1674 dofs[i*nv + j] = k++;
1678 int k =
ndofs + ngvdofs + (edge_id.index - pncmesh->
GetNEdges())*ne;
1679 for (
int j = 0; j < ne; j++)
1681 dofs[2*nv + j] = k++;
1685 void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1686 Array<int> &dofs)
const 1688 int nfv, V[4], E[4], Eo[4];
1695 int nf = (nfv == 3) ? nf_tri : nf_quad;
1697 dofs.SetSize(nfv*(nv + ne) + nf);
1700 for (
int i = 0; i < nfv; i++)
1703 int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1704 for (
int j = 0; j < nv; j++)
1706 dofs[offset++] = first + j;
1710 for (
int i = 0; i < nfv; i++)
1713 int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
1714 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
1716 for (
int j = 0; j < ne; j++)
1718 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
1719 : (-1 - (first + (-1 - ind[j])));
1723 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1724 int first =
ndofs + ngvdofs + ngedofs + nf_quad*ghost_face_index;
1726 for (
int j = 0; j < nf; j++)
1728 dofs[offset++] = first + j;
1732 void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
1733 Array<int> &dofs)
const 1738 case 0: GetGhostVertexDofs(
id, dofs);
break;
1739 case 1: GetGhostEdgeDofs(
id, dofs);
break;
1740 case 2: GetGhostFaceDofs(
id, dofs);
break;
1744 void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
1745 Array<int> &dofs)
const 1747 int ned, ghost, first;
1753 first = (
index < ghost)
1761 first = (
index < ghost)
1782 first =
ndofs + ngvdofs + ngedofs +
index*stride;
1788 for (
int i = 0; i < ned; i++)
1790 dofs[i] = first + i;
1794 int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof)
const 1806 return (
index < ghost)
1814 return (
index < ghost)
1816 :
ndofs + ngvdofs + (
index - ghost)*ned + edof;
1830 return ndofs + ngvdofs + ngedofs +
index*stride + edof;
1835 static int bisect(
const int* array,
int size,
int value)
1837 const int* end = array + size;
1838 const int* pos = std::upper_bound(array, end, value);
1839 MFEM_VERIFY(pos != array,
"value not found");
1842 MFEM_VERIFY(*(array+size - 1) == value,
"Last entry must be exact")
1844 return pos - array - 1;
1850 void ParFiniteElementSpace::UnpackDof(
int dof,
1851 int &entity,
int &
index,
int &edof)
const 1853 MFEM_VERIFY(dof >= 0,
"");
1859 entity = 0,
index = dof / nv, edof = dof % nv;
1866 entity = 1,
index = dof / ne, edof = dof % ne;
1875 index = dof / nf, edof = dof % nf;
1881 MFEM_ASSERT(table.Size() > 0,
"");
1882 int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
1883 index = bisect(table.GetI(), table.Size(), jpos);
1884 edof = dof - table.GetRow(
index)[0];
1889 MFEM_ABORT(
"Cannot unpack internal DOF");
1904 entity = 1,
index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
1911 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
1915 MFEM_ABORT(
"Out of range DOF.");
1923 struct PMatrixElement
1929 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
1930 : column(col), stride(str), value(val) {}
1932 bool operator<(
const PMatrixElement &other)
const 1933 {
return column < other.column; }
1935 typedef std::vector<PMatrixElement> List;
1943 PMatrixElement::List elems;
1946 void AddRow(
const PMatrixRow &other,
double coef)
1948 elems.reserve(elems.size() + other.elems.size());
1949 for (
unsigned i = 0; i < other.elems.size(); i++)
1951 const PMatrixElement &oei = other.elems[i];
1953 PMatrixElement(oei.column, oei.stride, coef * oei.value));
1960 if (!elems.size()) {
return; }
1961 std::sort(elems.begin(), elems.end());
1964 for (
unsigned i = 1; i < elems.size(); i++)
1966 if (elems[j].column == elems[i].column)
1968 elems[j].value += elems[i].value;
1972 elems[++j] = elems[i];
1978 void write(std::ostream &os,
double sign)
const 1980 bin_io::write<int>(os, elems.size());
1981 for (
unsigned i = 0; i < elems.size(); i++)
1983 const PMatrixElement &e = elems[i];
1984 bin_io::write<HYPRE_BigInt>(os, e.column);
1985 bin_io::write<int>(os, e.stride);
1986 bin_io::write<double>(os, e.value * sign);
1990 void read(std::istream &is,
double sign)
1992 elems.resize(bin_io::read<int>(is));
1993 for (
unsigned i = 0; i < elems.size(); i++)
1995 PMatrixElement &e = elems[i];
1996 e.column = bin_io::read<HYPRE_BigInt>(is);
1997 e.stride = bin_io::read<int>(is);
1998 e.value = bin_io::read<double>(is) * sign;
2006 class NeighborRowMessage :
public VarMessage<314>
2009 typedef NCMesh::MeshId MeshId;
2013 int entity,
index, edof;
2017 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row)
2018 : entity(ent),
index(idx), edof(edof), group(grp), row(row) {}
2020 RowInfo(
int ent,
int idx,
int edof, GroupId grp)
2021 : entity(ent),
index(idx), edof(edof), group(grp) {}
2024 NeighborRowMessage() : pncmesh(NULL) {}
2026 void AddRow(
int entity,
int index,
int edof, GroupId group,
2027 const PMatrixRow &row)
2029 rows.push_back(RowInfo(entity,
index, edof, group, row));
2032 const std::vector<RowInfo>& GetRows()
const {
return rows; }
2034 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2035 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
2037 typedef std::map<int, NeighborRowMessage> Map;
2040 std::vector<RowInfo> rows;
2043 const FiniteElementCollection* fec;
2045 virtual void Encode(
int rank);
2046 virtual void Decode(
int);
2049 void NeighborRowMessage::Encode(
int rank)
2051 std::ostringstream stream;
2053 Array<MeshId> ent_ids[3];
2054 Array<GroupId> group_ids[3];
2055 Array<int> row_idx[3];
2058 for (
unsigned i = 0; i < rows.size(); i++)
2060 const RowInfo &ri = rows[i];
2061 const MeshId &
id = pncmesh->GetNCList(ri.entity).LookUp(ri.index);
2062 ent_ids[ri.entity].Append(
id);
2063 row_idx[ri.entity].Append(i);
2064 group_ids[ri.entity].Append(ri.group);
2067 Array<GroupId> all_group_ids;
2068 all_group_ids.Reserve(rows.size());
2069 for (
int i = 0; i < 3; i++)
2071 all_group_ids.Append(group_ids[i]);
2074 pncmesh->AdjustMeshIds(ent_ids, rank);
2075 pncmesh->EncodeMeshIds(stream, ent_ids);
2076 pncmesh->EncodeGroups(stream, all_group_ids);
2079 for (
int ent = 0; ent < 3; ent++)
2081 const Array<MeshId> &ids = ent_ids[ent];
2082 for (
int i = 0; i < ids.Size(); i++)
2084 const MeshId &
id = ids[i];
2085 const RowInfo &ri = rows[row_idx[ent][i]];
2086 MFEM_ASSERT(ent == ri.entity,
"");
2088 #ifdef MFEM_DEBUG_PMATRIX 2089 mfem::out <<
"Rank " << pncmesh->MyRank <<
" sending to " << rank
2090 <<
": ent " << ri.entity <<
", index " << ri.index
2091 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/" 2092 << int(
id.local) <<
")" << std::endl;
2100 int eo = pncmesh->GetEdgeNCOrientation(
id);
2102 if ((edof = ind[edof]) < 0)
2109 bin_io::write<int>(stream, edof);
2110 ri.row.write(stream,
s);
2115 stream.str().swap(data);
2118 void NeighborRowMessage::Decode(
int rank)
2120 std::istringstream stream(data);
2122 Array<MeshId> ent_ids[3];
2123 Array<GroupId> group_ids;
2126 pncmesh->DecodeMeshIds(stream, ent_ids);
2127 pncmesh->DecodeGroups(stream, group_ids);
2129 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2130 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2133 rows.reserve(nrows);
2136 for (
int ent = 0, gi = 0; ent < 3; ent++)
2138 const Array<MeshId> &ids = ent_ids[ent];
2139 for (
int i = 0; i < ids.Size(); i++)
2141 const MeshId &
id = ids[i];
2142 int edof = bin_io::read<int>(stream);
2145 const int *ind = NULL;
2148 int eo = pncmesh->GetEdgeNCOrientation(
id);
2154 int fo = pncmesh->GetFaceOrientation(
id.
index);
2155 ind = fec->DofOrderForOrientation(geom, fo);
2158 #ifdef MFEM_DEBUG_PMATRIX 2159 mfem::out <<
"Rank " << pncmesh->MyRank <<
" receiving from " << rank
2160 <<
": ent " << ent <<
", index " <<
id.index
2161 <<
", edof " << edof <<
" (id " <<
id.element <<
"/" 2162 << int(
id.local) <<
")" << std::endl;
2166 double s = (edof < 0) ? -1.0 : 1.0;
2167 edof = (edof < 0) ? -1 - edof : edof;
2169 if (ind && (edof = ind[edof]) < 0)
2175 rows.push_back(RowInfo(ent,
id.
index, edof, group_ids[gi++]));
2176 rows.back().row.read(stream,
s);
2178 #ifdef MFEM_DEBUG_PMATRIX 2179 mfem::out <<
"Rank " << pncmesh->MyRank <<
" receiving from " << rank
2180 <<
": ent " << rows.back().entity <<
", index " 2181 << rows.back().index <<
", edof " << rows.back().edof
2189 ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
2191 NeighborRowMessage::Map &send_msg)
const 2194 UnpackDof(dof, ent, idx, edof);
2196 for (
const auto &rank : pncmesh->
GetGroup(group_id))
2200 NeighborRowMessage &msg = send_msg[rank];
2201 msg.AddRow(ent, idx, edof, group_id, row);
2202 msg.SetNCMesh(pncmesh);
2204 #ifdef MFEM_PMATRIX_STATS 2211 void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
2212 GroupId group_sent_id, GroupId group_id,
2213 NeighborRowMessage::Map &send_msg)
const 2216 UnpackDof(dof, ent, idx, edof);
2219 for (
unsigned i = 0; i < group.size(); i++)
2221 int rank = group[i];
2222 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
2224 NeighborRowMessage &msg = send_msg[rank];
2225 GroupId invalid = -1;
2226 msg.AddRow(ent, idx, edof, invalid, row);
2227 msg.SetNCMesh(pncmesh);
2229 #ifdef MFEM_PMATRIX_STATS 2232 #ifdef MFEM_DEBUG_PMATRIX 2234 << rank <<
": ent " << ent <<
", index" << idx
2235 <<
", edof " << edof << std::endl;
2241 #ifdef MFEM_DEBUG_PMATRIX 2242 void ParFiniteElementSpace
2243 ::DebugDumpDOFs(std::ostream &os,
2244 const SparseMatrix &deps,
2245 const Array<GroupId> &dof_group,
2246 const Array<GroupId> &dof_owner,
2247 const Array<bool> &finalized)
const 2249 for (
int i = 0; i < dof_group.Size(); i++)
2255 UnpackDof(i, ent, idx, edof);
2257 os << edof <<
" @ ";
2258 if (i >
ndofs) { os <<
"ghost "; }
2261 case 0: os <<
"vertex ";
break;
2262 case 1: os <<
"edge ";
break;
2263 default: os <<
"face ";
break;
2267 if (i < deps.Height() && deps.RowSize(i))
2269 os <<
"depends on ";
2270 for (
int j = 0; j < deps.RowSize(i); j++)
2272 os << deps.GetRowColumns(i)[j] <<
" (" 2273 << deps.GetRowEntries(i)[j] <<
")";
2274 if (j < deps.RowSize(i)-1) { os <<
", "; }
2283 os <<
"group " << dof_group[i] <<
" (";
2285 for (
unsigned j = 0; j < g.size(); j++)
2287 if (j) { os <<
", "; }
2291 os <<
"), owner " << dof_owner[i] <<
" (rank " 2292 << pncmesh->
GetGroup(dof_owner[i])[0] <<
"); " 2293 << (finalized[i] ?
"finalized" :
"NOT finalized");
2304 int ParFiniteElementSpace
2305 ::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
2306 Array<HYPRE_BigInt> &dof_offs,
2307 Array<HYPRE_BigInt> &tdof_offs,
2308 Array<int> *dof_tdof,
2315 "Nedelec NC tets of order >= 2 are not supported yet.");
2319 #ifdef MFEM_PMATRIX_STATS 2320 n_msgs_sent = n_msgs_recv = 0;
2321 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
2326 const int total_dofs =
ndofs + ngdofs;
2327 SparseMatrix deps(
ndofs, total_dofs);
2329 if (!dg && !partial)
2331 Array<int> master_dofs, slave_dofs;
2334 for (
int entity = 0; entity <= 2; entity++)
2336 const NCMesh::NCList &list = pncmesh->
GetNCList(entity);
2337 if (list.masters.Size() == 0) {
continue; }
2339 IsoparametricTransformation T;
2343 for (
const auto &mf : list.masters)
2346 if (pncmesh->
IsGhost(entity, mf.index))
2348 GetGhostDofs(entity, mf, master_dofs);
2355 if (master_dofs.Size() == 0) {
continue; }
2358 if (fe ==
nullptr) {
continue; }
2365 default: MFEM_ABORT(
"unsupported geometry");
2369 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
2371 const NCMesh::Slave &sf = list.slaves[si];
2372 if (pncmesh->
IsGhost(entity, sf.index)) {
continue; }
2374 constexpr
int variant = 0;
2375 GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
2376 if (!slave_dofs.Size()) {
continue; }
2378 list.OrientedPointMatrix(sf, T.GetPointMat());
2379 fe->GetLocalInterpolation(T, I);
2392 Array<GroupId> dof_group(total_dofs);
2393 Array<GroupId> dof_owner(total_dofs);
2401 auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
2402 this](
int entity,
const MeshId &id)
2404 if (
id.
index < 0) {
return; }
2409 GetBareDofs(entity,
id.
index, dofs);
2411 for (
auto dof : dofs)
2413 dof_owner[dof] = owner;
2414 dof_group[dof] = group;
2419 for (
int entity : {0,1,2})
2423 initialize_group_and_owner(entity,
id);
2427 initialize_group_and_owner(entity,
id);
2431 initialize_group_and_owner(entity,
id);
2438 Array<bool> finalized(total_dofs);
2442 int num_true_dofs = 0;
2443 for (
int i = 0; i <
ndofs; ++i)
2445 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
2448 finalized[i] =
true;
2452 #ifdef MFEM_DEBUG_PMATRIX 2454 auto dof_diagnostics = [&](
int dof,
bool print_diagnostic)
2456 const auto &comm_group = pncmesh->
GetGroup(dof_group[dof]);
2457 std::stringstream msg;
2458 msg << std::boolalpha;
2460 <<
" owner_rank " << pncmesh->
GetGroup(dof_owner[dof])[0] <<
" CommGroup {";
2461 for (
const auto &x : comm_group)
2465 msg <<
"} finalized " << finalized[dof];
2471 deps.GetRow(dof, cols, row);
2472 msg <<
" deps cols {";
2473 for (
const auto &x : cols)
2480 int entity,
index, edof;
2481 UnpackDof(dof, entity,
index, edof);
2482 msg <<
" entity " << entity <<
" index " <<
index <<
" edof " << edof;
2489 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
2493 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
2506 std::vector<PMatrixRow> pmatrix(total_dofs);
2509 const int vdim_factor = bynodes ? 1 :
vdim;
2510 const int dof_stride = bynodes ?
ndofs : 1;
2511 const int tdof_stride = bynodes ? num_true_dofs : 1;
2514 std::list<NeighborRowMessage::Map> send_msg;
2515 send_msg.push_back(NeighborRowMessage::Map());
2518 for (
int dof = 0, tdof = 0; dof <
ndofs; dof++)
2522 pmatrix[dof].elems.push_back(
2523 PMatrixElement(my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.));
2526 if (dof_group[dof] != 0)
2528 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
2531 for (
int vd = 0; vd <
vdim; vd++)
2533 const int vdof = dof*vdim_factor + vd*dof_stride;
2534 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
2536 if (R_) { (*R_)->Add(vtdof, vdof, 1.0); }
2537 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
2544 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2545 #ifdef MFEM_PMATRIX_STATS 2546 n_msgs_sent += send_msg.back().size();
2549 if (R_) { (*R_)->Finalize(); }
2554 NeighborRowMessage recv_msg;
2555 recv_msg.SetNCMesh(pncmesh);
2556 recv_msg.SetFEC(
fec);
2558 int num_finalized = num_true_dofs;
2560 buffer.elems.reserve(1024);
2562 while (num_finalized <
ndofs)
2565 if (send_msg.back().size())
2567 send_msg.push_back(NeighborRowMessage::Map());
2572 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2574 recv_msg.Recv(rank, size, MyComm);
2575 #ifdef MFEM_PMATRIX_STATS 2577 n_rows_recv += recv_msg.GetRows().size();
2580 for (
const auto &ri : recv_msg.GetRows())
2582 const int dof = PackDof(ri.entity, ri.index, ri.edof);
2583 pmatrix[dof] = ri.row;
2585 if (dof <
ndofs && !finalized[dof]) { ++num_finalized; }
2586 finalized[dof] =
true;
2588 if (ri.group >= 0 && dof_group[dof] != ri.group)
2591 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
2601 for (
int dof = 0; dof <
ndofs; dof++)
2603 const bool owned = (dof_owner[dof] == 0);
2609 UnpackDof(dof, ent, idx, edof);
2611 const int* dep_col = deps.GetRowColumns(dof);
2612 const double* dep_coef = deps.GetRowEntries(dof);
2613 int num_dep = deps.RowSize(dof);
2616 buffer.elems.clear();
2617 for (
int j = 0; j < num_dep; j++)
2619 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
2622 pmatrix[dof] = buffer;
2624 finalized[dof] =
true;
2629 const bool shared = (dof_group[dof] != 0);
2632 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
2639 #ifdef MFEM_DEBUG_PMATRIX 2640 static int dump = 0;
2644 snprintf(fname, 100,
"dofs%02d.txt", MyRank);
2645 std::ofstream
f(fname);
2646 DebugDumpDOFs(
f, deps, dof_group, dof_owner, finalized);
2652 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2653 #ifdef MFEM_PMATRIX_STATS 2654 n_msgs_sent += send_msg.back().size();
2660 *P_ = MakeVDimHypreMatrix(pmatrix,
ndofs, num_true_dofs,
2661 dof_offs, tdof_offs);
2667 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2669 recv_msg.RecvDrop(rank, size, MyComm);
2673 for (
auto &msg : send_msg)
2675 NeighborRowMessage::WaitAllSent(msg);
2678 #ifdef MFEM_PMATRIX_STATS 2679 int n_rounds = send_msg.size();
2680 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
2681 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
2683 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
2684 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2685 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2686 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2687 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2688 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
2692 mfem::out <<
"P matrix stats (avg per rank): " 2693 << double(glob_rounds)/NRanks <<
" rounds, " 2694 << double(glob_msgs_sent)/NRanks <<
" msgs sent, " 2695 << double(glob_msgs_recv)/NRanks <<
" msgs recv, " 2696 << double(glob_rows_sent)/NRanks <<
" rows sent, " 2697 << double(glob_rows_recv)/NRanks <<
" rows recv, " 2698 << double(glob_rows_fwd)/NRanks <<
" rows forwarded." 2703 return num_true_dofs*
vdim;
2707 HypreParMatrix* ParFiniteElementSpace
2708 ::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
2709 int local_rows,
int local_cols,
2710 Array<HYPRE_BigInt> &row_starts,
2711 Array<HYPRE_BigInt> &col_starts)
const 2713 bool assumed = HYPRE_AssumedPartitionCheck();
2716 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
2717 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
2720 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
2721 std::map<HYPRE_BigInt, int> col_map;
2722 for (
int i = 0; i < local_rows; i++)
2724 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2726 const PMatrixElement &elem = rows[i].elems[j];
2728 if (col >= first_col && col < next_col)
2735 for (
int vd = 0; vd <
vdim; vd++)
2745 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(col_map.size());
2747 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
2749 cmap[offd_col] = it->first;
2750 it->second = offd_col++;
2753 HYPRE_Int *I_diag = Memory<HYPRE_Int>(
vdim*local_rows + 1);
2754 HYPRE_Int *I_offd = Memory<HYPRE_Int>(
vdim*local_rows + 1);
2756 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
2757 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
2759 double *A_diag = Memory<double>(nnz_diag);
2760 double *A_offd = Memory<double>(nnz_offd);
2762 int vdim1 = bynodes ?
vdim : 1;
2763 int vdim2 = bynodes ? 1 :
vdim;
2764 int vdim_offset = bynodes ? local_cols : 1;
2767 nnz_diag = nnz_offd = 0;
2769 for (
int vd1 = 0; vd1 < vdim1; vd1++)
2771 for (
int i = 0; i < local_rows; i++)
2773 for (
int vd2 = 0; vd2 < vdim2; vd2++)
2775 I_diag[vrow] = nnz_diag;
2776 I_offd[vrow++] = nnz_offd;
2778 int vd = bynodes ? vd1 : vd2;
2779 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2781 const PMatrixElement &elem = rows[i].elems[j];
2782 if (elem.column >= first_col && elem.column < next_col)
2784 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
2785 A_diag[nnz_diag++] = elem.value;
2789 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
2790 A_offd[nnz_offd++] = elem.value;
2796 MFEM_ASSERT(vrow ==
vdim*local_rows,
"");
2797 I_diag[vrow] = nnz_diag;
2798 I_offd[vrow] = nnz_offd;
2800 return new HypreParMatrix(MyComm,
2801 row_starts.Last(), col_starts.Last(),
2802 row_starts.GetData(), col_starts.GetData(),
2803 I_diag, J_diag, A_diag,
2804 I_offd, J_offd, A_offd,
2805 col_map.size(), cmap);
2808 template <
typename int_type>
2809 static int_type* make_i_array(
int nrows)
2811 int_type *I = Memory<int_type>(nrows+1);
2812 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
2816 template <
typename int_type>
2817 static int_type* make_j_array(int_type* I,
int nrows)
2820 for (
int i = 0; i < nrows; i++)
2822 if (I[i] >= 0) { nnz++; }
2824 int_type *J = Memory<int_type>(nnz);
2827 for (
int i = 0, k = 0; i <= nrows; i++)
2829 int_type col = I[i];
2831 if (col >= 0) { J[k++] = col; }
2837 ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
2838 const Table* old_elem_dof,
2839 const Table* old_elem_fos)
2841 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
2842 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to " 2843 "be called before ParFiniteElementSpace::RebalanceMatrix");
2845 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2846 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2849 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
2855 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
2856 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
2857 "Mesh::Rebalance was not called before " 2858 "ParFiniteElementSpace::RebalanceMatrix");
2861 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
2862 for (
int i = 0; i < pmesh->
GetNE(); i++)
2864 if (old_index[i] >= 0)
2866 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
2869 for (
int vd = 0; vd <
vdim; vd++)
2871 for (
int j = 0; j < dofs.Size(); j++)
2874 if (row < 0) { row = -1 - row; }
2876 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
2877 if (col < 0) { col = -1 - col; }
2884 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
2887 Array<int> new_elements;
2888 Array<long> old_remote_dofs;
2889 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
2892 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
2893 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
2896 const long* old_dofs = &old_remote_dofs[pos];
2897 pos += dofs.Size() *
vdim;
2899 for (
int vd = 0; vd <
vdim; vd++)
2901 for (
int j = 0; j < dofs.Size(); j++)
2904 if (row < 0) { row = -1 - row; }
2906 if (i_diag[row] == i_diag[row+1])
2908 i_offd[row] = old_dofs[j + vd * dofs.Size()];
2915 #ifndef HYPRE_MIXEDINT 2916 HYPRE_Int *i_offd_hi = i_offd;
2919 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
2920 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
2921 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
2925 int offd_cols = i_offd_hi[vsize];
2926 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
2927 for (
int i = 0; i < offd_cols; i++)
2929 cmap_offd[i].one = j_offd[i];
2930 cmap_offd[i].two = i;
2933 #ifndef HYPRE_MIXEDINT 2934 HYPRE_Int *j_offd_hi = j_offd;
2936 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
2937 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
2940 SortPairs<HYPRE_BigInt, int>(cmap_offd, offd_cols);
2943 for (
int i = 0; i < offd_cols; i++)
2945 cmap[i] = cmap_offd[i].one;
2946 j_offd_hi[cmap_offd[i].two] = i;
2950 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
2951 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
2956 struct DerefDofMessage
2958 std::vector<HYPRE_BigInt> dofs;
2959 MPI_Request request;
2963 ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
2964 const Table* old_elem_dof,
2965 const Table *old_elem_fos)
2967 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
2969 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
2970 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
2972 #if 0 // check no longer seems to work with NC tet refinement 2973 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
2974 "Previous space is not finer.");
2982 Mesh::GeometryList elem_geoms(*
mesh);
2984 Array<int> dofs, old_dofs, old_vdofs;
2987 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
2994 for (
int i = 0; i < elem_geoms.Size(); i++)
3000 const CoarseFineTransformations &dtrans =
3001 old_pncmesh->GetDerefinementTransforms();
3002 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
3004 std::map<int, DerefDofMessage> messages;
3006 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
3007 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
3011 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3013 const Embedding &emb = dtrans.embeddings[k];
3015 int fine_rank = old_ranks[k];
3016 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
3017 : old_pncmesh->ElementRank(emb.parent);
3019 if (coarse_rank != MyRank && fine_rank == MyRank)
3021 old_elem_dof->GetRow(k, dofs);
3024 DerefDofMessage &msg = messages[k];
3025 msg.dofs.resize(dofs.Size());
3026 for (
int i = 0; i < dofs.Size(); i++)
3028 msg.dofs[i] = old_offset + dofs[i];
3031 MPI_Isend(&msg.dofs[0], msg.dofs.size(), HYPRE_MPI_BIG_INT,
3032 coarse_rank, 291, MyComm, &msg.request);
3034 else if (coarse_rank == MyRank && fine_rank != MyRank)
3036 MFEM_ASSERT(emb.parent >= 0,
"");
3039 DerefDofMessage &msg = messages[k];
3040 msg.dofs.resize(ldof[geom]*
vdim);
3042 MPI_Irecv(&msg.dofs[0], ldof[geom]*
vdim, HYPRE_MPI_BIG_INT,
3043 fine_rank, 291, MyComm, &msg.request);
3051 for (
int i = 0; i < elem_geoms.Size(); i++)
3057 SparseMatrix *diag =
new SparseMatrix(
ndofs*
vdim, old_ndofs*
vdim);
3059 Array<char> mark(diag->Height());
3064 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3066 const Embedding &emb = dtrans.embeddings[k];
3067 if (emb.parent < 0) {
continue; }
3069 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3070 int fine_rank = old_ranks[k];
3072 if (coarse_rank == MyRank && fine_rank == MyRank)
3075 DenseMatrix &lR = localR[geom](emb.matrix);
3078 old_elem_dof->GetRow(k, old_dofs);
3080 for (
int vd = 0; vd <
vdim; vd++)
3082 old_dofs.Copy(old_vdofs);
3085 for (
int i = 0; i < lR.Height(); i++)
3087 if (!std::isfinite(lR(i, 0))) {
continue; }
3090 int m = (r >= 0) ? r : (-1 - r);
3092 if (is_dg || !mark[m])
3095 diag->SetRow(r, old_vdofs, row);
3105 for (
auto it = messages.begin(); it != messages.end(); ++it)
3107 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
3111 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
3113 std::map<HYPRE_BigInt, int> col_map;
3114 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3116 const Embedding &emb = dtrans.embeddings[k];
3117 if (emb.parent < 0) {
continue; }
3119 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3120 int fine_rank = old_ranks[k];
3122 if (coarse_rank == MyRank && fine_rank != MyRank)
3125 DenseMatrix &lR = localR[geom](emb.matrix);
3129 DerefDofMessage &msg = messages[k];
3130 MFEM_ASSERT(msg.dofs.size(),
"");
3132 for (
int vd = 0; vd <
vdim; vd++)
3134 MFEM_ASSERT(ldof[geom],
"");
3137 for (
int i = 0; i < lR.Height(); i++)
3139 if (!std::isfinite(lR(i, 0))) {
continue; }
3142 int m = (r >= 0) ? r : (-1 - r);
3144 if (is_dg || !mark[m])
3147 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
3148 for (
int j = 0; j < ldof[geom]; j++)
3150 if (row[j] == 0.0) {
continue; }
3151 int &lcol = col_map[remote_dofs[j]];
3152 if (!lcol) { lcol = col_map.size(); }
3153 offd->_Set_(m, lcol-1, row[j]);
3164 offd->SetWidth(col_map.size());
3167 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
3168 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
3170 cmap[it->second-1] = it->first;
3177 int width = offd->Width();
3178 Array<Pair<HYPRE_BigInt, int> > reorder(width);
3179 for (
int i = 0; i < width; i++)
3181 reorder[i].one = cmap[i];
3186 Array<int> reindex(width);
3187 for (
int i = 0; i < width; i++)
3189 reindex[reorder[i].two] = i;
3190 cmap[i] = reorder[i].one;
3193 int *J = offd->GetJ();
3194 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
3196 J[i] = reindex[J[i]];
3198 offd->SortColumnIndices();
3201 HypreParMatrix* new_R;
3202 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
3203 dof_offsets, old_dof_offsets, diag, offd, cmap,
3206 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
3211 void ParFiniteElementSpace::Destroy()
3222 delete Pconf; Pconf = NULL;
3223 delete Rconf; Rconf = NULL;
3226 delete gcomm; gcomm = NULL;
3235 void ParFiniteElementSpace::CopyProlongationAndRestriction(
3236 const FiniteElementSpace &fes,
const Array<int> *perm)
3240 MFEM_VERIFY(pfes != NULL,
"");
3241 MFEM_VERIFY(P == NULL,
"");
3242 MFEM_VERIFY(R == NULL,
"");
3245 pfes->Dof_TrueDof_Matrix();
3247 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
3253 int n = perm->Size();
3254 perm_mat =
new SparseMatrix(n, fes.GetVSize());
3255 for (
int i=0; i<n; ++i)
3259 perm_mat->Set(i, j,
s);
3261 perm_mat->Finalize();
3265 if (pfes->P != NULL)
3268 else { P =
new HypreParMatrix(*pfes->P); }
3271 else if (perm != NULL)
3277 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
3278 col_starts, perm_mat);
3281 if (pfes->R != NULL)
3283 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
3284 else { R =
new SparseMatrix(*pfes->R); }
3286 else if (perm != NULL)
3307 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
3318 false, Tgf.OwnsOperator(),
false));
3319 Tgf.SetOperatorOwner(
false);
3326 "Parallel variable order space not supported yet.");
3334 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after " 3335 "each mesh modification.");
3344 Table* old_elem_dof = NULL;
3345 Table* old_elem_fos = NULL;
3356 Swap(dof_offsets, old_dof_offsets);
3377 old_elem_fos, old_ndofs));
3380 old_elem_dof = NULL;
3381 old_elem_fos = NULL;
3393 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
3399 false,
false,
true));
3406 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
3414 delete old_elem_dof;
3415 delete old_elem_fos;
3419 void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
3422 MFEM_VERIFY(new_pmesh != NULL,
3423 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
3430 : gc(gc_), local(local_)
3435 for (
int g=1; g<group_ldof.
Size(); ++g)
3439 n_external += group_ldof.
RowSize(g);
3442 int tsize = lsize - n_external;
3448 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3466 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
3468 gc(pfes.GroupComm()),
3474 for (
int gr = 1; gr < group_ldof.Size(); gr++)
3493 for ( ; j < end; j++)
3499 for ( ; j <
Height(); j++)
3513 const double *xdata = x.
HostRead();
3517 const int in_layout = 2;
3528 for (
int i = 0; i < m; i++)
3531 std::copy(xdata+j-i, xdata+end-i, ydata+j);
3534 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
3536 const int out_layout = 0;
3549 const double *xdata = x.
HostRead();
3559 for (
int i = 0; i < m; i++)
3562 std::copy(xdata+j, xdata+end, ydata+j-i);
3565 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
3567 const int out_layout = 2;
3577 mpi_gpu_aware(
Device::GetGPUAwareMPI())
3580 const int tdofs = R->
Height();
3581 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
3595 unique_ltdof.
Sort();
3598 for (
int i = 0; i < shared_ltdof.Size(); i++)
3600 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
3601 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
3626 int req_counter = 0;
3631 if (send_size > 0) { req_counter++; }
3635 if (recv_size > 0) { req_counter++; }
3637 requests =
new MPI_Request[req_counter];
3643 pfes.GetRestrictionMatrix(),
3646 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
3650 static void ExtractSubVector(
const Array<int> &indices,
3653 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
3654 auto y = vout.
Write();
3655 const auto x = vin.
Read();
3656 const auto I = indices.
Read();
3674 static void SetSubVector(
const Array<int> &indices,
3677 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
3680 const auto x = vin.
Read();
3681 const auto I = indices.
Read();
3708 int req_counter = 0;
3729 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3738 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3747 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
3778 static void AddSubVector(
const Array<int> &unique_dst_indices,
3785 const auto x = src.
Read();
3786 const auto DST_I = unique_dst_indices.
Read();
3787 const auto SRC_O = unique_to_src_offsets.
Read();
3788 const auto SRC_I = unique_to_src_indices.
Read();
3791 const int dst_idx = DST_I[i];
3792 double sum = y[dst_idx];
3793 const int end = SRC_O[i+1];
3794 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
3810 int req_counter = 0;
3821 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3830 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3839 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
Abstract class for all finite elements.
VDofTransformation VDoFTrans
void SendRebalanceDofs(int old_ndofs, const Table &old_element_dofs, long old_global_offset, FiniteElementSpace *space)
Use the communication pattern from last Rebalance() to send element DOFs.
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
void SetSubVector(const Array< int > &dofs, const double value)
Set the entries listed in dofs to the given value.
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1)
BiLinear2DFiniteElement QuadrilateralFE
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
HYPRE_BigInt GetMyTDofOffset() const
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
std::vector< int > CommGroup
const FiniteElement * GetFaceNbrFaceFE(int i) const
HYPRE_BigInt GetGlobalScalarTDofNumber(int sldof)
Operator that extracts Face degrees of freedom in parallel.
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T...
static void AddDependencies(SparseMatrix &deps, Array< int > &master_dofs, Array< int > &slave_dofs, DenseMatrix &I, int skipfirst=0)
int GetNGhostEdges() const
void AddColumnsInRow(int r, int ncol)
Linear1DFiniteElement SegmentFE
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
const FiniteElement * GetFaceNbrFE(int i) const
Geometry::Type GetElementBaseGeometry(int i) const
Field is discontinuous across element interfaces.
int TrueVSize() const
Obsolete, kept for backward compatibility.
int Dimension() const
Dimension of the reference space used within the elements.
virtual int GetContType() const =0
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
void SetSize(int s)
Resize the vector to size s.
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
virtual const Operator * GetRestrictionOperator() const
Array< Element * > face_nbr_elements
void Delete()
Delete the owned pointers and reset the Memory object.
void Create(const Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
virtual const double * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with ReduceBegin().
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
virtual void Update(bool want_transform=true)
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Pointer to an Operator of a specified type.
void SetDims(int rows, int nnz)
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
virtual DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'. The returned indices are offsets in...
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
int Size() const
Returns the size of the vector.
virtual double * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
int vdim
Vector dimension (number of unknowns per degree of freedom).
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
HYPRE_BigInt GetMyDofOffset() const
void DivideByGroupSize(double *vec)
Scale a vector of true dofs.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
void LoseData()
Call this if data has been stolen.
Abstract parallel finite element space.
bool operator<(const Pair< A, B > &p, const Pair< A, B > &q)
Comparison operator for class Pair, based on the first element only.
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor...
void Lose_Dof_TrueDof_Matrix()
void GetNeighborLDofTable(Table &nbr_ldof) const
Dofs to be received from communication neighbors.
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
void GroupEdge(int group, int i, int &edge, int &o) const
virtual const SparseMatrix * GetRestrictionMatrix() const
Get the R matrix which restricts a local dof vector to true dof vector.
const FiniteElementCollection * fec
Associated FE collection (not owned).
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
int GetNEdges() const
Return the number of edges in the NCMesh.
void DeleteAll()
Delete the whole array.
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
void AddConnections(int r, const int *c, int nc)
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
virtual int DofForGeometry(Geometry::Type GeomType) const =0
std::function< double(const Vector &)> f(double mass_coeff)
int uni_fdof
of single face DOFs if all faces uniform; -1 otherwise
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
ID for class SparseMatrix.
DofTransformation * GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const
Determine the boundary degrees of freedom.
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
ID for the base class Operator, i.e. any type.
Array< int > face_nbr_elements_offset
void ExchangeFaceNbrData()
void BuildElementToDofTable() const
double * GetData() const
Returns the matrix data array.
void write(std::ostream &os, T value)
Write 'value' to stream.
Array< DofTransformation * > DoFTrans
int GetMaxElementOrder() const
Return the maximum polynomial order.
Geometry::Type GetGeometryType() const
static const int Dimension[NumGeom]
const CommGroup & GetGroup(GroupId id) const
Return a list of ranks contained in the group of the given ID.
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
int GetNFaces() const
Return the number of (2D) faces in the NCMesh.
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
int Append(const T &el)
Append element 'el' to array, resize if necessary.
GroupId GetEntityGroupId(int entity, int index)
const FiniteElementCollection * FEColl() const
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
Memory< int > & GetJMemory()
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
int GroupNVertices(int group) const
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root.
Operator that extracts Face degrees of freedom for NCMesh in parallel.
int GroupVertex(int group, int i) const
void AddConnection(int r, int c)
const int * HostReadJ() const
void LoseData()
NULL-ifies the data.
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 BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root.
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...
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
int GetNFaceNeighbors() const
void CopyFrom(const U *src)
Copy from src into this array. Copies enough entries to fill the Capacity size of this array...
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes, defined on a coarse mesh, to this FE space, defined on a refined mesh.
T read(std::istream &is)
Read a value from the stream and return it.
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Tangential components of vector field.
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
HYPRE_BigInt GlobalTrueVSize() const
bool Finalized() const
Returns whether or not CSR format has been finalized.
int GroupNTriangles(int group) const
virtual DofTransformation * GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i'th element.
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 ...
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
virtual int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const
virtual const int * DofOrderForOrientation(Geometry::Type GeomType, int Or) const =0
Returns an array, say p, that maps a local permuted index i to a local base index: base_i = p[i]...
void Swap(Array< T > &, Array< T > &)
double p(const Vector &x, double t)
Memory< int > & GetIMemory()
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
void GetNeighborLTDofTable(Table &nbr_ltdof) const
Dofs to be sent to communication neighbors.
const GroupTopology & GetGroupTopology()
Get a reference to the associated GroupTopology object.
virtual const FiniteElement * GetFE(int i) const
void forall(int N, lambda &&body)
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
HYPRE_BigInt GlobalVSize() const
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
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...
void AddAColumnInRow(int r)
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
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...
int GetLocalTDofNumber(int ldof) const
GroupId GetEntityOwnerId(int entity, int index)
Return vertex/edge/face ('entity' == 0/1/2, resp.) owner.
ParFiniteElementSpace(const ParFiniteElementSpace &orig, ParMesh *pmesh=NULL, const FiniteElementCollection *fec=NULL)
Copy constructor: deep copy all data from orig except the ParMesh, the FiniteElementCollection, and some derived data.
int GetDof() const
Returns the number of degrees of freedom in the finite element.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
virtual 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...
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
MFEM_EXPORT Linear2DFiniteElement TriangleFE
const int * HostReadI() const
HypreParMatrix * Transpose() const
Returns the transpose of *this.
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
int height
Dimension of the output / number of rows in the matrix.
Array< HYPRE_BigInt > face_nbr_glob_dof_map
HYPRE_BigInt * GetDofOffsets() const
int GetNE() const
Returns number of elements.
virtual int GetTrueVSize() const
Return the number of local vector true dofs.
NURBSExtension * NURBSext
Optional NURBS mesh extension.
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
Operator::Type Type() const
Get the currently set operator type id.
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
void GetFaceNbrElementFaces(int i, Array< int > &fcs, Array< int > &cor) const
MPI_Comm GetComm() const
Return the MPI communicator.
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Table face_nbr_element_dof
int HasFaceDofs(Geometry::Type geom, int p) const
Mesh * mesh
The mesh that FE space lives on (not owned).
int GetGroupSize(int g) const
Get the number of processors in a group.
GridFunction interpolation operator applicable after mesh refinement.
void PrintPartitionStats()
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
int Size() const
Returns the number of TYPE I elements.
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
int GetNGhostFaces() const
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
General triple product operator x -> A*B*C*x, with ownership of the factors.
Array< MeshId > conforming
int GetNGhostVertices() const
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) 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.).
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
int index(int i, int j, int nx, int ny)
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root.
int FirstFaceDof(int face, int variant=0) const
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
Operator * Ptr() const
Access the underlying Operator pointer.
int Size_of_connections() const
Operator that extracts Face degrees of freedom for NCMesh in parallel.
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
int Size() const
Return the logical size of the array.
bool Nonconforming() const
void MakeRef(T *, int)
Make this Array a reference to a pointer.
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 GetFaceNbrRank(int fn) const
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
int GroupNEdges(int group) const
Identity Operator I: x -> x.
bool UseDevice() const
Read the internal device flag.
ID for class HypreParMatrix.
int GroupNQuadrilaterals(int group) const
static bool DofFinalizable(int dof, const Array< bool > &finalized, const SparseMatrix &deps)
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with BcastBegin().
Biwise-OR of all device backends.
NURBSExtension * NURBSext
void GroupQuadrilateral(int group, int i, int &face, int &o) const
Operation GetLastOperation() const
Return type of last modification of the mesh.
int GetNVertices() const
Return the number of vertices in the NCMesh.
Table send_face_nbr_elements
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Base class for operators that extracts Face degrees of freedom.
Wrapper for hypre's ParCSR matrix class.
void GroupTriangle(int group, int i, int &face, int &o) const
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
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...
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
Class for parallel meshes.
Geometry::Type GetBdrElementBaseGeometry(int i) const
Abstract data type element.
HYPRE_BigInt * GetTrueDofOffsets() const
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
virtual DofTransformation * GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i'th boundary element.
int width
Dimension of the input / number of columns in the matrix.
int VDofToDof(int vdof) const
Compute the inverse of the Dof to VDof mapping for a single index vdof.
void GetTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer GridFunction data from coarse_fes...
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.