12 #include "../config/config.hpp"
18 #include "../general/forall.hpp"
19 #include "../general/sort_pairs.hpp"
20 #include "../mesh/mesh_headers.hpp"
21 #include "../general/binaryio.hpp"
34 ParInit(pmesh ? pmesh : orig.pmesh);
50 f ? f : global_fes->FEColl(),
51 global_fes->GetVDim(), global_fes->GetOrdering())
70 int dim,
int ordering)
80 if (globNURBSext == NULL) {
return NULL; }
81 const ParNURBSExtension *pNURBSext =
82 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
83 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
85 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
87 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
90 void ParFiniteElementSpace::ParInit(ParMesh *pm)
115 MFEM_ASSERT(
own_ext,
"internal error");
117 ParNURBSExtension *pNe =
new ParNURBSExtension(
136 void ParFiniteElementSpace::Construct()
139 " for ParFiniteElementSpace yet.");
143 ConstructTrueNURBSDofs();
144 GenerateGlobalOffsets();
149 GenerateGlobalOffsets();
162 ngedofs = ngfdofs = 0;
182 ngdofs = ngvdofs + ngedofs + ngfdofs;
186 ltdof_size = BuildParallelConformingInterpolation(
187 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
197 long ltdofs = ltdof_size;
198 long min_ltdofs, max_ltdofs, sum_ltdofs;
200 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG, MPI_MIN, 0, MyComm);
201 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG, MPI_MAX, 0, MyComm);
202 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG, MPI_SUM, 0, MyComm);
206 double avg = double(sum_ltdofs) / NRanks;
207 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
208 <<
", avg " << std::fixed << std::setprecision(1) << avg
209 <<
", max " << max_ltdofs
210 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
218 mfem::out <<
"True DOFs by rank: " << ltdofs;
219 for (
int i = 1; i < NRanks; i++)
222 MPI_Recv(<dofs, 1, MPI_LONG, i, 123, MyComm, &status);
229 MPI_Send(<dofs, 1, MPI_LONG, 0, 123, MyComm);
234 void ParFiniteElementSpace::GetGroupComm(
239 int nvd, ned, ntd = 0, nqd = 0;
242 int group_ldof_counter;
267 group_ldof_counter = 0;
268 for (gr = 1; gr < ng; gr++)
271 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
277 group_ldof_counter *=
vdim;
280 group_ldof.
SetDims(ng, group_ldof_counter);
283 group_ldof_counter = 0;
284 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
285 for (gr = 1; gr < ng; gr++)
287 int j, k, l, m, o, nv, ne, nt, nq;
298 for (j = 0; j < nv; j++)
304 for (l = 0; l < nvd; l++, m++)
314 for (l = 0; l < dofs.
Size(); l++)
316 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
324 for (j = 0; j < ne; j++)
331 for (l = 0; l < ned; l++)
335 dofs[l] = m + (-1-ind[l]);
338 (*g_ldof_sign)[dofs[l]] = -1;
343 dofs[l] = m + ind[l];
352 for (l = 0; l < dofs.
Size(); l++)
354 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
362 for (j = 0; j < nt; j++)
369 for (l = 0; l < ntd; l++)
373 dofs[l] = m + (-1-ind[l]);
376 (*g_ldof_sign)[dofs[l]] = -1;
381 dofs[l] = m + ind[l];
390 for (l = 0; l < dofs.
Size(); l++)
392 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
400 for (j = 0; j < nq; j++)
407 for (l = 0; l < nqd; l++)
411 dofs[l] = m + (-1-ind[l]);
414 (*g_ldof_sign)[dofs[l]] = -1;
419 dofs[l] = m + ind[l];
428 for (l = 0; l < dofs.
Size(); l++)
430 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
435 group_ldof.
GetI()[gr+1] = group_ldof_counter;
441 void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
445 for (
int i = 0; i < dofs.Size(); i++)
449 if (ldof_sign[-1-dofs[i]] < 0)
451 dofs[i] = -1-dofs[i];
456 if (ldof_sign[dofs[i]] < 0)
458 dofs[i] = -1-dofs[i];
464 void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
466 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
467 ApplyLDofSigns(all_dofs);
489 ApplyLDofSigns(dofs);
514 ApplyLDofSigns(dofs);
530 ApplyLDofSigns(dofs);
548 auto key = std::make_tuple(is_dg_space, e_ordering, type, m);
549 auto itr =
L2F.find(key);
550 if (itr !=
L2F.end())
588 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
589 pmesh->
GroupEdge(group, ei, l_edge, ori);
599 for (
int i = 0; i < dofs.
Size(); i++)
601 const int di = dofs[i];
602 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
611 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
612 "invalid triangular face index");
623 for (
int i = 0; i < dofs.
Size(); i++)
625 const int di = dofs[i];
626 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
635 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
636 "invalid quadrilateral face index");
647 for (
int i = 0; i < dofs.
Size(); i++)
649 const int di = dofs[i];
650 dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
655 void ParFiniteElementSpace::GenerateGlobalOffsets()
const
667 if (HYPRE_AssumedPartitionCheck())
670 GroupTopology > = GetGroupTopo();
671 int nsize = gt.GetNumNeighbors()-1;
672 MPI_Request *requests =
new MPI_Request[2*nsize];
673 MPI_Status *statuses =
new MPI_Status[2*nsize];
674 tdof_nb_offsets.
SetSize(nsize+1);
675 tdof_nb_offsets[0] = tdof_offsets[0];
678 int request_counter = 0;
679 for (
int i = 1; i <= nsize; i++)
681 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
682 gt.GetNeighborRank(i), 5365, MyComm,
683 &requests[request_counter++]);
685 for (
int i = 1; i <= nsize; i++)
687 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
688 gt.GetNeighborRank(i), 5365, MyComm,
689 &requests[request_counter++]);
691 MPI_Waitall(request_counter, requests, statuses);
698 void ParFiniteElementSpace::CheckNDSTriaDofs()
701 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
722 for (
int g = 1; g < ngrps; g++)
729 int loc_nd_strias = strias ? 1 : 0;
730 int glb_nd_strias = 0;
731 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1,
732 MPI_INTEGER, MPI_SUM, MyComm);
733 nd_strias = glb_nd_strias > 0;
736 void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
748 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
749 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
752 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
753 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
761 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
763 i_diag[0] = i_offd[0] = 0;
764 diag_counter = offd_counter = 0;
765 for (
int i = 0; i < ldof; i++)
770 j_diag[diag_counter++] = ltdof_i;
775 cmap_j_offd[offd_counter].two = offd_counter;
778 i_diag[i+1] = diag_counter;
779 i_offd[i+1] = offd_counter;
782 SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_counter);
784 for (
int i = 0; i < offd_counter; i++)
786 cmap[i] = cmap_j_offd[i].one;
787 j_offd[cmap_j_offd[i].two] = i;
790 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
791 i_diag, j_diag, i_offd, j_offd,
803 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
804 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
811 Array<int> ldsize(ldof); ldsize = 0;
812 Array<int> ltori(ldof); ltori = 0;
817 for (
int g = 1; g < ngrps; g++)
826 for (
int i=0; i<sdofs.Size(); i++)
828 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
829 if (ldsize[ind] == 0) { nnz_offd++; }
835 int face, ori, info1, info2;
839 for (
int i=0; i<3*
nedofs; i++)
841 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
842 if (ldsize[ind] == 0) { nnz_offd++; }
845 for (
int i=3*nedofs; i<sdofs.Size(); i++)
847 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
848 ldsize[sdofs[i]] = 2;
849 ltori[sdofs[i]] = info2 % 64;
855 for (
int i=0; i<sdofs.Size(); i++)
857 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
858 if (ldsize[ind] == 0) { nnz_offd++; }
865 HYPRE_Int *i_diag =
new HYPRE_Int[ldof+1];
866 HYPRE_Int *j_diag =
new HYPRE_Int[ltdof];
867 double *d_diag =
new double[ltdof];
870 HYPRE_Int *i_offd =
new HYPRE_Int[ldof+1];
871 HYPRE_Int *j_offd =
new HYPRE_Int[nnz_offd];
872 double *d_offd =
new double[nnz_offd];
880 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
882 i_diag[0] = i_offd[0] = 0;
883 diag_counter = offd_counter = 0;
884 int offd_col_counter = 0;
885 for (
int i = 0; i < ldof; i++)
890 j_diag[diag_counter] = ltdofi;
891 d_diag[diag_counter++] = 1.0;
898 cmap_j_offd[offd_col_counter].two = offd_counter;
905 cmap_j_offd[offd_col_counter].two = offd_counter;
908 i_diag[i+1] = diag_counter;
909 i_offd[i+1] = offd_counter;
912 cmap_j_offd[offd_col_counter].two = offd_counter;
917 i_diag[i+1] = diag_counter;
918 i_offd[i+1] = offd_counter;
921 SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_col_counter);
923 for (
int i = 0; i < nnz_offd; i++)
929 for (
int i = 0; i < offd_col_counter; i++)
931 cmap[i] = cmap_j_offd[i].one;
932 j_offd[cmap_j_offd[i].two] = i;
935 for (
int i = 0; i < ldof; i++)
937 if (i_offd[i+1] == i_offd[i] + 1)
939 d_offd[i_offd[i]] = 1.0;
941 else if (i_offd[i+1] == i_offd[i] + 2)
945 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
946 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
948 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
949 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
950 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
954 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
955 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
956 offd_col_counter, cmap);
968 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
969 P_pc_col_starts, NULL,
true);
978 for (
int i = 0; i < ldof_group.
Size(); i++)
982 if (ldof_ltdof[i] >= 0)
1000 gc->
Create(pNURBSext()->ldof_group);
1004 GetGroupComm(*gc, 0);
1014 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1018 gcomm->
Bcast(ldof_marker);
1023 int component)
const
1046 const int *ess_dofs_data = ess_dofs.
HostRead();
1047 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1050 const int *ted = true_ess_dofs.
HostRead();
1051 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1053 if (
bool(ted[i]) != bool(true_ess_dofs2[i])) { counter++; }
1055 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1056 <<
", rank = " << MyRank);
1068 return ldof_ltdof[ldof];
1072 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1074 return ldof_ltdof[ldof];
1087 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1093 if (HYPRE_AssumedPartitionCheck())
1095 return ldof_ltdof[ldof] +
1096 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1100 return ldof_ltdof[ldof] +
1110 MFEM_ABORT(
"Not implemented for NC mesh.");
1113 if (HYPRE_AssumedPartitionCheck())
1117 return ldof_ltdof[sldof] +
1119 ldof_group[sldof])] /
vdim;
1123 return (ldof_ltdof[sldof*
vdim] +
1124 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1131 return ldof_ltdof[sldof] +
1133 ldof_group[sldof])] /
vdim;
1137 return (ldof_ltdof[sldof*
vdim] +
1138 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1145 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1150 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1157 if (Pconf) {
return Pconf; }
1188 if (Rconf) {
return Rconf; }
1231 if (num_face_nbrs == 0)
1237 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1238 MPI_Request *send_requests = requests;
1239 MPI_Request *recv_requests = requests + num_face_nbrs;
1240 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1246 Table send_nbr_elem_dof;
1253 for (
int fn = 0; fn < num_face_nbrs; fn++)
1258 for (
int i = 0; i < num_my_elems; i++)
1261 for (
int j = 0; j < ldofs.
Size(); j++)
1263 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1265 if (ldof_marker[ldof] != fn)
1267 ldof_marker[ldof] = fn;
1277 MyComm, &send_requests[fn]);
1280 MyComm, &recv_requests[fn]);
1283 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1288 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1296 int *send_I = send_nbr_elem_dof.
GetI();
1298 for (
int fn = 0; fn < num_face_nbrs; fn++)
1302 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1303 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1305 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1306 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1309 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1310 send_nbr_elem_dof.
MakeJ();
1314 for (
int fn = 0; fn < num_face_nbrs; fn++)
1319 for (
int i = 0; i < num_my_elems; i++)
1322 for (
int j = 0; j < ldofs.
Size(); j++)
1324 int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
1326 if (ldof_marker[ldof] != fn)
1328 ldof_marker[ldof] = fn;
1333 send_el_off[fn] + i, ldofs, ldofs.
Size());
1340 int *send_J = send_nbr_elem_dof.
GetJ();
1341 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1345 int j_end = send_I[send_el_off[fn+1]];
1347 for (
int i = 0; i < num_ldofs; i++)
1349 int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
1350 ldof_marker[ldof] = i;
1353 for ( ; j < j_end; j++)
1355 int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
1356 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
1360 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1367 for (
int fn = 0; fn < num_face_nbrs; fn++)
1372 MPI_Isend(send_J + send_I[send_el_off[fn]],
1373 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1374 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1376 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1377 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1378 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1381 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1384 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1387 int j_end = recv_I[recv_el_off[fn+1]];
1389 for ( ; j < j_end; j++)
1402 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1406 for (
int fn = 0; fn < num_face_nbrs; fn++)
1413 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1417 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1420 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1421 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1428 for (
int fn = 0; fn < num_face_nbrs; fn++)
1433 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1434 MyComm, &send_requests[fn]);
1436 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
1437 MyComm, &recv_requests[fn]);
1440 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1444 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1458 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1478 if (
vdim == 1 || doftrans == NULL)
1495 int el1, el2, inf1, inf2;
1508 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
1510 for (
int j = 0; j < vdofs.
Size(); j++)
1512 const int ldof = vdofs[j];
1513 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
1525 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
1526 " does not support NURBS!");
1546 #if MFEM_HYPRE_VERSION <= 22200
1547 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
1548 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
1549 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
1558 void ParFiniteElementSpace::ConstructTrueDofs()
1565 GetGroupComm(*gcomm, 1, &ldof_sign);
1575 for (gr = 1; gr < group_ldof.
Size(); gr++)
1577 const int *ldofs = group_ldof.
GetRow(gr);
1578 const int nldofs = group_ldof.
RowSize(gr);
1579 for (i = 0; i < nldofs; i++)
1581 ldof_group[ldofs[i]] = gr;
1586 for (i = 0; i < nldofs; i++)
1588 ldof_ltdof[ldofs[i]] = -2;
1595 for (i = 0; i < n; i++)
1597 if (ldof_ltdof[i] == -1)
1599 ldof_ltdof[i] = ltdof_size++;
1605 gcomm->
Bcast(ldof_ltdof);
1608 void ParFiniteElementSpace::ConstructTrueNURBSDofs()
1611 GroupTopology > = pNURBSext()->
gtopo;
1612 gcomm =
new GroupCommunicator(gt);
1617 ldof_group.
MakeRef(pNURBSext()->ldof_group);
1621 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
1623 for (
int i = 0; i < n; i++)
1625 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
1629 gcomm->
Create(ldof_group);
1637 for (
int i = 0; i < n; i++)
1639 if (gt.IAmMaster(ldof_group[i]))
1641 ldof_ltdof[i] = ltdof_size;
1652 gcomm->
Bcast(ldof_ltdof);
1655 void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
1656 Array<int> &dofs)
const
1660 for (
int j = 0; j < nv; j++)
1662 dofs[j] =
ndofs + nv*
id.index + j;
1666 void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
1667 Array<int> &dofs)
const
1671 dofs.SetSize(2*nv + ne);
1676 for (
int i = 0; i < 2; i++)
1678 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1679 for (
int j = 0; j < nv; j++)
1681 dofs[i*nv + j] = k++;
1685 int k =
ndofs + ngvdofs + (edge_id.index - pncmesh->
GetNEdges())*ne;
1686 for (
int j = 0; j < ne; j++)
1688 dofs[2*nv + j] = k++;
1692 void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
1693 Array<int> &dofs)
const
1695 int nfv, V[4], E[4], Eo[4];
1702 int nf = (nfv == 3) ? nf_tri : nf_quad;
1704 dofs.SetSize(nfv*(nv + ne) + nf);
1707 for (
int i = 0; i < nfv; i++)
1710 int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
1711 for (
int j = 0; j < nv; j++)
1713 dofs[offset++] = first + j;
1717 for (
int i = 0; i < nfv; i++)
1720 int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
1721 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
1723 for (
int j = 0; j < ne; j++)
1725 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
1726 : (-1 - (first + (-1 - ind[j])));
1730 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
1731 int first =
ndofs + ngvdofs + ngedofs + nf_quad*ghost_face_index;
1733 for (
int j = 0; j < nf; j++)
1735 dofs[offset++] = first + j;
1739 void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
1740 Array<int> &dofs)
const
1745 case 0: GetGhostVertexDofs(
id, dofs);
break;
1746 case 1: GetGhostEdgeDofs(
id, dofs);
break;
1747 case 2: GetGhostFaceDofs(
id, dofs);
break;
1751 void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
1752 Array<int> &dofs)
const
1754 int ned, ghost, first;
1760 first = (index < ghost)
1762 :
ndofs + (index - ghost)*ned;
1768 first = (index < ghost)
1770 :
ndofs + ngvdofs + (index - ghost)*ned;
1789 first =
ndofs + ngvdofs + ngedofs + index*stride;
1795 for (
int i = 0; i < ned; i++)
1797 dofs[i] = first + i;
1801 int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof)
const
1813 return (index < ghost)
1815 :
ndofs + (index - ghost)*ned + edof;
1821 return (index < ghost)
1822 ?
nvdofs + index*ned + edof
1823 :
ndofs + ngvdofs + (index - ghost)*ned + edof;
1837 return ndofs + ngvdofs + ngedofs + index*stride + edof;
1842 static int bisect(
const int* array,
int size,
int value)
1844 const int* end = array + size;
1845 const int* pos = std::lower_bound(array, end, value);
1846 MFEM_VERIFY(pos != end,
"value not found");
1853 void ParFiniteElementSpace::UnpackDof(
int dof,
1854 int &entity,
int &index,
int &edof)
const
1856 MFEM_VERIFY(dof >= 0,
"");
1862 entity = 0, index = dof / nv, edof = dof % nv;
1869 entity = 1, index = dof / ne, edof = dof % ne;
1878 index = dof / nf, edof = dof % nf;
1883 MFEM_ASSERT(table.Size(),
"");
1884 int jpos = bisect(table.GetJ(), table.Size_of_connections(), dof);
1885 index = bisect(table.GetI(), table.Size(), jpos);
1886 edof = dof - table.GetRow(index)[0];
1891 MFEM_ABORT(
"Cannot unpack internal DOF");
1899 entity = 0, index = pncmesh->
GetNVertices() + dof / nv, edof = dof % nv;
1906 entity = 1, index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
1913 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
1917 MFEM_ABORT(
"Out of range DOF.");
1925 struct PMatrixElement
1931 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
1932 : column(col), stride(str), value(val) {}
1934 bool operator<(
const PMatrixElement &other)
const
1935 {
return column < other.column; }
1937 typedef std::vector<PMatrixElement> List;
1945 PMatrixElement::List elems;
1948 void AddRow(
const PMatrixRow &other,
double coef)
1950 elems.reserve(elems.size() + other.elems.size());
1951 for (
unsigned i = 0; i < other.elems.size(); i++)
1953 const PMatrixElement &oei = other.elems[i];
1955 PMatrixElement(oei.column, oei.stride, coef * oei.value));
1962 if (!elems.size()) {
return; }
1963 std::sort(elems.begin(), elems.end());
1966 for (
unsigned i = 1; i < elems.size(); i++)
1968 if (elems[j].column == elems[i].column)
1970 elems[j].value += elems[i].value;
1974 elems[++j] = elems[i];
1980 void write(std::ostream &os,
double sign)
const
1982 bin_io::write<int>(os, elems.size());
1983 for (
unsigned i = 0; i < elems.size(); i++)
1985 const PMatrixElement &e = elems[i];
1986 bin_io::write<HYPRE_BigInt>(os, e.column);
1987 bin_io::write<int>(os, e.stride);
1988 bin_io::write<double>(os, e.value * sign);
1992 void read(std::istream &is,
double sign)
1994 elems.resize(bin_io::read<int>(is));
1995 for (
unsigned i = 0; i < elems.size(); i++)
1997 PMatrixElement &e = elems[i];
1998 e.column = bin_io::read<HYPRE_BigInt>(is);
1999 e.stride = bin_io::read<int>(is);
2000 e.value = bin_io::read<double>(is) * sign;
2008 class NeighborRowMessage :
public VarMessage<314>
2011 typedef NCMesh::MeshId MeshId;
2016 int entity,
index, edof;
2020 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row)
2021 : entity(ent), index(idx), edof(edof), group(grp), row(row) {}
2023 RowInfo(
int ent,
int idx,
int edof, GroupId grp)
2024 : entity(ent), index(idx), edof(edof), group(grp) {}
2026 typedef std::vector<RowInfo> List;
2029 NeighborRowMessage() : pncmesh(NULL) {}
2031 void AddRow(
int entity,
int index,
int edof, GroupId group,
2032 const PMatrixRow &row)
2034 rows.push_back(RowInfo(entity, index, edof, group, row));
2037 const RowInfo::List& GetRows()
const {
return rows; }
2039 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2040 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
2042 typedef std::map<int, NeighborRowMessage> Map;
2048 const FiniteElementCollection* fec;
2050 virtual void Encode(
int rank);
2051 virtual void Decode(
int);
2055 void NeighborRowMessage::Encode(
int rank)
2057 std::ostringstream stream;
2059 Array<MeshId> ent_ids[3];
2060 Array<GroupId> group_ids[3];
2061 Array<int> row_idx[3];
2064 for (
unsigned i = 0; i < rows.size(); i++)
2066 const RowInfo &ri = rows[i];
2067 const MeshId &
id = pncmesh->GetNCList(ri.entity).LookUp(ri.index);
2068 ent_ids[ri.entity].Append(
id);
2069 row_idx[ri.entity].Append(i);
2070 group_ids[ri.entity].Append(ri.group);
2073 Array<GroupId> all_group_ids;
2074 all_group_ids.Reserve(rows.size());
2075 for (
int i = 0; i < 3; i++)
2077 all_group_ids.Append(group_ids[i]);
2080 pncmesh->AdjustMeshIds(ent_ids, rank);
2081 pncmesh->EncodeMeshIds(stream, ent_ids);
2082 pncmesh->EncodeGroups(stream, all_group_ids);
2085 for (
int ent = 0; ent < 3; ent++)
2087 const Array<MeshId> &ids = ent_ids[ent];
2088 for (
int i = 0; i < ids.Size(); i++)
2090 const MeshId &
id = ids[i];
2091 const RowInfo &ri = rows[row_idx[ent][i]];
2092 MFEM_ASSERT(ent == ri.entity,
"");
2094 #ifdef MFEM_DEBUG_PMATRIX
2095 mfem::out <<
"Rank " << pncmesh->MyRank <<
" sending to " << rank
2096 <<
": ent " << ri.entity <<
", index " << ri.index
2097 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
2098 << int(
id.local) <<
")" << std::endl;
2106 int eo = pncmesh->GetEdgeNCOrientation(
id);
2108 if ((edof = ind[edof]) < 0)
2115 bin_io::write<int>(stream, edof);
2116 ri.row.write(stream, s);
2121 stream.str().swap(data);
2124 void NeighborRowMessage::Decode(
int rank)
2126 std::istringstream stream(data);
2128 Array<MeshId> ent_ids[3];
2129 Array<GroupId> group_ids;
2132 pncmesh->DecodeMeshIds(stream, ent_ids);
2133 pncmesh->DecodeGroups(stream, group_ids);
2135 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
2136 MFEM_ASSERT(nrows == group_ids.Size(),
"");
2139 rows.reserve(nrows);
2142 for (
int ent = 0, gi = 0; ent < 3; ent++)
2144 const Array<MeshId> &ids = ent_ids[ent];
2145 for (
int i = 0; i < ids.Size(); i++)
2147 const MeshId &
id = ids[i];
2148 int edof = bin_io::read<int>(stream);
2151 const int *ind = NULL;
2154 int eo = pncmesh->GetEdgeNCOrientation(
id);
2160 int fo = pncmesh->GetFaceOrientation(
id.index);
2161 ind = fec->DofOrderForOrientation(geom, fo);
2165 if (ind && (edof = ind[edof]) < 0)
2171 rows.push_back(RowInfo(ent,
id.index, edof, group_ids[gi++]));
2172 rows.back().row.read(stream, s);
2174 #ifdef MFEM_DEBUG_PMATRIX
2175 mfem::out <<
"Rank " << pncmesh->MyRank <<
" receiving from " << rank
2176 <<
": ent " << rows.back().entity <<
", index "
2177 << rows.back().index <<
", edof " << rows.back().edof
2185 ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
2187 NeighborRowMessage::Map &send_msg)
const
2190 UnpackDof(dof, ent, idx, edof);
2193 for (
unsigned i = 0; i < group.size(); i++)
2195 int rank = group[i];
2198 NeighborRowMessage &msg = send_msg[rank];
2199 msg.AddRow(ent, idx, edof, group_id, row);
2200 msg.SetNCMesh(pncmesh);
2202 #ifdef MFEM_PMATRIX_STATS
2209 void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
2210 GroupId group_sent_id, GroupId group_id,
2211 NeighborRowMessage::Map &send_msg)
const
2214 UnpackDof(dof, ent, idx, edof);
2217 for (
unsigned i = 0; i < group.size(); i++)
2219 int rank = group[i];
2220 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
2222 NeighborRowMessage &msg = send_msg[rank];
2223 GroupId invalid = -1;
2224 msg.AddRow(ent, idx, edof, invalid, row);
2225 msg.SetNCMesh(pncmesh);
2227 #ifdef MFEM_PMATRIX_STATS
2230 #ifdef MFEM_DEBUG_PMATRIX
2232 << rank <<
": ent " << ent <<
", index" << idx
2233 <<
", edof " << edof << std::endl;
2239 #ifdef MFEM_DEBUG_PMATRIX
2240 void ParFiniteElementSpace
2241 ::DebugDumpDOFs(std::ostream &os,
2242 const SparseMatrix &deps,
2243 const Array<GroupId> &dof_group,
2244 const Array<GroupId> &dof_owner,
2245 const Array<bool> &finalized)
const
2247 for (
int i = 0; i < dof_group.Size(); i++)
2253 UnpackDof(i, ent, idx, edof);
2255 os << edof <<
" @ ";
2256 if (i >
ndofs) { os <<
"ghost "; }
2259 case 0: os <<
"vertex ";
break;
2260 case 1: os <<
"edge ";
break;
2261 default: os <<
"face ";
break;
2265 if (i < deps.Height() && deps.RowSize(i))
2267 os <<
"depends on ";
2268 for (
int j = 0; j < deps.RowSize(i); j++)
2270 os << deps.GetRowColumns(i)[j] <<
" ("
2271 << deps.GetRowEntries(i)[j] <<
")";
2272 if (j < deps.RowSize(i)-1) { os <<
", "; }
2281 os <<
"group " << dof_group[i] <<
" (";
2283 for (
unsigned j = 0; j < g.size(); j++)
2285 if (j) { os <<
", "; }
2289 os <<
"), owner " << dof_owner[i] <<
" (rank "
2290 << pncmesh->
GetGroup(dof_owner[i])[0] <<
"); "
2291 << (finalized[i] ?
"finalized" :
"NOT finalized");
2302 int ParFiniteElementSpace
2303 ::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
2304 Array<HYPRE_BigInt> &dof_offs,
2305 Array<HYPRE_BigInt> &tdof_offs,
2306 Array<int> *dof_tdof,
2313 "Nedelec NC tets of order >= 2 are not supported yet.");
2317 #ifdef MFEM_PMATRIX_STATS
2318 n_msgs_sent = n_msgs_recv = 0;
2319 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
2324 int total_dofs =
ndofs + ngdofs;
2325 SparseMatrix deps(
ndofs, total_dofs);
2327 if (!dg && !partial)
2329 Array<int> master_dofs, slave_dofs;
2332 for (
int entity = 0; entity <= 2; entity++)
2334 const NCMesh::NCList &list = pncmesh->
GetNCList(entity);
2335 if (!list.masters.Size()) {
continue; }
2337 IsoparametricTransformation T;
2341 for (
int mi = 0; mi < list.masters.Size(); mi++)
2343 const NCMesh::Master &mf = list.
masters[mi];
2346 if (pncmesh->
IsGhost(entity, mf.index))
2348 GetGhostDofs(entity, mf, master_dofs);
2355 if (!master_dofs.Size()) {
continue; }
2358 if (!fe) {
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 const 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);
2402 for (
int entity = 0; entity <= 2; entity++)
2404 const NCMesh::NCList &list = pncmesh->
GetNCList(entity);
2407 { list.
conforming.Size(), list.masters.Size(), list.slaves.Size() };
2409 for (
int l = 0; l < 3; l++)
2411 for (
int i = 0; i < lsize[l]; i++)
2414 (l == 0) ? list.conforming[i] :
2415 (l == 1) ? (
const MeshId&) list.masters[i]
2416 : (
const MeshId&) list.slaves[i];
2418 if (
id.index < 0) {
continue; }
2423 GetBareDofs(entity,
id.index, dofs);
2425 for (
int j = 0; j < dofs.Size(); j++)
2428 dof_owner[dof] = owner;
2429 dof_group[dof] = group;
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;
2454 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
2458 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
2463 *R_ =
new SparseMatrix(num_true_dofs*
vdim, ndofs*vdim);
2467 dof_tdof->SetSize(ndofs*
vdim);
2471 std::vector<PMatrixRow> pmatrix(total_dofs);
2474 int vdim_factor = bynodes ? 1 :
vdim;
2475 int dof_stride = bynodes ? ndofs : 1;
2476 int tdof_stride = bynodes ? num_true_dofs : 1;
2479 std::list<NeighborRowMessage::Map> send_msg;
2480 send_msg.push_back(NeighborRowMessage::Map());
2483 for (
int dof = 0, tdof = 0; dof <
ndofs; dof++)
2487 pmatrix[dof].elems.push_back(
2488 PMatrixElement(my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.));
2491 if (dof_group[dof] != 0)
2493 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
2496 for (
int vd = 0; vd <
vdim; vd++)
2498 int vdof = dof*vdim_factor + vd*dof_stride;
2499 int vtdof = tdof*vdim_factor + vd*tdof_stride;
2501 if (R_) { (*R_)->Add(vtdof, vdof, 1.0); }
2502 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
2509 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2510 #ifdef MFEM_PMATRIX_STATS
2511 n_msgs_sent += send_msg.back().size();
2514 if (R_) { (*R_)->Finalize(); }
2519 NeighborRowMessage recv_msg;
2520 recv_msg.SetNCMesh(pncmesh);
2521 recv_msg.SetFEC(
fec);
2523 int num_finalized = num_true_dofs;
2525 buffer.elems.reserve(1024);
2527 while (num_finalized < ndofs)
2530 if (send_msg.back().size())
2532 send_msg.push_back(NeighborRowMessage::Map());
2537 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2539 recv_msg.Recv(rank, size, MyComm);
2540 #ifdef MFEM_PMATRIX_STATS
2542 n_rows_recv += recv_msg.GetRows().size();
2545 const NeighborRowMessage::RowInfo::List &rows = recv_msg.GetRows();
2546 for (
unsigned i = 0; i < rows.size(); i++)
2548 const NeighborRowMessage::RowInfo &ri = rows[i];
2549 int dof = PackDof(ri.entity, ri.index, ri.edof);
2550 pmatrix[dof] = ri.row;
2552 if (dof < ndofs && !finalized[dof]) { num_finalized++; }
2553 finalized[dof] =
true;
2555 if (ri.group >= 0 && dof_group[dof] != ri.group)
2558 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
2568 for (
int dof = 0; dof <
ndofs; dof++)
2570 if (finalized[dof]) {
continue; }
2572 bool owned = (dof_owner[dof] == 0);
2573 bool shared = (dof_group[dof] != 0);
2577 const int* dep_col = deps.GetRowColumns(dof);
2578 const double* dep_coef = deps.GetRowEntries(dof);
2579 int num_dep = deps.RowSize(dof);
2582 buffer.elems.clear();
2583 for (
int j = 0; j < num_dep; j++)
2585 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
2588 pmatrix[dof] = buffer;
2590 finalized[dof] =
true;
2597 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
2604 #ifdef MFEM_DEBUG_PMATRIX
2617 NeighborRowMessage::IsendAll(send_msg.back(), MyComm);
2618 #ifdef MFEM_PMATRIX_STATS
2619 n_msgs_sent += send_msg.back().size();
2625 *P_ = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
2626 dof_offs, tdof_offs);
2632 while (NeighborRowMessage::IProbe(rank, size, MyComm))
2634 recv_msg.RecvDrop(rank, size, MyComm);
2638 for (std::list<NeighborRowMessage::Map>::iterator
2639 it = send_msg.begin(); it != send_msg.end(); ++it)
2641 NeighborRowMessage::WaitAllSent(*it);
2644 #ifdef MFEM_PMATRIX_STATS
2645 int n_rounds = send_msg.size();
2646 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
2647 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
2649 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
2650 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2651 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2652 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
2653 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
2654 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
2658 mfem::out <<
"P matrix stats (avg per rank): "
2659 << double(glob_rounds)/NRanks <<
" rounds, "
2660 << double(glob_msgs_sent)/NRanks <<
" msgs sent, "
2661 << double(glob_msgs_recv)/NRanks <<
" msgs recv, "
2662 << double(glob_rows_sent)/NRanks <<
" rows sent, "
2663 << double(glob_rows_recv)/NRanks <<
" rows recv, "
2664 << double(glob_rows_fwd)/NRanks <<
" rows forwarded."
2669 return num_true_dofs*
vdim;
2673 HypreParMatrix* ParFiniteElementSpace
2674 ::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
2675 int local_rows,
int local_cols,
2676 Array<HYPRE_BigInt> &row_starts,
2677 Array<HYPRE_BigInt> &col_starts)
const
2679 bool assumed = HYPRE_AssumedPartitionCheck();
2682 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
2683 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
2686 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
2687 std::map<HYPRE_BigInt, int> col_map;
2688 for (
int i = 0; i < local_rows; i++)
2690 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2692 const PMatrixElement &elem = rows[i].elems[j];
2694 if (col >= first_col && col < next_col)
2701 for (
int vd = 0; vd <
vdim; vd++)
2711 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(col_map.size());
2713 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
2715 cmap[offd_col] = it->first;
2716 it->second = offd_col++;
2719 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
2720 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
2722 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
2723 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
2725 double *A_diag = Memory<double>(nnz_diag);
2726 double *A_offd = Memory<double>(nnz_offd);
2728 int vdim1 = bynodes ? vdim : 1;
2729 int vdim2 = bynodes ? 1 :
vdim;
2730 int vdim_offset = bynodes ? local_cols : 1;
2733 nnz_diag = nnz_offd = 0;
2735 for (
int vd1 = 0; vd1 < vdim1; vd1++)
2737 for (
int i = 0; i < local_rows; i++)
2739 for (
int vd2 = 0; vd2 < vdim2; vd2++)
2741 I_diag[vrow] = nnz_diag;
2742 I_offd[vrow++] = nnz_offd;
2744 int vd = bynodes ? vd1 : vd2;
2745 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
2747 const PMatrixElement &elem = rows[i].elems[j];
2748 if (elem.column >= first_col && elem.column < next_col)
2750 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
2751 A_diag[nnz_diag++] = elem.value;
2755 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
2756 A_offd[nnz_offd++] = elem.value;
2762 MFEM_ASSERT(vrow == vdim*local_rows,
"");
2763 I_diag[vrow] = nnz_diag;
2764 I_offd[vrow] = nnz_offd;
2766 return new HypreParMatrix(MyComm,
2767 row_starts.Last(), col_starts.Last(),
2768 row_starts.GetData(), col_starts.GetData(),
2769 I_diag, J_diag, A_diag,
2770 I_offd, J_offd, A_offd,
2771 col_map.size(), cmap);
2774 template <
typename int_type>
2775 static int_type* make_i_array(
int nrows)
2777 int_type *I = Memory<int_type>(nrows+1);
2778 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
2782 template <
typename int_type>
2783 static int_type* make_j_array(int_type* I,
int nrows)
2786 for (
int i = 0; i < nrows; i++)
2788 if (I[i] >= 0) { nnz++; }
2790 int_type *J = Memory<int_type>(nnz);
2793 for (
int i = 0, k = 0; i <= nrows; i++)
2795 int_type col = I[i];
2797 if (col >= 0) { J[k++] = col; }
2803 ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
2804 const Table* old_elem_dof,
2805 const Table* old_elem_fos)
2807 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
2808 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
2809 "be called before ParFiniteElementSpace::RebalanceMatrix");
2811 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2812 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2815 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
2821 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
2822 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
2823 "Mesh::Rebalance was not called before "
2824 "ParFiniteElementSpace::RebalanceMatrix");
2827 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
2828 for (
int i = 0; i < pmesh->
GetNE(); i++)
2830 if (old_index[i] >= 0)
2832 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
2835 for (
int vd = 0; vd <
vdim; vd++)
2837 for (
int j = 0; j < dofs.Size(); j++)
2840 if (row < 0) { row = -1 - row; }
2842 int col =
DofToVDof(old_dofs[j], vd, old_ndofs);
2843 if (col < 0) { col = -1 - col; }
2850 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
2853 Array<int> new_elements;
2854 Array<long> old_remote_dofs;
2855 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
2858 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
2859 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
2862 const long* old_dofs = &old_remote_dofs[pos];
2863 pos += dofs.Size() *
vdim;
2865 for (
int vd = 0; vd <
vdim; vd++)
2867 for (
int j = 0; j < dofs.Size(); j++)
2870 if (row < 0) { row = -1 - row; }
2872 if (i_diag[row] == i_diag[row+1])
2874 i_offd[row] = old_dofs[j + vd * dofs.Size()];
2881 #ifndef HYPRE_MIXEDINT
2882 HYPRE_Int *i_offd_hi = i_offd;
2885 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
2886 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
2887 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
2891 int offd_cols = i_offd_hi[vsize];
2892 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
2893 for (
int i = 0; i < offd_cols; i++)
2895 cmap_offd[i].one = j_offd[i];
2896 cmap_offd[i].two = i;
2899 #ifndef HYPRE_MIXEDINT
2900 HYPRE_Int *j_offd_hi = j_offd;
2902 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
2903 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
2906 SortPairs<HYPRE_BigInt, int>(cmap_offd, offd_cols);
2909 for (
int i = 0; i < offd_cols; i++)
2911 cmap[i] = cmap_offd[i].one;
2912 j_offd_hi[cmap_offd[i].two] = i;
2916 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
2917 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
2922 struct DerefDofMessage
2924 std::vector<HYPRE_BigInt> dofs;
2925 MPI_Request request;
2929 ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
2930 const Table* old_elem_dof,
2931 const Table *old_elem_fos)
2933 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
2935 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
2936 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
2938 #if 0 // check no longer seems to work with NC tet refinement
2939 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
2940 "Previous space is not finer.");
2948 Mesh::GeometryList elem_geoms(*
mesh);
2950 Array<int> dofs, old_dofs, old_vdofs;
2953 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
2960 for (
int i = 0; i < elem_geoms.Size(); i++)
2966 const CoarseFineTransformations &dtrans =
2967 old_pncmesh->GetDerefinementTransforms();
2968 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
2970 std::map<int, DerefDofMessage> messages;
2972 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
2973 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2977 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
2979 const Embedding &emb = dtrans.embeddings[k];
2981 int fine_rank = old_ranks[k];
2982 int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
2983 : old_pncmesh->ElementRank(emb.parent);
2985 if (coarse_rank != MyRank && fine_rank == MyRank)
2987 old_elem_dof->GetRow(k, dofs);
2990 DerefDofMessage &msg = messages[k];
2991 msg.dofs.resize(dofs.Size());
2992 for (
int i = 0; i < dofs.Size(); i++)
2994 msg.dofs[i] = old_offset + dofs[i];
2997 MPI_Isend(&msg.dofs[0], msg.dofs.size(), HYPRE_MPI_BIG_INT,
2998 coarse_rank, 291, MyComm, &msg.request);
3000 else if (coarse_rank == MyRank && fine_rank != MyRank)
3002 MFEM_ASSERT(emb.parent >= 0,
"");
3005 DerefDofMessage &msg = messages[k];
3006 msg.dofs.resize(ldof[geom]*vdim);
3008 MPI_Irecv(&msg.dofs[0], ldof[geom]*vdim, HYPRE_MPI_BIG_INT,
3009 fine_rank, 291, MyComm, &msg.request);
3017 for (
int i = 0; i < elem_geoms.Size(); i++)
3023 SparseMatrix *diag =
new SparseMatrix(ndofs*vdim, old_ndofs*vdim);
3025 Array<char> mark(diag->Height());
3028 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3030 const Embedding &emb = dtrans.embeddings[k];
3031 if (emb.parent < 0) {
continue; }
3033 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3034 int fine_rank = old_ranks[k];
3036 if (coarse_rank == MyRank && fine_rank == MyRank)
3039 DenseMatrix &lR = localR[geom](emb.matrix);
3042 old_elem_dof->GetRow(k, old_dofs);
3044 for (
int vd = 0; vd <
vdim; vd++)
3046 old_dofs.Copy(old_vdofs);
3049 for (
int i = 0; i < lR.Height(); i++)
3051 if (!std::isfinite(lR(i, 0))) {
continue; }
3054 int m = (r >= 0) ? r : (-1 - r);
3059 diag->SetRow(r, old_vdofs, row);
3069 for (
auto it = messages.begin(); it != messages.end(); ++it)
3071 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
3075 SparseMatrix *offd =
new SparseMatrix(ndofs*vdim, 1);
3077 std::map<HYPRE_BigInt, int> col_map;
3078 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3080 const Embedding &emb = dtrans.embeddings[k];
3081 if (emb.parent < 0) {
continue; }
3083 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3084 int fine_rank = old_ranks[k];
3086 if (coarse_rank == MyRank && fine_rank != MyRank)
3089 DenseMatrix &lR = localR[geom](emb.matrix);
3093 DerefDofMessage &msg = messages[k];
3094 MFEM_ASSERT(msg.dofs.size(),
"");
3096 for (
int vd = 0; vd <
vdim; vd++)
3098 MFEM_ASSERT(ldof[geom],
"");
3101 for (
int i = 0; i < lR.Height(); i++)
3103 if (!std::isfinite(lR(i, 0))) {
continue; }
3106 int m = (r >= 0) ? r : (-1 - r);
3111 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
3112 for (
int j = 0; j < ldof[geom]; j++)
3114 if (row[j] == 0.0) {
continue; }
3115 int &lcol = col_map[remote_dofs[j]];
3116 if (!lcol) { lcol = col_map.size(); }
3117 offd->_Set_(m, lcol-1, row[j]);
3127 offd->SetWidth(col_map.size());
3130 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
3131 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
3133 cmap[it->second-1] = it->first;
3140 int width = offd->Width();
3141 Array<Pair<HYPRE_BigInt, int> > reorder(width);
3142 for (
int i = 0; i < width; i++)
3144 reorder[i].one = cmap[i];
3149 Array<int> reindex(width);
3150 for (
int i = 0; i < width; i++)
3152 reindex[reorder[i].two] = i;
3153 cmap[i] = reorder[i].one;
3156 int *J = offd->GetJ();
3157 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
3159 J[i] = reindex[J[i]];
3161 offd->SortColumnIndices();
3164 HypreParMatrix* new_R;
3165 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
3166 dof_offsets, old_dof_offsets, diag, offd, cmap,
3169 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
3174 void ParFiniteElementSpace::Destroy()
3185 delete Pconf; Pconf = NULL;
3186 delete Rconf; Rconf = NULL;
3187 delete R_transpose; R_transpose = NULL;
3190 delete gcomm; gcomm = NULL;
3199 void ParFiniteElementSpace::CopyProlongationAndRestriction(
3200 const FiniteElementSpace &fes,
const Array<int> *perm)
3204 MFEM_VERIFY(pfes != NULL,
"");
3205 MFEM_VERIFY(P == NULL,
"");
3206 MFEM_VERIFY(R == NULL,
"");
3208 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
3214 int n = perm->Size();
3215 perm_mat =
new SparseMatrix(n, fes.GetVSize());
3216 for (
int i=0; i<n; ++i)
3220 perm_mat->Set(i, j, s);
3222 perm_mat->Finalize();
3226 if (pfes->P != NULL)
3229 else { P =
new HypreParMatrix(*pfes->P); }
3232 else if (perm != NULL)
3238 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
3239 col_starts, perm_mat);
3242 if (pfes->R != NULL)
3244 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
3245 else { R =
new SparseMatrix(*pfes->R); }
3247 else if (perm != NULL)
3268 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
3279 false, Tgf.OwnsOperator(),
false));
3280 Tgf.SetOperatorOwner(
false);
3287 "Parallel variable order space not supported yet.");
3295 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
3296 "each mesh modification.");
3305 Table* old_elem_dof = NULL;
3306 Table* old_elem_fos = NULL;
3317 Swap(dof_offsets, old_dof_offsets);
3338 old_elem_fos, old_ndofs));
3341 old_elem_dof = NULL;
3342 old_elem_fos = NULL;
3354 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
3360 false,
false,
true));
3367 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
3375 delete old_elem_dof;
3376 delete old_elem_fos;
3380 void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
3383 MFEM_VERIFY(new_pmesh != NULL,
3384 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
3391 : gc(gc_), local(local_)
3396 for (
int g=1; g<group_ldof.
Size(); ++g)
3400 n_external += group_ldof.
RowSize(g);
3403 int tsize = lsize - n_external;
3409 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3427 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
3429 gc(pfes.GroupComm()),
3435 for (
int gr = 1; gr < group_ldof.Size(); gr++)
3454 for ( ; j < end; j++)
3460 for ( ; j <
Height(); j++)
3474 const double *xdata = x.
HostRead();
3478 const int in_layout = 2;
3485 gc.
BcastBegin(const_cast<double*>(xdata), in_layout);
3489 for (
int i = 0; i < m; i++)
3492 std::copy(xdata+j-i, xdata+end-i, ydata+j);
3495 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
3497 const int out_layout = 0;
3510 const double *xdata = x.
HostRead();
3520 for (
int i = 0; i < m; i++)
3523 std::copy(xdata+j, xdata+end, ydata+j-i);
3526 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
3528 const int out_layout = 2;
3538 mpi_gpu_aware(
Device::GetGPUAwareMPI())
3541 const int tdofs = R->
Height();
3542 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
3556 unique_ltdof.
Sort();
3559 for (
int i = 0; i < shared_ltdof.Size(); i++)
3561 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
3562 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
3587 int req_counter = 0;
3592 if (send_size > 0) { req_counter++; }
3596 if (recv_size > 0) { req_counter++; }
3598 requests =
new MPI_Request[req_counter];
3604 pfes.GetRestrictionMatrix(),
3607 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
3611 static void ExtractSubVector(
const Array<int> &indices,
3614 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
3615 auto y = vout.
Write();
3616 const auto x = vin.
Read();
3617 const auto I = indices.
Read();
3618 MFEM_FORALL(i, indices.
Size(), y[i] = x[I[i]];);
3632 static void SetSubVector(
const Array<int> &indices,
3635 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
3638 const auto x = vin.
Read();
3639 const auto I = indices.
Read();
3640 MFEM_FORALL(i, indices.
Size(), y[I[i]] = x[i];);
3663 int req_counter = 0;
3684 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3686 gtopo.
GetComm(), &requests[req_counter++]);
3693 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3695 gtopo.
GetComm(), &requests[req_counter++]);
3702 MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
3733 static void AddSubVector(
const Array<int> &unique_dst_indices,
3740 const auto x = src.
Read();
3741 const auto DST_I = unique_dst_indices.
Read();
3742 const auto SRC_O = unique_to_src_offsets.
Read();
3743 const auto SRC_I = unique_to_src_indices.
Read();
3744 MFEM_FORALL(i, unique_dst_indices.
Size(),
3746 const int dst_idx = DST_I[i];
3747 double sum = y[dst_idx];
3748 const int end = SRC_O[i+1];
3749 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
3765 int req_counter = 0;
3776 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3778 gtopo.
GetComm(), &requests[req_counter++]);
3785 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3787 gtopo.
GetComm(), &requests[req_counter++]);
3794 MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
Abstract class for all finite elements.
VDofTransformation VDoFTrans
Geometry::Type GetGeometryType() const
int GetNFaceNeighbors() const
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
void SendRebalanceDofs(int old_ndofs, const Table &old_element_dofs, long old_global_offset, FiniteElementSpace *space)
Use the communication pattern from last Rebalance() to send element DOFs.
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with ReduceBegin().
int Size() const
Return the logical size of the array.
void SetSubVector(const Array< int > &dofs, const double value)
Set the entries listed in dofs to the given value.
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root.
void GetNeighborLDofTable(Table &nbr_ldof) const
Dofs to be received from communication neighbors.
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1)
HYPRE_BigInt GetMyTDofOffset() const
int GetNDofs() const
Returns number of degrees of freedom.
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
std::vector< int > CommGroup
HYPRE_BigInt GetGlobalScalarTDofNumber(int sldof)
Operator that extracts Face degrees of freedom in parallel.
int DofToVDof(int dof, int vd, int ndofs=-1) const
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Memory< T > & GetMemory()
Return a reference to the Memory object used by the Array.
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T...
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
static void AddDependencies(SparseMatrix &deps, Array< int > &master_dofs, Array< int > &slave_dofs, DenseMatrix &I, int skipfirst=0)
const CommGroup & GetGroup(GroupId id) const
Return a list of ranks contained in the group of the given ID.
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
void GetNeighborLTDofTable(Table &nbr_ltdof) const
Dofs to be sent to communication neighbors.
Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
void BuildElementToDofTable() const
void AddColumnsInRow(int r, int ncol)
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const
Determine the boundary degrees of freedom.
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
virtual int GetContType() const =0
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
Tangential components of vector field.
void SetSize(int s)
Resize the vector to size s.
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.
int TrueVSize() const
Obsolete, kept for backward compatibility.
virtual DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'.
static int DecodeDof(int dof)
Helpers to remove encoded sign from a DOF.
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
int GetGroupSize(int g) const
Get the number of processors in a group.
virtual void Update(bool want_transform=true)
Pointer to an Operator of a specified type.
void SetDims(int rows, int nnz)
Operator::Type Type() const
Get the currently set operator type id.
int VDofToDof(int vdof) const
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
HYPRE_BigInt GlobalTrueVSize() const
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
int GetNVertices() const
Return the number of vertices in the NCMesh.
virtual DofTransformation * GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i'th boundary element.
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_vdofs, int component=-1) const
Mark degrees of freedom associated with boundary elements with the specified boundary attributes (mar...
virtual double * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
int vdim
Vector dimension (number of unknowns per degree of freedom).
int Size() const
Returns the size of the vector.
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes, defined on a coarse mesh, to this FE space, defined on a refined mesh.
int GetNE() const
Returns number of elements.
int GetEdgeDofs(int edge, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
void DivideByGroupSize(double *vec)
Scale a vector of true dofs.
void LoseData()
Call this if data has been stolen.
Abstract parallel finite element space.
bool operator<(const Pair< A, B > &p, const Pair< A, B > &q)
Comparison operator for class Pair, based on the first element only.
void Synchronize(Array< int > &ldof_marker) const
Given an integer array on the local degrees of freedom, perform a bitwise OR between the shared dofs...
void Lose_Dof_TrueDof_Matrix()
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
int GetLocalTDofNumber(int ldof) const
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
double * GetData() const
Returns the matrix data array.
const FiniteElementCollection * fec
Associated FE collection (not owned).
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
virtual int GetFaceDofs(int face, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
int GroupNQuadrilaterals(int group)
Geometry::Type GetElementBaseGeometry(int i) const
virtual void UseDevice(bool use_dev) const
Enable execution of Vector operations using the mfem::Device.
int Size_of_connections() const
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
void DeleteAll()
Delete the whole array.
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
void AddConnections(int r, const int *c, int nc)
void GroupTriangle(int group, int i, int &face, int &o)
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with BcastBegin().
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
virtual int DofForGeometry(Geometry::Type GeomType) const =0
virtual int GetTrueVSize() const
Return the number of local vector true dofs.
int GroupVertex(int group, int i)
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
int uni_fdof
of single face DOFs if all faces uniform; -1 otherwise
Geometry::Type GetGeomType() const
Returns the Geometry::Type of the reference element.
ID for class SparseMatrix.
HYPRE_BigInt GlobalVSize() const
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
int HasFaceDofs(Geometry::Type geom, int p) const
const FiniteElement * GetFaceNbrFE(int i) const
ID for the base class Operator, i.e. any type.
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i'th element.
Array< int > face_nbr_elements_offset
void ExchangeFaceNbrData()
double f(const Vector &xvec)
void write(std::ostream &os, T value)
Write 'value' to stream.
Array< DofTransformation * > DoFTrans
virtual const FiniteElement * GetFE(int i) const
static const int Dimension[NumGeom]
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
int Append(const T &el)
Append element 'el' to array, resize if necessary.
GroupId GetEntityGroupId(int entity, int index)
virtual double * Write(bool on_dev=true)
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), on_dev).
int GetNumNeighbors() const
Return the number of neighbors including the local processor.
Memory< int > & GetJMemory()
void mfem_error(const char *msg)
Function called when an error is encountered. Used by the macros MFEM_ABORT, MFEM_ASSERT, MFEM_VERIFY.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
MPI_Comm GetComm() const
Return the MPI communicator.
Operator that extracts Face degrees of freedom for NCMesh in parallel.
int GetNGhostEdges() const
void AddConnection(int r, int c)
void LoseData()
NULL-ifies the data.
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
const int * HostReadJ() const
virtual const Operator * GetProlongationMatrix() const
The returned Operator is owned by the FiniteElementSpace.
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
void CopyFrom(const U *src)
Copy from src into this array. Copies enough entries to fill the Capacity size of this array...
int GetMaxElementOrder() const
Return the maximum polynomial order.
const T * Read(bool on_dev=true) const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), on_dev).
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
HypreParMatrix * Transpose() const
Returns the transpose of *this.
T read(std::istream &is)
Read a value from the stream and return it.
HYPRE_BigInt GetMyDofOffset() const
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
int GroupNVertices(int group)
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
int FindSorted(const T &el) const
Do bisection search for 'el' in a sorted array; return -1 if not found.
int Size() const
Returns the number of TYPE I elements.
GroupTopology & GetGroupTopology()
Get a reference to the associated GroupTopology object.
HYPRE_BigInt * GetDofOffsets() const
int GetNFaces() const
Return the number of (2D) faces in the NCMesh.
virtual const double * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
virtual DofTransformation * GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i'th element.
Operator * Ptr() const
Access the underlying Operator pointer.
int GetEntityDofs(int entity, int index, Array< int > &dofs, Geometry::Type master_geom=Geometry::INVALID, int variant=0) const
Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
int FirstFaceDof(int face, int variant=0) const
virtual DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'.
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor...
void GetTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer GridFunction data from coarse_fes...
bool Finalized() const
Returns whether or not CSR format has been finalized.
void GroupQuadrilateral(int group, int i, int &face, int &o)
HYPRE_BigInt * GetTrueDofOffsets() const
int GroupNTriangles(int group)
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
int GetNGhostFaces() const
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
virtual const int * DofOrderForOrientation(Geometry::Type GeomType, int Or) const =0
Returns an array, say p, that maps a local permuted index i to a local base index: base_i = p[i]...
void Swap(Array< T > &, Array< T > &)
double p(const Vector &x, double t)
Memory< int > & GetIMemory()
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
int GetDof() const
Returns the number of degrees of freedom in the finite element.
int GetNeighborRank(int i) const
Return the MPI rank of neighbor 'i'.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
void AddAColumnInRow(int r)
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
GroupId GetEntityOwnerId(int entity, int index)
Return vertex/edge/face ('entity' == 0/1/2, resp.) owner.
ParFiniteElementSpace(const ParFiniteElementSpace &orig, ParMesh *pmesh=NULL, const FiniteElementCollection *fec=NULL)
Copy constructor: deep copy all data from orig except the ParMesh, the FiniteElementCollection, and some derived data.
void GetFaceNbrElementFaces(int i, Array< int > &fcs, Array< int > &cor) const
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
int GetNEdges() const
Return the number of edges in the NCMesh.
Linear2DFiniteElement TriangleFE
int height
Dimension of the output / number of rows in the matrix.
void BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root.
Operation GetLastOperation() const
Return type of last modification of the mesh.
bool Nonconforming() const
Array< HYPRE_BigInt > face_nbr_glob_dof_map
NURBSExtension * NURBSext
Optional NURBS mesh extension.
virtual const Operator * GetRestrictionOperator() const
virtual double * ReadWrite(bool on_dev=true)
Shortcut for mfem::ReadWrite(vec.GetMemory(), vec.Size(), on_dev).
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Table face_nbr_element_dof
Mesh * mesh
The mesh that FE space lives on (not owned).
GridFunction interpolation operator applicable after mesh refinement.
void PrintPartitionStats()
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
virtual const SparseMatrix * GetRestrictionMatrix() const
Get the R matrix which restricts a local dof vector to true dof vector.
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
virtual int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
General triple product operator x -> A*B*C*x, with ownership of the factors.
Array< MeshId > conforming
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) const
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
int index(int i, int j, int nx, int ny)
bool IsGhost(int entity, int index) const
Return true if the specified vertex/edge/face is a ghost.
const FiniteElement * GetFaceNbrFaceFE(int i) const
Operator that extracts Face degrees of freedom for NCMesh in parallel.
Geometry::Type GetBdrElementBaseGeometry(int i) const
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root.
virtual const Operator * GetRestrictionTransposeOperator() const
Return logical transpose of restriction matrix, but in non-assembled optimized matrix-free form...
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
Geometry::Type GetFaceGeometryType(int Face) const
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
void MakeRef(T *, int)
Make this Array a reference to a pointer.
void GroupEdge(int group, int i, int &edge, int &o)
BiLinear2DFiniteElement QuadrilateralFE
const int * HostReadI() const
Identity Operator I: x -> x.
ID for class HypreParMatrix.
bool UseDevice() const
Read the internal device flag.
static bool DofFinalizable(int dof, const Array< bool > &finalized, const SparseMatrix &deps)
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...
Biwise-OR of all device backends.
NURBSExtension * NURBSext
int GetNGhostVertices() const
Table send_face_nbr_elements
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
The MFEM Device class abstracts hardware devices such as GPUs, as well as programming models such as ...
Base class for operators that extracts Face degrees of freedom.
Wrapper for hypre's ParCSR matrix class.
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
Class for parallel meshes.
Abstract data type element.
int GetFaceNbrRank(int fn) const
Linear1DFiniteElement SegmentFE
virtual const double * Read(bool on_dev=true) const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), on_dev).
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
DofTransformation * GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
int GroupNEdges(int group)
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const