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 long ltdofs = ltdof_size;
198 long long min_ltdofs, max_ltdofs, sum_ltdofs;
200 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
201 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
202 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_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_LONG, i, 123, MyComm, &status);
229 MPI_Send(<dofs, 1, MPI_LONG_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)
1768 first = (
index < ghost)
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)
1821 return (
index < ghost)
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");
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];
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());
3030 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3032 const Embedding &emb = dtrans.embeddings[k];
3033 if (emb.parent < 0) {
continue; }
3035 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3036 int fine_rank = old_ranks[k];
3038 if (coarse_rank == MyRank && fine_rank == MyRank)
3041 DenseMatrix &lR = localR[geom](emb.matrix);
3044 old_elem_dof->GetRow(k, old_dofs);
3046 for (
int vd = 0; vd <
vdim; vd++)
3048 old_dofs.Copy(old_vdofs);
3051 for (
int i = 0; i < lR.Height(); i++)
3053 if (!std::isfinite(lR(i, 0))) {
continue; }
3056 int m = (r >= 0) ? r : (-1 - r);
3058 if (is_dg || !mark[m])
3061 diag->SetRow(r, old_vdofs, row);
3071 for (
auto it = messages.begin(); it != messages.end(); ++it)
3073 MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
3077 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
3079 std::map<HYPRE_BigInt, int> col_map;
3080 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
3082 const Embedding &emb = dtrans.embeddings[k];
3083 if (emb.parent < 0) {
continue; }
3085 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
3086 int fine_rank = old_ranks[k];
3088 if (coarse_rank == MyRank && fine_rank != MyRank)
3091 DenseMatrix &lR = localR[geom](emb.matrix);
3095 DerefDofMessage &msg = messages[k];
3096 MFEM_ASSERT(msg.dofs.size(),
"");
3098 for (
int vd = 0; vd <
vdim; vd++)
3100 MFEM_ASSERT(ldof[geom],
"");
3103 for (
int i = 0; i < lR.Height(); i++)
3105 if (!std::isfinite(lR(i, 0))) {
continue; }
3108 int m = (r >= 0) ? r : (-1 - r);
3110 if (is_dg || !mark[m])
3113 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
3114 for (
int j = 0; j < ldof[geom]; j++)
3116 if (row[j] == 0.0) {
continue; }
3117 int &lcol = col_map[remote_dofs[j]];
3118 if (!lcol) { lcol = col_map.size(); }
3119 offd->_Set_(m, lcol-1, row[j]);
3130 offd->SetWidth(col_map.size());
3133 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
3134 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
3136 cmap[it->second-1] = it->first;
3143 int width = offd->Width();
3144 Array<Pair<HYPRE_BigInt, int> > reorder(width);
3145 for (
int i = 0; i < width; i++)
3147 reorder[i].one = cmap[i];
3152 Array<int> reindex(width);
3153 for (
int i = 0; i < width; i++)
3155 reindex[reorder[i].two] = i;
3156 cmap[i] = reorder[i].one;
3159 int *J = offd->GetJ();
3160 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
3162 J[i] = reindex[J[i]];
3164 offd->SortColumnIndices();
3167 HypreParMatrix* new_R;
3168 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
3169 dof_offsets, old_dof_offsets, diag, offd, cmap,
3172 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
3177 void ParFiniteElementSpace::Destroy()
3188 delete Pconf; Pconf = NULL;
3189 delete Rconf; Rconf = NULL;
3190 delete R_transpose; R_transpose = NULL;
3193 delete gcomm; gcomm = NULL;
3202 void ParFiniteElementSpace::CopyProlongationAndRestriction(
3203 const FiniteElementSpace &fes,
const Array<int> *perm)
3207 MFEM_VERIFY(pfes != NULL,
"");
3208 MFEM_VERIFY(P == NULL,
"");
3209 MFEM_VERIFY(R == NULL,
"");
3212 pfes->Dof_TrueDof_Matrix();
3214 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
3220 int n = perm->Size();
3221 perm_mat =
new SparseMatrix(n, fes.GetVSize());
3222 for (
int i=0; i<n; ++i)
3226 perm_mat->Set(i, j,
s);
3228 perm_mat->Finalize();
3232 if (pfes->P != NULL)
3235 else { P =
new HypreParMatrix(*pfes->P); }
3238 else if (perm != NULL)
3244 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
3245 col_starts, perm_mat);
3248 if (pfes->R != NULL)
3250 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
3251 else { R =
new SparseMatrix(*pfes->R); }
3253 else if (perm != NULL)
3274 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
3285 false, Tgf.OwnsOperator(),
false));
3286 Tgf.SetOperatorOwner(
false);
3293 "Parallel variable order space not supported yet.");
3301 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after " 3302 "each mesh modification.");
3311 Table* old_elem_dof = NULL;
3312 Table* old_elem_fos = NULL;
3323 Swap(dof_offsets, old_dof_offsets);
3344 old_elem_fos, old_ndofs));
3347 old_elem_dof = NULL;
3348 old_elem_fos = NULL;
3360 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
3366 false,
false,
true));
3373 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
3381 delete old_elem_dof;
3382 delete old_elem_fos;
3386 void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
3389 MFEM_VERIFY(new_pmesh != NULL,
3390 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
3397 : gc(gc_), local(local_)
3402 for (
int g=1; g<group_ldof.
Size(); ++g)
3406 n_external += group_ldof.
RowSize(g);
3409 int tsize = lsize - n_external;
3415 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
3433 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
3435 gc(pfes.GroupComm()),
3441 for (
int gr = 1; gr < group_ldof.Size(); gr++)
3460 for ( ; j < end; j++)
3466 for ( ; j <
Height(); j++)
3480 const double *xdata = x.
HostRead();
3484 const int in_layout = 2;
3495 for (
int i = 0; i < m; i++)
3498 std::copy(xdata+j-i, xdata+end-i, ydata+j);
3501 std::copy(xdata+j-m, xdata+
Width(), ydata+j);
3503 const int out_layout = 0;
3516 const double *xdata = x.
HostRead();
3526 for (
int i = 0; i < m; i++)
3529 std::copy(xdata+j, xdata+end, ydata+j-i);
3532 std::copy(xdata+j, xdata+
Height(), ydata+j-m);
3534 const int out_layout = 2;
3544 mpi_gpu_aware(
Device::GetGPUAwareMPI())
3547 const int tdofs = R->
Height();
3548 MFEM_ASSERT(tdofs == R->
HostReadI()[tdofs],
"");
3562 unique_ltdof.
Sort();
3565 for (
int i = 0; i < shared_ltdof.Size(); i++)
3567 shared_ltdof[i] = unique_ltdof.
FindSorted(shared_ltdof[i]);
3568 MFEM_ASSERT(shared_ltdof[i] != -1,
"internal error");
3593 int req_counter = 0;
3598 if (send_size > 0) { req_counter++; }
3602 if (recv_size > 0) { req_counter++; }
3604 requests =
new MPI_Request[req_counter];
3610 pfes.GetRestrictionMatrix(),
3613 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
3617 static void ExtractSubVector(
const Array<int> &indices,
3620 MFEM_ASSERT(indices.
Size() == vout.
Size(),
"incompatible sizes!");
3621 auto y = vout.
Write();
3622 const auto x = vin.
Read();
3623 const auto I = indices.
Read();
3624 MFEM_FORALL(i, indices.
Size(), y[i] = x[I[i]];);
3638 static void SetSubVector(
const Array<int> &indices,
3641 MFEM_ASSERT(indices.
Size() == vin.
Size(),
"incompatible sizes!");
3644 const auto x = vin.
Read();
3645 const auto I = indices.
Read();
3646 MFEM_FORALL(i, indices.
Size(), y[I[i]] = x[i];);
3669 int req_counter = 0;
3690 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3699 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3708 MPI_Waitall(req_counter,
requests, MPI_STATUSES_IGNORE);
3739 static void AddSubVector(
const Array<int> &unique_dst_indices,
3746 const auto x = src.
Read();
3747 const auto DST_I = unique_dst_indices.
Read();
3748 const auto SRC_O = unique_to_src_offsets.
Read();
3749 const auto SRC_I = unique_to_src_indices.
Read();
3750 MFEM_FORALL(i, unique_dst_indices.
Size(),
3752 const int dst_idx = DST_I[i];
3753 double sum = y[dst_idx];
3754 const int end = SRC_O[i+1];
3755 for (
int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
3771 int req_counter = 0;
3782 MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
3791 MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
3800 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
Operator that extracts Face degrees of freedom for H1 FiniteElementSpaces.
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.
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)
Helpers to remove encoded sign from a 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'.
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.
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
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()
virtual const Operator * GetRestrictionTransposeOperator() const
Return logical transpose of restriction matrix, but in non-assembled optimized matrix-free form...
void BuildElementToDofTable() const
double * GetData() const
Returns the matrix data array.
double f(const Vector &xvec)
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.
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 indexes of degrees of freedom in array dofs for i'th element.
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
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.
virtual const FaceRestriction * GetFaceRestriction(ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) const
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'.
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
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
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
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
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
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.