29#include <unordered_map>
30#include <unordered_set>
39 ParInit(pmesh ? pmesh : orig.pmesh);
55 f ?
f : global_fes->FEColl(),
56 global_fes->GetVDim(), global_fes->GetOrdering())
75 int dim,
int ordering)
85 if (globNURBSext == NULL) {
return NULL; }
86 const ParNURBSExtension *pNURBSext =
87 dynamic_cast<const ParNURBSExtension*
>(parNURBSext);
88 MFEM_ASSERT(pNURBSext,
"need a ParNURBSExtension");
90 NURBSExtension *tmp_globNURBSext =
new NURBSExtension(*globNURBSext);
92 return new ParNURBSExtension(tmp_globNURBSext, pNURBSext);
95void ParFiniteElementSpace::ParInit(ParMesh *pm)
118 MFEM_ASSERT(
own_ext,
"internal error");
120 ParNURBSExtension *pNe =
new ParNURBSExtension(
136void ParFiniteElementSpace::CommunicateGhostOrder()
141 "Variable-order space requires a nonconforming mesh.");
148 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
149 "each mesh modification.");
159 int global_orders_changed = 0;
161 MPI_Allreduce(&local_orders_changed, &global_orders_changed, 1, MPI_INT,
164 if ((global_orders_changed == 0 && !href) || NRanks == 1)
171 Array<ParNCMesh::VarOrderElemInfo> localOrders(
mesh->
GetNE());
174 ParNCMesh::VarOrderElemInfo order_i{(
unsigned int) i,
elem_order[i]};
175 localOrders[i] = order_i;
181void ParFiniteElementSpace::Construct()
185 ConstructTrueNURBSDofs();
186 GenerateGlobalOffsets();
191 GenerateGlobalOffsets();
204 ngedofs = ngfdofs = 0;
220 const int ghostEdge = pncmesh->
GetNEdges() + i;
222 for (
int var=0; var<nvar; ++var)
247 const int ghostFace = pncmesh->
GetNFaces() + i;
249 for (
int var=0; var<nvar; ++var)
267 ngdofs = ngvdofs + ngedofs + ngfdofs;
277 ltdof_size = BuildParallelConformingInterpolation(
278 &P, &R, dof_offsets, tdof_offsets, &ldof_ltdof,
false);
292 long long ltdofs = ltdof_size;
293 long long min_ltdofs, max_ltdofs, sum_ltdofs;
295 MPI_Reduce(<dofs, &min_ltdofs, 1, MPI_LONG_LONG, MPI_MIN, 0, MyComm);
296 MPI_Reduce(<dofs, &max_ltdofs, 1, MPI_LONG_LONG, MPI_MAX, 0, MyComm);
297 MPI_Reduce(<dofs, &sum_ltdofs, 1, MPI_LONG_LONG, MPI_SUM, 0, MyComm);
302 mfem::out <<
"True DOF partitioning: min " << min_ltdofs
303 <<
", avg " << std::fixed << std::setprecision(1) << avg
304 <<
", max " << max_ltdofs
305 <<
", (max-avg)/avg " << 100.0*(max_ltdofs - avg)/avg
313 mfem::out <<
"True DOFs by rank: " << ltdofs;
314 for (
int i = 1; i < NRanks; i++)
317 MPI_Recv(<dofs, 1, MPI_LONG_LONG, i, 123, MyComm, &status);
324 MPI_Send(<dofs, 1, MPI_LONG_LONG, 0, 123, MyComm);
329void ParFiniteElementSpace::GetGroupComm(
334 int nvd, ned, ntd = 0, nqd = 0;
337 int group_ldof_counter;
355 bool have_sign_flips =
false;
363 group_ldof_counter = 0;
364 for (gr = 1; gr < ng; gr++)
367 group_ldof_counter += ned * pmesh->
GroupNEdges(gr);
373 group_ldof_counter *=
vdim;
376 group_ldof.
SetDims(ng, group_ldof_counter);
379 group_ldof_counter = 0;
380 group_ldof.
GetI()[0] = group_ldof.
GetI()[1] = 0;
381 for (gr = 1; gr < ng; gr++)
383 int j, k, l, m, o, nv, ne, nt, nq;
394 for (j = 0; j < nv; j++)
400 for (l = 0; l < nvd; l++, m++)
410 for (l = 0; l < dofs.
Size(); l++)
412 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
420 for (j = 0; j < ne; j++)
427 for (l = 0; l < ned; l++)
434 (*g_ldof_sign)[dofs[l]] = -1;
435 have_sign_flips =
true;
440 dofs[l] = m + ind[l];
449 for (l = 0; l < dofs.
Size(); l++)
451 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
459 for (j = 0; j < nt; j++)
466 for (l = 0; l < ntd; l++)
473 (*g_ldof_sign)[dofs[l]] = -1;
474 have_sign_flips =
true;
479 dofs[l] = m + ind[l];
488 for (l = 0; l < dofs.
Size(); l++)
490 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
498 for (j = 0; j < nq; j++)
505 for (l = 0; l < nqd; l++)
512 (*g_ldof_sign)[dofs[l]] = -1;
513 have_sign_flips =
true;
518 dofs[l] = m + ind[l];
527 for (l = 0; l < dofs.
Size(); l++)
529 group_ldof.
GetJ()[group_ldof_counter++] = dofs[l];
534 group_ldof.
GetI()[gr+1] = group_ldof_counter;
537 if (g_ldof_sign && have_sign_flips ==
false)
545void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs)
const
550 for (
int i = 0; i < dofs.Size(); i++)
561 if (ldof_sign[dofs[i]] < 0)
569void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof)
const
571 Array<int> all_dofs(el_dof.GetJ(), el_dof.Size_of_connections());
572 ApplyLDofSigns(all_dofs);
580 for (
int i = 0; i <
ndofs; i++)
582 if (ldof_sign[i] < 0)
584 for (
int d = 0; d <
vdim; d++)
586 const int idx = byvdim ? d+
vdim*i : i+
ndofs*d;
587 h_data[idx] = -h_data[idx];
616 ApplyLDofSigns(dofs);
643 ApplyLDofSigns(dofs);
650 if (
face_dof !=
nullptr && variant == 0)
658 ApplyLDofSigns(dofs);
676 auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
677 auto itr =
L2F.find(key);
678 if (itr !=
L2F.end())
680 return itr->second.get();
684 std::unique_ptr<FaceRestriction> res;
711 return L2F.emplace(key, std::move(res)).first->second.get();
719 MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group),
"invalid edge index");
720 pmesh->
GroupEdge(group, ei, l_edge, ori);
730 for (
int i = 0; i < dofs.
Size(); i++)
732 const int di = dofs[i];
733 dofs[i] = di >= 0 ? rdofs[di] :
743 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNTriangles(group),
744 "invalid triangular face index");
755 for (
int i = 0; i < dofs.
Size(); i++)
757 const int di = dofs[i];
758 dofs[i] = di >= 0 ? rdofs[di] :
768 MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNQuadrilaterals(group),
769 "invalid quadrilateral face index");
780 for (
int i = 0; i < dofs.
Size(); i++)
782 const int di = dofs[i];
783 dofs[i] = (di >= 0) ? rdofs[di] :
789void ParFiniteElementSpace::GenerateGlobalOffsets()
const
801 if (HYPRE_AssumedPartitionCheck())
804 GroupTopology > = GetGroupTopo();
805 int nsize = gt.GetNumNeighbors()-1;
806 MPI_Request *requests =
new MPI_Request[2*nsize];
807 MPI_Status *statuses =
new MPI_Status[2*nsize];
808 tdof_nb_offsets.
SetSize(nsize+1);
809 tdof_nb_offsets[0] = tdof_offsets[0];
812 int request_counter = 0;
813 for (
int i = 1; i <= nsize; i++)
815 MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_BIG_INT,
816 gt.GetNeighborRank(i), 5365, MyComm,
817 &requests[request_counter++]);
819 for (
int i = 1; i <= nsize; i++)
821 MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_BIG_INT,
822 gt.GetNeighborRank(i), 5365, MyComm,
823 &requests[request_counter++]);
825 MPI_Waitall(request_counter, requests, statuses);
832void ParFiniteElementSpace::CheckNDSTriaDofs()
835 bool nd_basis =
dynamic_cast<const ND_FECollection*
>(
fec);
856 for (
int g = 1; g < ngrps; g++)
863 int loc_nd_strias = strias ? 1 : 0;
864 int glb_nd_strias = 0;
865 MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1, MPI_INT, MPI_SUM, MyComm);
866 nd_strias = glb_nd_strias > 0;
869void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const
881 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
882 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
885 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
886 HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
894 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
896 i_diag[0] = i_offd[0] = 0;
897 diag_counter = offd_counter = 0;
898 for (
int i = 0; i < ldof; i++)
903 j_diag[diag_counter++] = ltdof_i;
908 cmap_j_offd[offd_counter].two = offd_counter;
911 i_diag[i+1] = diag_counter;
912 i_offd[i+1] = offd_counter;
917 for (
int i = 0; i < offd_counter; i++)
919 cmap[i] = cmap_j_offd[i].one;
920 j_offd[cmap_j_offd[i].two] = i;
923 P =
new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
924 i_diag, j_diag, i_offd, j_offd,
936 MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
937 MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_BIG_INT, MPI_SUM, MyComm);
944 Array<int> ldsize(ldof); ldsize = 0;
945 Array<int> ltori(ldof); ltori = 0;
950 for (
int g = 1; g < ngrps; g++)
959 for (
int i=0; i<sdofs.Size(); i++)
961 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
962 if (ldsize[ind] == 0) { nnz_offd++; }
968 int face, ori, info1, info2;
972 for (
int i=0; i<3*
nedofs; i++)
974 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
975 if (ldsize[ind] == 0) { nnz_offd++; }
978 for (
int i=3*
nedofs; i<sdofs.Size(); i++)
980 if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
981 ldsize[sdofs[i]] = 2;
982 ltori[sdofs[i]] = info2 % 64;
988 for (
int i=0; i<sdofs.Size(); i++)
990 int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
991 if (ldsize[ind] == 0) { nnz_offd++; }
998 HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
999 HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
1000 real_t *d_diag = Memory<real_t>(ltdof);
1003 HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
1004 HYPRE_Int *j_offd = Memory<HYPRE_Int>(nnz_offd);
1005 real_t *d_offd = Memory<real_t>(nnz_offd);
1013 Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
1015 i_diag[0] = i_offd[0] = 0;
1016 diag_counter = offd_counter = 0;
1017 int offd_col_counter = 0;
1018 for (
int i = 0; i < ldof; i++)
1023 j_diag[diag_counter] = ltdofi;
1024 d_diag[diag_counter++] = 1.0;
1031 cmap_j_offd[offd_col_counter].two = offd_counter;
1038 cmap_j_offd[offd_col_counter].two = offd_counter;
1041 i_diag[i+1] = diag_counter;
1042 i_offd[i+1] = offd_counter;
1045 cmap_j_offd[offd_col_counter].two = offd_counter;
1050 i_diag[i+1] = diag_counter;
1051 i_offd[i+1] = offd_counter;
1056 for (
int i = 0; i < nnz_offd; i++)
1062 for (
int i = 0; i < offd_col_counter; i++)
1064 cmap[i] = cmap_j_offd[i].one;
1065 j_offd[cmap_j_offd[i].two] = i;
1068 for (
int i = 0; i < ldof; i++)
1070 if (i_offd[i+1] == i_offd[i] + 1)
1072 d_offd[i_offd[i]] = 1.0;
1074 else if (i_offd[i+1] == i_offd[i] + 2)
1078 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
1079 d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
1081 j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
1082 j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
1083 d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
1087 P =
new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
1088 i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
1089 offd_col_counter, cmap);
1101 BuildParallelConformingInterpolation(&P_pc, NULL, P_pc_row_starts,
1102 P_pc_col_starts, NULL,
true);
1111 for (
int i = 0; i < ldof_group.
Size(); i++)
1115 if (ldof_ltdof[i] >= 0)
1133 gc->
Create(pNURBSext()->ldof_group);
1137 GetGroupComm(*gc, 0);
1147 MFEM_VERIFY(ldof_marker.
Size() ==
GetVSize(),
"invalid in/out array");
1151 gcomm->
Bcast(ldof_marker);
1156 int component)
const
1168 int component)
const
1189 const int *ess_dofs_data = ess_dofs.
HostRead();
1190 Pt->BooleanMult(1, ess_dofs_data, 0, true_ess_dofs2);
1192 const int *ted = true_ess_dofs.
HostRead();
1193 std::string error_msg =
"failed dof: ";
1194 for (
int i = 0; i < true_ess_dofs.
Size(); i++)
1196 if (
bool(ted[i]) !=
bool(true_ess_dofs2[i]))
1198 error_msg += std::to_string(i) +=
"(R ";
1199 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1200 error_msg += std::to_string(
bool(true_ess_dofs2[i])) +=
") ";
1209 MFEM_ASSERT(R->
Width() == ess_dofs.
Size(),
"!");
1210 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1211 <<
", rank = " << MyRank <<
", " << error_msg);
1222 int component)
const
1225 "GetEssentialTrueDofsVar is only for variable-order spaces");
1231 const int ntdofs = tdof2ldof.
Size();
1233 vdim * ntdofs == true_ess_dofs.
Size(),
"");
1237 const int vdim_factor = bynodes ? 1 :
vdim;
1239 const int tdof_stride = bynodes ? num_true_dofs : 1;
1242 for (
int l=0; l<
ndofs; ++l)
1244 const int tdof = ldof_ltdof[l];
1245 if (tdof >= 0 && ess_dofs[l])
1247 for (
int vd = 0; vd <
vdim; vd++)
1249 if (component >= 0 && vd != component) {
continue; }
1250 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1251 true_ess_dofs[vtdof] = 1;
1257 std::set<int> edges, faces;
1261 for (
int tdof=0; tdof<ntdofs; ++tdof)
1264 if (!tdof2ldof[tdof].set) {
continue; }
1266 const bool edge = tdof2ldof[tdof].isEdge;
1267 const int index = tdof2ldof[tdof].idx;
1269 const bool bdry = edge ? edges.count(
index) > 0 : faces.count(
index) > 0;
1270 if (!bdry) {
continue; }
1272 for (
int vd = 0; vd <
vdim; vd++)
1274 if (component >= 0 && vd != component) {
continue; }
1275 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
1276 true_ess_dofs[vtdof] = 1;
1282 int component)
const
1301 "GetBoundaryLoopEdgeDofs does not support nonconforming meshes");
1303 "GetBoundaryLoopEdgeDofs requires 2D or 3D meshes to find 1D edge objects");
1309 &loc_edges, &loc_belems);
1311 std::unordered_set<int> boundary_edge_dofs;
1312 std::unordered_map<int, int> dof_to_edge_map;
1313 std::unordered_map<int, int> dof_to_boundary_element;
1314 boundary_edge_dofs.reserve(loc_dofs.
Size());
1315 dof_to_edge_map.reserve(loc_dofs.
Size());
1316 dof_to_boundary_element.reserve(loc_dofs.
Size());
1317 for (
int i = 0; i < loc_dofs.
Size(); i++)
1319 const int dof = loc_dofs[i];
1320 boundary_edge_dofs.insert(dof);
1321 dof_to_edge_map[dof] = loc_edges[i];
1322 dof_to_boundary_element[dof] = loc_belems[i];
1326 std::unordered_map<int, int> edge_to_group_size;
1329 int total_shared_edges = 0;
1330 for (
int group = 1; group < num_groups; group++)
1334 edge_to_group_size.reserve(total_shared_edges);
1336 for (
int group = 1; group < num_groups; group++)
1339 int num_edges_in_group = pmesh->
GroupNEdges(group);
1341 for (
int i = 0; i < num_edges_in_group; i++)
1343 edge_to_group_size.emplace(pmesh->
GroupEdge(group, i), group_size);
1353 std::unordered_map<int, int> boundary_element_to_companion;
1354 std::unordered_set<int> dofs_to_remove;
1361 for (
int boundary_element_idx : boundary_element_indices)
1363 int face_index, face_orientation;
1365 boundary_element_to_companion[boundary_element_idx] = face_index;
1368 std::vector<HYPRE_BigInt> local_data;
1369 local_data.reserve(boundary_edge_dofs.size() * 2);
1371 std::unordered_set<int> processed_edges;
1372 processed_edges.reserve(boundary_edge_dofs.size());
1374 for (
const auto& [dof, local_edge] : dof_to_edge_map)
1377 if (!processed_edges.insert(local_edge).second) {
continue; }
1380 auto it = edge_to_group_size.find(local_edge);
1381 if (it != edge_to_group_size.end() && it->second > 1)
1384 int boundary_element_idx = dof_to_boundary_element[dof];
1385 int companion_index = boundary_element_to_companion[boundary_element_idx];
1388 local_data.push_back(global_edge_indices[local_edge]);
1389 local_data.push_back(global_face_indices[companion_index]);
1395 int local_size = local_data.size();
1397 std::vector<int> mpi_arrays(num_procs * 4);
1398 int* all_sizes = mpi_arrays.data();
1399 int* displs = all_sizes + num_procs;
1400 int* byte_sizes = displs + num_procs;
1401 int* byte_displs = byte_sizes + num_procs;
1403 MPI_Allgather(&local_size, 1, MPI_INT, all_sizes, 1, MPI_INT, pmesh->
GetComm());
1407 for (
int i = 0; i < num_procs; i++)
1409 displs[i] = total_size;
1410 byte_displs[i] = total_size * hypre_size;
1411 total_size += all_sizes[i];
1412 byte_sizes[i] = all_sizes[i] * hypre_size;
1417 std::vector<HYPRE_BigInt> all_data(total_size);
1418 MPI_Allgatherv(local_data.data(), local_size * hypre_size, MPI_BYTE,
1419 all_data.data(), byte_sizes, byte_displs, MPI_BYTE, pmesh->
GetComm());
1422 std::unordered_map<HYPRE_BigInt, int> global_to_local_edge;
1423 global_to_local_edge.reserve(global_edge_indices.
Size());
1424 for (
int i = 0; i < global_edge_indices.
Size(); ++i)
1426 global_to_local_edge[global_edge_indices[i]] = i;
1430 std::unordered_map<HYPRE_BigInt, std::unordered_set<HYPRE_BigInt>>edge_to_faces;
1431 edge_to_faces.reserve(total_size / 2);
1433 for (
size_t i = 0; i < all_data.size(); i += 2)
1435 edge_to_faces[all_data[i]].insert(all_data[i + 1]);
1439 dofs_to_remove.reserve(local_data.size() / 4);
1441 for (
size_t i = 0; i < local_data.size(); i += 2)
1446 if (edge_to_faces[global_edge_id].size() >= 2)
1448 int local_edge = global_to_local_edge[global_edge_id];
1453 for (
int k = 0; k < local_edge_dofs.
Size(); ++k)
1455 int dof = local_edge_dofs[k];
1456 if (boundary_edge_dofs.count(dof))
1458 dofs_to_remove.insert(dof);
1478 boundary_dof_count = 0;
1479 for (
const int dof : boundary_edge_dofs)
1481 boundary_dof_count[dof] = 1;
1486 gcomm->
Bcast(boundary_dof_count);
1488 for (
const int dof : boundary_edge_dofs)
1490 if (boundary_dof_count[dof] % 2 == 0)
1492 dofs_to_remove.insert(dof);
1498 for (
int dof : dofs_to_remove)
1500 boundary_edge_dofs.erase(dof);
1501 dof_to_edge_map.erase(dof);
1502 dof_to_boundary_element.erase(dof);
1513 ess_edge_list->
Reserve(boundary_edge_dofs.size());
1519 local_ldof_marker = 0;
1521 for (
int dof : boundary_edge_dofs)
1523 local_ldof_marker[dof] = 1;
1540 std::unordered_map<int, int> shared_dof_to_edge;
1542 for (
int group = 1; group < num_groups; group++)
1544 const int num_edges_in_group = pmesh->
GroupNEdges(group);
1545 for (
int i = 0; i < num_edges_in_group; i++)
1547 const int edge = pmesh->
GroupEdge(group, i);
1549 for (
int k = 0; k < shared_edge_dofs.
Size(); k++)
1551 shared_dof_to_edge.emplace(shared_edge_dofs[k], edge);
1557 std::vector<std::pair<int, int>> tdof_edge_pairs;
1558 tdof_edge_pairs.reserve(boundary_edge_dofs.size());
1560 for (
int dof = 0; dof < local_ldof_marker.
Size(); dof++)
1562 if (!local_ldof_marker[dof]) {
continue; }
1565 if (tdof < 0) {
continue; }
1568 auto it = dof_to_edge_map.find(dof);
1569 if (it != dof_to_edge_map.end())
1575 auto shared_it = shared_dof_to_edge.find(dof);
1576 if (shared_it != shared_dof_to_edge.end())
1578 edge = shared_it->second;
1581 tdof_edge_pairs.push_back({tdof, edge});
1585 std::sort(tdof_edge_pairs.begin(), tdof_edge_pairs.end());
1588 for (
const auto& pair : tdof_edge_pairs)
1593 ess_edge_list->
Append(pair.second);
1599 std::vector<int> kept(boundary_edge_dofs.begin(), boundary_edge_dofs.end());
1600 std::sort(kept.begin(), kept.end());
1602 boundary_edge_dofs_out.
SetSize(0);
1603 boundary_edge_dofs_out.
Reserve(
static_cast<int>(kept.size()));
1607 dof_edges->
Reserve(
static_cast<int>(kept.size()));
1609 if (dof_boundary_elements)
1611 dof_boundary_elements->
SetSize(0);
1612 dof_boundary_elements->
Reserve(
static_cast<int>(kept.size()));
1614 for (
int dof : kept)
1616 boundary_edge_dofs_out.
Append(dof);
1617 if (dof_edges) { dof_edges->
Append(dof_to_edge_map[dof]); }
1618 if (dof_boundary_elements)
1620 dof_boundary_elements->
Append(dof_to_boundary_element[dof]);
1624 if (ldof_marker) { ldof_marker->
Swap(local_ldof_marker); }
1628 int component)
const
1640 const int *ext_dofs_data = ext_dofs.
HostRead();
1641 Pt->BooleanMult(1, ext_dofs_data, 0, true_ext_dofs2);
1643 const int *ted = true_ext_dofs.
HostRead();
1644 std::string error_msg =
"failed dof: ";
1645 for (
int i = 0; i < true_ext_dofs.
Size(); i++)
1647 if (
bool(ted[i]) !=
bool(true_ext_dofs2[i]))
1649 error_msg += std::to_string(i) +=
"(R ";
1650 error_msg += std::to_string(
bool(ted[i])) +=
" P^T ";
1651 error_msg += std::to_string(
bool(true_ext_dofs2[i])) +=
") ";
1657 MFEM_ASSERT(R->
Width() == ext_dofs.
Size(),
"!");
1658 MFEM_VERIFY(counter == 0,
"internal MFEM error: counter = " << counter
1659 <<
", rank = " << MyRank <<
", " << error_msg);
1671 return ldof_ltdof[ldof];
1675 if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
1677 return ldof_ltdof[ldof];
1690 MFEM_VERIFY(ldof_ltdof[ldof] >= 0,
"ldof " << ldof <<
" not a true DOF.");
1696 if (HYPRE_AssumedPartitionCheck())
1698 return ldof_ltdof[ldof] +
1699 tdof_nb_offsets[GetGroupTopo().
GetGroupMaster(ldof_group[ldof])];
1703 return ldof_ltdof[ldof] +
1713 MFEM_ABORT(
"Not implemented for NC mesh.");
1716 if (HYPRE_AssumedPartitionCheck())
1720 return ldof_ltdof[sldof] +
1722 ldof_group[sldof])] /
vdim;
1726 return (ldof_ltdof[sldof*
vdim] +
1727 tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
1734 return ldof_ltdof[sldof] +
1736 ldof_group[sldof])] /
vdim;
1740 return (ldof_ltdof[sldof*
vdim] +
1741 tdof_offsets[GetGroupTopo().GetGroupMasterRank(
1748 return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
1753 return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
1760 if (Pconf) {
return Pconf; }
1791 if (Rconf) {
return Rconf; }
1828 if (num_face_nbrs == 0)
1834 MPI_Request *requests =
new MPI_Request[2*num_face_nbrs];
1835 MPI_Request *send_requests = requests;
1836 MPI_Request *recv_requests = requests + num_face_nbrs;
1837 MPI_Status *statuses =
new MPI_Status[num_face_nbrs];
1843 Table send_nbr_elem_dof;
1850 for (
int fn = 0; fn < num_face_nbrs; fn++)
1855 for (
int i = 0; i < num_my_elems; i++)
1858 for (
int j = 0; j < ldofs.
Size(); j++)
1862 if (ldof_marker[ldof] != fn)
1864 ldof_marker[ldof] = fn;
1874 MyComm, &send_requests[fn]);
1877 MyComm, &recv_requests[fn]);
1880 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1885 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1893 int *send_I = send_nbr_elem_dof.
GetI();
1895 for (
int fn = 0; fn < num_face_nbrs; fn++)
1899 MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
1900 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1902 MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
1903 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1906 MPI_Waitall(num_face_nbrs, send_requests, statuses);
1907 send_nbr_elem_dof.
MakeJ();
1911 for (
int fn = 0; fn < num_face_nbrs; fn++)
1916 for (
int i = 0; i < num_my_elems; i++)
1919 for (
int j = 0; j < ldofs.
Size(); j++)
1923 if (ldof_marker[ldof] != fn)
1925 ldof_marker[ldof] = fn;
1930 send_el_off[fn] + i, ldofs, ldofs.
Size());
1937 int *send_J = send_nbr_elem_dof.
GetJ();
1938 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1942 int j_end = send_I[send_el_off[fn+1]];
1944 for (
int i = 0; i < num_ldofs; i++)
1947 ldof_marker[ldof] = i;
1950 for ( ; j < j_end; j++)
1953 send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
1958 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1965 for (
int fn = 0; fn < num_face_nbrs; fn++)
1970 MPI_Isend(send_J + send_I[send_el_off[fn]],
1971 send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
1972 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
1974 MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
1975 recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
1976 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
1979 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
1982 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
1985 int j_end = recv_I[recv_el_off[fn+1]];
1987 for ( ; j < j_end; j++)
2000 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2004 for (
int fn = 0; fn < num_face_nbrs; fn++)
2011 MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
2015 MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
2018 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2019 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2026 for (
int fn = 0; fn < num_face_nbrs; fn++)
2031 MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
2032 MyComm, &send_requests[fn]);
2034 MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_BIG_INT, nbr_rank, tag,
2035 MyComm, &recv_requests[fn]);
2038 MPI_Waitall(num_face_nbrs, recv_requests, statuses);
2042 for (
int fn = 0, j = 0; fn < num_face_nbrs; fn++)
2051 MPI_Waitall(num_face_nbrs, send_requests, statuses);
2088 int el1, el2, inf1, inf2;
2101 Ordering::DofsToVDofs<Ordering::byNODES>(nd/
vdim,
vdim, vdofs);
2103 for (
int j = 0; j < vdofs.
Size(); j++)
2105 const int ldof = vdofs[j];
2106 vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] :
2115 mfem_error(
"ParFiniteElementSpace::GetFaceNbrFE"
2116 " does not support NURBS!");
2125 const int ndofs_order = FE->
GetDof();
2126 if (ndofs_order ==
ndofs)
2130 else if (ndofs_order >
ndofs)
2132 MFEM_ABORT(
"Finite element order not found in GetFaceNbrFE");
2158#if MFEM_HYPRE_VERSION <= 22200
2159 hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
2160 hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
2161 hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
2170void ParFiniteElementSpace::ConstructTrueDofs()
2177 GetGroupComm(*gcomm, 1, &ldof_sign);
2187 for (gr = 1; gr < group_ldof.
Size(); gr++)
2189 const int *ldofs = group_ldof.
GetRow(gr);
2190 const int nldofs = group_ldof.
RowSize(gr);
2191 for (i = 0; i < nldofs; i++)
2193 ldof_group[ldofs[i]] = gr;
2198 for (i = 0; i < nldofs; i++)
2200 ldof_ltdof[ldofs[i]] = -2;
2207 for (i = 0; i < n; i++)
2209 if (ldof_ltdof[i] == -1)
2211 ldof_ltdof[i] = ltdof_size++;
2217 gcomm->
Bcast(ldof_ltdof);
2220void ParFiniteElementSpace::ConstructTrueNURBSDofs()
2223 GroupTopology > = pNURBSext()->
gtopo;
2224 gcomm =
new GroupCommunicator(gt);
2229 ldof_group.
MakeRef(pNURBSext()->ldof_group);
2233 const int *scalar_ldof_group = pNURBSext()->
ldof_group;
2235 for (
int i = 0; i < n; i++)
2237 ldof_group[i] = scalar_ldof_group[
VDofToDof(i)];
2241 gcomm->
Create(ldof_group);
2249 for (
int i = 0; i < n; i++)
2251 if (gt.IAmMaster(ldof_group[i]))
2253 ldof_ltdof[i] = ltdof_size;
2264 gcomm->
Bcast(ldof_ltdof);
2267void ParFiniteElementSpace::GetGhostVertexDofs(
const MeshId &
id,
2268 Array<int> &dofs)
const
2272 for (
int j = 0; j < nv; j++)
2274 dofs[j] =
ndofs + nv*
id.index + j;
2278static const char* msg_orders_changed =
2279 "Element orders changed, you need to Update() the space first.";
2281void ParFiniteElementSpace::GetGhostEdgeDofs(
const MeshId &edge_id,
2282 Array<int> &dofs,
int variant)
const
2286 int order, ne, base;
2289 const int edge = edge_id.index;
2292 base = beg[variant];
2293 ne = beg[variant+1] - base;
2304 base = (edge_id.index - pncmesh->
GetNEdges())*ne;
2309 dofs.SetSize(2*nv + ne);
2314 for (
int i = 0; i < 2; i++)
2316 int k = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
2317 for (
int j = 0; j < nv; j++)
2319 dofs[i*nv + j] = k++;
2323 int k =
ndofs + ngvdofs + base;
2324 for (
int j = 0; j < ne; j++)
2326 dofs[2*nv + j] = k++;
2330void ParFiniteElementSpace::GetGhostFaceDofs(
const MeshId &face_id,
2331 Array<int> &dofs)
const
2335 int nfv, V[4], E[4], Eo[4];
2342 int nf = (nfv == 3) ? nf_tri : nf_quad;
2344 const int ghost_face_index = face_id.index - pncmesh->
GetNFaces();
2346 Array<int> evar(nfv);
2351 const int face = face_id.index;
2353 constexpr int variant = 0;
2355 base = beg[variant];
2356 nf = beg[variant+1] - base;
2363 for (
int i = 0; i < nfv; i++)
2379 MFEM_VERIFY(eo == fo,
"Edge must have same order as face");
2382 const int ne_i = ebeg[evar[i] + 1] - ebeg[evar[i]];
2386 dofs.SetSize((nfv * nv) + allne + nf);
2390 base = nf_quad * ghost_face_index;
2394 dofs.SetSize(nfv*(nv + ne) + nf);
2398 for (
int i = 0; i < nfv; i++)
2401 const int first = (V[i] < ghost) ? V[i]*nv : (
ndofs + (V[i] - ghost)*nv);
2402 for (
int j = 0; j < nv; j++)
2404 dofs[offset++] = first + j;
2408 for (
int i = 0; i < nfv; i++)
2413 const int variant = evar[i];
2416 int ebase = beg[variant];
2417 ne = beg[variant+1] - ebase;
2419 MFEM_ASSERT(ebase ==
FindEdgeDof(E[i], ne),
"sanity check?");
2421 const int first = (E[i] < ghost) ?
nvdofs + ebase
2429 for (
int j = 0; j < ne; j++)
2431 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
2437 const int first = (E[i] < ghost) ?
nvdofs + E[i]*ne
2438 :
ndofs + ngvdofs + (E[i] - ghost)*ne;
2440 for (
int j = 0; j < ne; j++)
2442 dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
2448 const int first =
ndofs + ngvdofs + ngedofs + base;
2449 for (
int j = 0; j < nf; j++)
2451 dofs[offset++] = first + j;
2455void ParFiniteElementSpace::GetGhostDofs(
int entity,
const MeshId &
id,
2456 Array<int> &dofs,
int var)
const
2461 case 0: GetGhostVertexDofs(
id, dofs);
break;
2462 case 1: GetGhostEdgeDofs(
id, dofs, var);
break;
2463 case 2: GetGhostFaceDofs(
id, dofs);
break;
2467void ParFiniteElementSpace::GetBareDofsVar(
int entity,
int index,
2468 Array<int> &dofs)
const
2470 int ned, ghost, first;
2476 first = (
index < ghost)
2485 ned = rowNext[0] - row[0];
2486 first = (
index < ghost)
2502 first =
ndofs + ngvdofs + ngedofs + row0 -
nfdofs;
2509 for (
int i = 0; i < ned; i++)
2511 dofs[i] = first + i;
2515void ParFiniteElementSpace::GetBareDofs(
int entity,
int index,
2516 Array<int> &dofs)
const
2520 GetBareDofsVar(entity,
index, dofs);
2524 int ned, ghost, first;
2530 first = (
index < ghost)
2538 first = (
index < ghost)
2559 first =
ndofs + ngvdofs + ngedofs +
index*stride;
2565 for (
int i = 0; i < ned; i++)
2567 dofs[i] = first + i;
2571int ParFiniteElementSpace::PackDofVar(
int entity,
int index,
int edof,
2582 return (
index < ghost)
2591 const int d = row[var] + edof;
2615static int bisect(
const int* array,
int size,
int value)
2617 const int* end = array + size;
2618 const int* pos = std::upper_bound(array, end, value);
2619 MFEM_VERIFY(pos != array,
"value not found");
2622 MFEM_VERIFY(*(array+size - 1) == value,
"Last entry must be exact")
2624 return pos - array - 1;
2627void ParFiniteElementSpace::UnpackDofVar(
int dof,
int &entity,
int &
index,
2628 int &edof,
int &order)
const
2631 MFEM_ASSERT(dof >= 0,
"");
2637 entity = 0,
index = dof / nv, edof = dof % nv;
2644 index = var_edge_dofmap[dof].index;
2645 edof = var_edge_dofmap[dof].edof;
2650 const int edge =
index;
2652 for (
int v=0; v<nvar; ++v)
2656 if (edof < os + dofs)
2665 MFEM_ASSERT(order >= 0,
"");
2674 index = var_face_dofmap[dof].index;
2675 edof = var_face_dofmap[dof].edof;
2680 const int face =
index;
2683 for (
int v=0; v<nvar; ++v)
2687 if (edof < os + dofs)
2696 MFEM_ASSERT(order >= 0,
"");
2701 MFEM_ABORT(
"Cannot unpack internal DOF");
2718 edof = var_edge_dofmap[dof +
nedofs].edof;
2727 edof = var_face_dofmap[dof +
nfdofs].edof;
2730 MFEM_ABORT(
"Out of range DOF.");
2734int ParFiniteElementSpace::PackDof(
int entity,
int index,
int edof,
2739 return PackDofVar(entity,
index, edof, var);
2752 return (
index < ghost)
2760 return (
index < ghost)
2762 :
ndofs + ngvdofs + (
index - ghost)*ned + edof;
2776 return ndofs + ngvdofs + ngedofs +
index*stride + edof;
2784void ParFiniteElementSpace::UnpackDof(
int dof,
2785 int &entity,
int &
index,
2786 int &edof,
int &order)
const
2792 UnpackDofVar(dof, entity,
index, edof, order);
2796 MFEM_ASSERT(dof >= 0,
"");
2802 entity = 0,
index = dof / nv, edof = dof % nv;
2809 entity = 1,
index = dof / ne, edof = dof % ne;
2818 index = dof / nf, edof = dof % nf;
2824 MFEM_ASSERT(table.Size() > 0,
"");
2825 int jpos =
bisect(table.GetJ(), table.Size_of_connections(), dof);
2827 edof = dof - table.GetRow(
index)[0];
2832 MFEM_ABORT(
"Cannot unpack internal DOF");
2847 entity = 1,
index = pncmesh->
GetNEdges() + dof / ne, edof = dof % ne;
2854 index = pncmesh->
GetNFaces() + dof / stride, edof = dof % stride;
2858 MFEM_ABORT(
"Out of range DOF.");
2866struct PMatrixElement
2872 PMatrixElement(
HYPRE_BigInt col = 0,
int str = 0,
double val = 0)
2873 : column(col), stride(str), value(val) {}
2875 bool operator<(
const PMatrixElement &other)
const
2876 {
return column < other.column; }
2878 typedef std::vector<PMatrixElement> List;
2886 PMatrixElement::List elems;
2889 void AddRow(
const PMatrixRow &other,
real_t coef)
2891 elems.reserve(elems.size() + other.elems.size());
2892 for (
const PMatrixElement &oei : other.elems)
2894 elems.emplace_back(oei.column, oei.stride, coef * oei.value);
2901 if (!elems.size()) {
return; }
2902 std::sort(elems.begin(), elems.end());
2905 for (
unsigned i = 1; i < elems.size(); i++)
2907 if (elems[j].column == elems[i].column)
2909 elems[j].value += elems[i].value;
2913 elems[++j] = elems[i];
2919 void write(std::ostream &os,
real_t sign)
const
2922 for (
unsigned i = 0; i < elems.size(); i++)
2924 const PMatrixElement &e = elems[i];
2931 void read(std::istream &is,
real_t sign)
2934 for (
unsigned i = 0; i < elems.size(); i++)
2936 PMatrixElement &e = elems[i];
2944class NeighborOrderMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ORDER_VM>
2947 typedef NCMesh::MeshId MeshId;
2952 int entity,
index, order;
2955 OrderInfo(
int ent,
int idx,
int p, GroupId grp)
2956 : entity(ent),
index(idx), order(
p), group(grp) {}
2959 NeighborOrderMessage() : pncmesh(NULL) {}
2961 void AddOrder(
int ent,
int idx,
int p, GroupId grp)
2963 msgs.emplace_back(ent, idx,
p, grp);
2966 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
2968 const std::vector<OrderInfo>& GetMsgs()
const {
return msgs; }
2970 typedef std::map<int, NeighborOrderMessage> Map;
2973 std::vector<OrderInfo> msgs;
2978 void Encode(
int rank)
override;
2980 void Decode(
int rank)
override;
2983void NeighborOrderMessage::Encode(
int rank)
2985 std::ostringstream stream;
2987 Array<MeshId> ent_ids[3];
2988 Array<GroupId> group_ids[3];
2989 Array<int> row_idx[3];
2992 for (
unsigned i = 0; i < msgs.size(); i++)
2994 const OrderInfo &ri = msgs[i];
2996 ent_ids[ri.entity].Append(
id);
2997 row_idx[ri.entity].Append(i);
2998 group_ids[ri.entity].Append(ri.group);
3001 Array<GroupId> all_group_ids;
3002 all_group_ids.Reserve(msgs.size());
3003 for (
int i = 0; i < 3; i++)
3005 all_group_ids.Append(group_ids[i]);
3013 for (
int ent = 0; ent < 3; ent++)
3015 for (
int i = 0; i < ent_ids[ent].Size(); i++)
3017 const OrderInfo &ri = msgs[row_idx[ent][i]];
3018 MFEM_ASSERT(ent == ri.entity,
"");
3025 stream.str().swap(
data);
3028void NeighborOrderMessage::Decode(
int rank)
3030 std::istringstream stream(
data);
3032 Array<MeshId> ent_ids[3];
3033 Array<GroupId> group_ids;
3039 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
3040 MFEM_ASSERT(nrows == group_ids.Size(),
"");
3043 msgs.reserve(nrows);
3046 for (
int ent = 1, gi = 0; ent < 3; ent++)
3049 const Array<MeshId> &ids = ent_ids[ent];
3050 for (
int i = 0; i < ids.Size(); i++)
3052 const MeshId &
id = ids[i];
3058 msgs.emplace_back(ent,
id.
index, order_i, group_ids[gi++]);
3066class NeighborRowMessage :
public VarMessage<VarMessageTag::NEIGHBOR_ROW_VM>
3069 typedef NCMesh::MeshId MeshId;
3073 int entity,
index, edof, var;
3077 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
const PMatrixRow &row,
3079 : entity(ent),
index(idx), edof(edof), var(v), group(grp), row(row) {}
3081 RowInfo(
int ent,
int idx,
int edof, GroupId grp,
int v = 0)
3082 : entity(ent),
index(idx), edof(edof), var(v), group(grp) {}
3085 NeighborRowMessage() : pncmesh(NULL) {}
3087 void AddRow(
int entity,
int index,
int edof, GroupId group,
3088 const PMatrixRow &row,
int order)
3091 if (varOrder && entity == 1)
3097 MFEM_ASSERT(order_v >= 0,
"");
3098 if (order == order_v)
3112 else if (varOrder && entity == 2)
3118 MFEM_ASSERT(order_v >= 0,
"");
3119 if (order == order_v)
3135 rows.emplace_back(entity,
index, edof, group, row, var);
3138 const std::vector<RowInfo>& GetRows()
const {
return rows; }
3140 void SetNCMesh(ParNCMesh* pnc) { pncmesh = pnc; }
3141 void SetFEC(
const FiniteElementCollection* fec_) { this->fec = fec_; }
3142 void SetSpace(
const ParFiniteElementSpace* fes_)
3148 typedef std::map<int, NeighborRowMessage> Map;
3151 std::vector<RowInfo> rows;
3154 const FiniteElementCollection* fec;
3155 const ParFiniteElementSpace* fes;
3157 bool varOrder =
false;
3159 int GetEdgeVarOffset(
int edge,
int var);
3160 int GetFaceVarOffset(
int face,
int var);
3163 void Encode(
int rank)
override;
3165 void Decode(
int rank)
override;
3168void NeighborRowMessage::Encode(
int rank)
3170 std::ostringstream stream;
3172 Array<MeshId> ent_ids[3];
3173 Array<GroupId> group_ids[3];
3174 Array<int> row_idx[3];
3177 for (
unsigned i = 0; i < rows.size(); i++)
3179 const RowInfo &ri = rows[i];
3181 ent_ids[ri.entity].Append(
id);
3182 row_idx[ri.entity].Append(i);
3183 group_ids[ri.entity].Append(ri.group);
3186 Array<GroupId> all_group_ids;
3187 all_group_ids.Reserve(
static_cast<int>(rows.size()));
3188 for (
int i = 0; i < 3; i++)
3190 all_group_ids.Append(group_ids[i]);
3198 for (
int ent = 0; ent < 3; ent++)
3200 const Array<MeshId> &ids = ent_ids[ent];
3201 for (
int i = 0; i < ids.Size(); i++)
3203 const MeshId &
id = ids[i];
3204 const RowInfo &ri = rows[row_idx[ent][i]];
3205 MFEM_ASSERT(ent == ri.entity,
"");
3207#ifdef MFEM_DEBUG_PMATRIX
3209 <<
": ent " << ri.entity <<
", index " << ri.index
3210 <<
", edof " << ri.edof <<
" (id " <<
id.element <<
"/"
3211 << int(
id.local) <<
")" << std::endl;
3222 const int *ind =
nullptr;
3234 if (ind && (edof = ind[edof]) < 0)
3243 if (ent == 2 && varOrder)
3251 ri.row.write(stream, s);
3256 stream.str().swap(
data);
3259int NeighborRowMessage::GetEdgeVarOffset(
int edge,
int var)
3262 for (
int v=0; v<var; ++v)
3272int NeighborRowMessage::GetFaceVarOffset(
int face,
int var)
3276 for (
int v=0; v<var; ++v)
3279 const int dofs = fec->
GetNumDof(geom, fo);
3286void NeighborRowMessage::Decode(
int rank)
3288 std::istringstream stream(
data);
3290 Array<MeshId> ent_ids[3];
3291 Array<GroupId> group_ids;
3297 int nrows = ent_ids[0].Size() + ent_ids[1].Size() + ent_ids[2].Size();
3298 MFEM_ASSERT(nrows == group_ids.Size(),
"");
3301 rows.reserve(nrows);
3304 for (
int ent = 0, gi = 0; ent < 3; ent++)
3307 const Array<MeshId> &ids = ent_ids[ent];
3308 for (
int i = 0; i < ids.Size(); i++)
3310 const MeshId &
id = ids[i];
3314 MFEM_ASSERT(order_i >= 0,
"");
3321 const int *ind =
nullptr;
3343 if (order == order_i)
3358 RowInfo tmprow(1, 0, 0, 0);
3359 tmprow.row.read(stream, 1.0);
3377 "Only quadrilateral faces are supported in "
3378 "variable-order spaces");
3391 if (order == order_i)
3406 RowInfo tmprow(1, 0, 0, 0);
3407 tmprow.row.read(stream, 1.0);
3424 const bool process_dof_pairs = (ent == 2 &&
3428#ifdef MFEM_DEBUG_PMATRIX
3430 <<
": ent " << ent <<
", index " <<
id.index
3431 <<
", edof " << edof <<
" (id " <<
id.element <<
"/"
3432 << int(
id.local) <<
")" << std::endl;
3436 real_t s = (edof < 0) ? -1.0 : 1.0;
3438 if (ind && (edof = ind[edof]) < 0)
3448 rows.emplace_back(ent,
id.
index, edof, group_ids[gi++], var);
3449 rows.back().row.read(stream, s);
3451#ifdef MFEM_DEBUG_PMATRIX
3453 <<
": ent " << rows.back().entity <<
", index "
3454 << rows.back().index <<
", edof " << rows.back().edof
3458 if (process_dof_pairs)
3478 auto &first_row = rows.back().row;
3480 const auto initial_first_row = first_row;
3483 const MeshId &next_id = ids[++i];
3490 s = (edof < 0) ? -1.0 : 1.0;
3492 if (ind && (edof = ind[edof]) < 0)
3498 rows.emplace_back(ent, next_id.index, edof, group_ids[gi++]);
3499 rows.back().row.read(stream, s);
3500 auto &second_row = rows.back().row;
3503 const auto initial_second_row = second_row;
3521 MFEM_ASSERT(fo != 2 &&
3522 fo != 4,
"This code branch is ambiguous for face orientations 2 and 4."
3523 " Please report this mesh for further testing.\n");
3525 first_row.AddRow(initial_first_row, T[0] - 1.0);
3526 first_row.AddRow(initial_second_row, T[2]);
3527 second_row.AddRow(initial_first_row, T[1]);
3528 second_row.AddRow(initial_second_row, T[3] - 1.0);
3530 first_row.Collapse();
3531 second_row.Collapse();
3538ParFiniteElementSpace::ScheduleSendRow(
const PMatrixRow &row,
int dof,
3540 NeighborRowMessage::Map &send_msg)
const
3542 int ent, idx, edof, order;
3543 UnpackDof(dof, ent, idx, edof, order);
3545 for (
const auto &rank : pncmesh->GetGroup(group_id))
3549 NeighborRowMessage &msg = send_msg[rank];
3551 msg.AddRow(ent, idx, edof, group_id, row, order);
3552 msg.SetNCMesh(pncmesh);
3554#ifdef MFEM_PMATRIX_STATS
3561void ParFiniteElementSpace::ForwardRow(
const PMatrixRow &row,
int dof,
3562 GroupId group_sent_id, GroupId group_id,
3563 NeighborRowMessage::Map &send_msg)
const
3565 int ent, idx, edof, order;
3566 UnpackDof(dof, ent, idx, edof, order);
3569 for (
unsigned i = 0; i < group.size(); i++)
3571 int rank = group[i];
3572 if (rank != MyRank && !pncmesh->
GroupContains(group_sent_id, rank))
3574 NeighborRowMessage &msg = send_msg[rank];
3575 GroupId invalid = -1;
3577 msg.AddRow(ent, idx, edof, invalid, row, order);
3578 msg.SetNCMesh(pncmesh);
3580#ifdef MFEM_PMATRIX_STATS
3583#ifdef MFEM_DEBUG_PMATRIX
3585 << rank <<
": ent " << ent <<
", index" << idx
3586 <<
", edof " << edof << std::endl;
3592#ifdef MFEM_DEBUG_PMATRIX
3593void ParFiniteElementSpace
3594::DebugDumpDOFs(std::ostream &os,
3595 const SparseMatrix &deps,
3596 const Array<GroupId> &dof_group,
3597 const Array<GroupId> &dof_owner,
3598 const Array<bool> &finalized)
const
3600 for (
int i = 0; i < dof_group.Size(); i++)
3603 if (i < (nvdofs + nedofs + nfdofs) || i >= ndofs)
3606 UnpackDof(i, ent, idx, edof);
3608 os << edof <<
" @ ";
3609 if (i > ndofs) { os <<
"ghost "; }
3612 case 0: os <<
"vertex ";
break;
3613 case 1: os <<
"edge ";
break;
3614 default: os <<
"face ";
break;
3618 if (i < deps.Height() && deps.RowSize(i))
3620 os <<
"depends on ";
3621 for (
int j = 0; j < deps.RowSize(i); j++)
3623 os << deps.GetRowColumns(i)[j] <<
" ("
3624 << deps.GetRowEntries(i)[j] <<
")";
3625 if (j < deps.RowSize(i)-1) { os <<
", "; }
3634 os <<
"group " << dof_group[i] <<
" (";
3636 for (
unsigned j = 0; j < g.size(); j++)
3638 if (j) { os <<
", "; }
3642 os <<
"), owner " << dof_owner[i] <<
" (rank "
3643 << pncmesh->GetGroup(dof_owner[i])[0] <<
"); "
3644 << (finalized[i] ?
"finalized" :
"NOT finalized");
3655void ParFiniteElementSpace::ScheduleSendOrder(
3656 int ent,
int idx,
int order, GroupId group_id,
3657 NeighborOrderMessage::Map &send_msg)
const
3659 for (
const auto &rank : pncmesh->GetGroup(group_id))
3663 NeighborOrderMessage &msg = send_msg[rank];
3664 msg.AddOrder(ent, idx, order, group_id);
3665 msg.SetNCMesh(pncmesh);
3671 const std::set<int> &edges,
const std::set<int> &faces,
3675 bool changed = edges.size() > 0 || faces.size() > 0;
3679 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3685 NeighborOrderMessage::Map send_msg;
3688 for (
int entity = 1; entity <= 2; ++entity)
3690 const std::set<int> &indices = entity == 1 ? edges : faces;
3692 for (
auto idx : indices)
3698 ScheduleSendOrder(entity, idx,
MinOrder(orders[idx]),
3707 MPI_Barrier(MyComm);
3709 NeighborOrderMessage recv_msg;
3710 recv_msg.SetNCMesh(pncmesh);
3717 recv_msg.Recv(rank, size, MyComm);
3719 for (
const auto &ri : recv_msg.GetMsgs())
3725 edge_orders[ri.index] |= mask;
3726 if (edge_orders[ri.index] != initOrders)
3731 else if (ri.entity == 2)
3734 face_orders[ri.index] |= mask;
3735 if (face_orders[ri.index] != initOrders)
3742 MFEM_ABORT(
"Invalid entity type");
3751 recv_msg.RecvDrop(rank, size, MyComm);
3758 MPI_Allreduce(MPI_IN_PLACE, &
orders_changed, 1, MPI_INT, MPI_MAX, MyComm);
3767 MFEM_VERIFY(intermediate.
Size() ==
ndofs,
"");
3772 for (
int e=0; e<n; ++e)
3775 for (
int var = 1; var < nvar - 1; ++var)
3780 for (
auto dof : dofs)
3784 intermediate[dof] =
true;
3791void ParFiniteElementSpace::SetVarDofMap(
const Table & dofs,
3794 if (dofs.
Size() < 1)
3800 MFEM_ASSERT(dofs.
RowSize(dofs.
Size() - 1) == 1,
"");
3801 const int* rowLast = dofs.
GetRow(dofs.
Size() - 1);
3802 const int ndofs = rowLast[0];
3806 for (
int r = 0; r < dofs.
Size() - 1; ++r)
3808 const int* row = dofs.
GetRow(r);
3809 const int* row1 = dofs.
GetRow(r+1);
3811 for (
int d=row[0]; d<row1[0]; ++d)
3814 dmap[d].edof = d - row[0];
3819void ParFiniteElementSpace::SetTDOF2LDOFinfo(
int ntdofs,
int vdim_factor,
3820 int dof_stride,
int allnedofs)
3825 for (
int i=0; i<ntdofs; ++i)
3827 tdof2ldof[i].set =
false;
3833 for (
int entity = 1; entity < pmesh->
Dimension(); entity++)
3836 const int num_ent = (entity == 1) ? pmesh->
GetNEdges() :
3838 MFEM_ASSERT(ent_dofs.Size() >= num_ent+1,
"");
3840 for (
int idx = 0; idx < num_ent; idx++)
3842 if (ent_dofs.RowSize(idx) == 0) {
continue; }
3852 const int order0 =
GetEntityDofs(entity, idx, dofs, geom, 0);
3859 numVert = verts.Size();
3860 MFEM_VERIFY(numVert == 4,
"Only quadrilateral faces are supported");
3868 constexpr int vd = 0;
3869 for (
int i=idof0; i<dofs.Size(); ++i)
3871 const int dof_i = dofs[i];
3872 const int vdof_i = dof_i*vdim_factor + vd*dof_stride;
3873 const int tdof = ldof_ltdof[vdof_i];
3874 if (tdof < 0) {
continue; }
3876 MFEM_ASSERT(!tdof2ldof[tdof].set,
"");
3878 tdof2ldof[tdof].set =
true;
3879 tdof2ldof[tdof].minOrder = minOrder;
3880 tdof2ldof[tdof].isEdge = (entity == 1);
3881 tdof2ldof[tdof].idx = idx;
3887void ParFiniteElementSpace
3888::SetRestrictionMatrixEdgesFaces(
int vdim_factor,
int dof_stride,
3889 int tdof_stride,
const Array<int> &dof_tdof,
3890 const Array<HYPRE_BigInt> &dof_offs)
3892 MFEM_VERIFY(IsVariableOrder(),
"");
3894 const int ntdofs = tdof2ldof.Size();
3895 MFEM_VERIFY(vdim * ntdofs == R->NumRows(),
"");
3897 int prevEntity = -1;
3901 Array<int> ldofs, tdofs;
3904 for (
int tdof=0; tdof<ntdofs; ++tdof)
3906 if (!tdof2ldof[tdof].set) {
continue; }
3908 const int minOrder = tdof2ldof[tdof].minOrder;
3909 const bool edge = tdof2ldof[tdof].isEdge;
3910 const int index = tdof2ldof[tdof].idx;
3911 const int entity = edge ? 1 : 2;
3912 MFEM_ASSERT(!pncmesh->IsGhost(entity,
index),
3913 "True DOFs are not defined on ghost entities");
3915 if (entity != prevEntity ||
index != prevIndex)
3924 prevEntity = entity;
3932 const FiniteElement *feT = fec->FiniteElementForGeometry(geom);
3933 const FiniteElement *feL = fec->FiniteElementForGeometry(geom);
3938 tdofOrder = GetEdgeOrder(
index, 0);
3939 GetEdgeDofs(
index, tdofs, 0);
3940 for (
int var=0; ; ++var)
3942 const int order_var = GetEdgeOrder(
index, var);
3943 if (order_var == minOrder)
3945 GetEdgeDofs(
index, ldofs, var);
3952 tdofOrder = GetFaceOrder(
index, 0);
3953 GetFaceDofs(
index, tdofs, 0);
3954 for (
int var=0; ; ++var)
3956 const int order_var = GetFaceOrder(
index, var);
3957 if (order_var == minOrder)
3959 GetFaceDofs(
index, ldofs, var);
3965 MFEM_VERIFY(tdofs.Size() > 0 && ldofs.Size() > 0,
"");
3968 idof0 = GetNumBorderDofs(geom, tdofOrder);
3970 feT = fec->GetFE(geom, tdofOrder);
3971 feL = fec->GetFE(geom, minOrder);
3973 MFEM_VERIFY(feT && feL,
"");
3975 IsoparametricTransformation T;
3981 default: MFEM_ABORT(
"unsupported geometry");
3985 T.SetIdentityTransformation(geom);
3986 feT->GetTransferMatrix(*feL, T, I);
3989 for (
int ldi=0; ldi<ldofs.Size(); ++ldi)
3991 const real_t value = I(tdi + idof0, ldi);
3992 if (std::abs(value) > 1e-12)
3994 const int ldof = all2local[ldofs[ldi]];
3995 for (
int vd = 0; vd < vdim; vd++)
3997 const int vdof = ldof*vdim_factor + vd*dof_stride;
3998 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
3999 R->Add(vtdof, vdof, value);
4006int ParFiniteElementSpace
4007::BuildParallelConformingInterpolation(HypreParMatrix **P_, SparseMatrix **R_,
4008 Array<HYPRE_BigInt> &dof_offs,
4009 Array<HYPRE_BigInt> &tdof_offs,
4010 Array<int> *dof_tdof,
4013 const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
4014 const bool H1var = IsVariableOrderH1();
4016#ifdef MFEM_PMATRIX_STATS
4017 n_msgs_sent = n_msgs_recv = 0;
4018 n_rows_sent = n_rows_recv = n_rows_fwd = 0;
4023 const int total_dofs = ndofs + ngdofs;
4024 SparseMatrix deps(ndofs, total_dofs);
4026 if (!dg && !partial)
4028 VariableOrderMinimumRule(deps);
4030 Array<int> master_dofs, slave_dofs;
4033 for (
int entity = 0; entity <= 2; entity++)
4035 const NCMesh::NCList &list = pncmesh->GetNCList(entity);
4036 if (list.masters.Size() == 0) {
continue; }
4038 IsoparametricTransformation T;
4042 for (
const auto &mf : list.masters)
4045 if (entity == 1 && skip_edge.Size() > 0)
4047 if (skip_edge[mf.index])
4052 else if (entity == 2 && skip_face.Size() > 0)
4054 if (skip_face[mf.index])
4060 if (pncmesh->IsGhost(entity, mf.index))
4062 GetGhostDofs(entity, mf, master_dofs, 0);
4066 GetEntityDofs(entity, mf.index, master_dofs, mf.Geom(), 0);
4069 if (master_dofs.Size() == 0) {
continue; }
4071 const FiniteElement *fe = fec->FiniteElementForGeometry(mf.Geom());
4073 if (IsVariableOrder())
4076 if (entity == 1) { mfOrder = GetEdgeOrder(mf.index, 0); }
4077 else if (entity == 2) { mfOrder = GetFaceOrder(mf.index, 0); }
4079 if (entity != 0) { fe = fec->GetFE(mf.Geom(), mfOrder); }
4082 if (fe ==
nullptr) {
continue; }
4089 default: MFEM_ABORT(
"unsupported geometry");
4093 for (
int si = mf.slaves_begin; si < mf.slaves_end; si++)
4095 const NCMesh::Slave &sf = list.slaves[si];
4096 if (pncmesh->IsGhost(entity, sf.index)) {
continue; }
4098 constexpr int variant = 0;
4099 const int q = GetEntityDofs(entity, sf.index, slave_dofs, mf.Geom(), variant);
4100 if (q < 0) {
break; }
4102 list.OrientedPointMatrix(sf, T.GetPointMat());
4104 const auto *slave_fe = fec->GetFE(mf.Geom(), q);
4105 slave_fe->GetTransferMatrix(*fe, T, I);
4108 AddDependencies(deps, master_dofs, slave_dofs, I);
4118 Array<GroupId> dof_group(total_dofs);
4119 Array<GroupId> dof_owner(total_dofs);
4127 auto initialize_group_and_owner = [&dof_group, &dof_owner, &dofs,
4128 this](
int entity,
const MeshId &id)
4130 if (
id.
index < 0) {
return; }
4132 GroupId owner = pncmesh->GetEntityOwnerId(entity,
id.
index);
4133 GroupId group = pncmesh->GetEntityGroupId(entity,
id.
index);
4135 GetBareDofs(entity,
id.
index, dofs);
4137 for (
auto dof : dofs)
4139 dof_owner[dof] = owner;
4140 dof_group[dof] = group;
4145 for (
int entity : {0,1,2})
4147 for (
const auto &
id : pncmesh->GetNCList(entity).conforming)
4149 initialize_group_and_owner(entity,
id);
4151 for (
const auto &
id : pncmesh->GetNCList(entity).masters)
4153 initialize_group_and_owner(entity,
id);
4155 for (
const auto &
id : pncmesh->GetNCList(entity).slaves)
4157 initialize_group_and_owner(entity,
id);
4164 Array<bool> finalized(total_dofs);
4168 int num_true_dofs = 0;
4169 for (
int i = 0; i < ndofs; ++i)
4171 if (dof_owner[i] == 0 && deps.RowSize(i) == 0)
4174 finalized[i] =
true;
4178#ifdef MFEM_DEBUG_PMATRIX
4180 auto dof_diagnostics = [&](
int dof,
bool print_diagnostic)
4182 const auto &comm_group = pncmesh->GetGroup(dof_group[dof]);
4183 std::stringstream msg;
4184 msg << std::boolalpha;
4186 <<
" owner_rank " << pncmesh->GetGroup(dof_owner[dof])[0] <<
" CommGroup {";
4187 for (
const auto &x : comm_group)
4191 msg <<
"} finalized " << finalized[dof];
4197 deps.GetRow(dof, cols, row);
4198 msg <<
" deps cols {";
4199 for (
const auto &x : cols)
4206 int entity,
index, edof;
4207 UnpackDof(dof, entity,
index, edof);
4208 msg <<
" entity " << entity <<
" index " <<
index <<
" edof " << edof;
4215 HYPRE_BigInt loc_sizes[2] = { ndofs*vdim, num_true_dofs*vdim };
4216 Array<HYPRE_BigInt>* offsets[2] = { &dof_offs, &tdof_offs };
4217 pmesh->GenerateOffsets(2, loc_sizes, offsets);
4221 tdof_offs[HYPRE_AssumedPartitionCheck() ? 0 : MyRank];
4226 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
4230 dof_tdof->SetSize(ndofs*vdim);
4234 std::vector<PMatrixRow> pmatrix(total_dofs);
4237 const int vdim_factor = bynodes ? 1 : vdim;
4238 const int dof_stride = bynodes ? ndofs : 1;
4239 const int tdof_stride = bynodes ? num_true_dofs : 1;
4242 std::list<NeighborRowMessage::Map> send_msg;
4243 send_msg.emplace_back();
4246 for (
int dof = 0, tdof = 0; dof < ndofs; dof++)
4250 pmatrix[dof].elems.emplace_back(
4251 my_tdof_offset + vdim_factor*tdof, tdof_stride, 1.);
4254 if (dof_group[dof] != 0)
4256 MFEM_VERIFY(!send_msg.empty(),
"");
4257 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof], send_msg.back());
4260 for (
int vd = 0; vd < vdim; vd++)
4262 const int vdof = dof*vdim_factor + vd*dof_stride;
4263 const int vtdof = tdof*vdim_factor + vd*tdof_stride;
4265 if (R_ && !H1var) { (*R_)->Add(vtdof, vdof, 1.0); }
4266 if (dof_tdof) { (*dof_tdof)[vdof] = vtdof; }
4273 MFEM_VERIFY(!send_msg.empty(),
"");
4275#ifdef MFEM_PMATRIX_STATS
4276 n_msgs_sent += send_msg.back().size();
4279 if (R_ && !H1var) { (*R_)->Finalize(); }
4284 NeighborRowMessage recv_msg;
4285 recv_msg.SetNCMesh(pncmesh);
4286 recv_msg.SetSpace(
this);
4287 recv_msg.SetFEC(fec);
4289 int num_finalized = num_true_dofs;
4291 buffer.elems.reserve(1024);
4297 Array<bool> intermediate(ndofs);
4298 intermediate =
false;
4300 MarkIntermediateEntityDofs(1, intermediate);
4301 MarkIntermediateEntityDofs(2, intermediate);
4303 while (num_finalized < ndofs)
4306 MFEM_VERIFY(!send_msg.empty(),
"");
4307 if (send_msg.back().size())
4309 send_msg.emplace_back();
4317 recv_msg.Recv(rank, size, MyComm);
4319#ifdef MFEM_PMATRIX_STATS
4321 n_rows_recv += recv_msg.GetRows().size();
4324 for (
const auto &ri : recv_msg.GetRows())
4326 const int dof = PackDof(ri.entity, ri.index, ri.edof, ri.var);
4327 pmatrix[dof] = ri.row;
4329 if (dof < ndofs && !finalized[dof]) { ++num_finalized; }
4330 finalized[dof] =
true;
4332 if (ri.group >= 0 && dof_group[dof] != ri.group)
4335 MFEM_VERIFY(!send_msg.empty(),
"");
4336 ForwardRow(ri.row, dof, ri.group, dof_group[dof], send_msg.back());
4346 for (
int dof = 0; dof < ndofs; dof++)
4348 const bool owned = (dof_owner[dof] == 0);
4350 && (owned || intermediate[dof])
4351 && DofFinalizable(dof, finalized, deps))
4353 const int* dep_col = deps.GetRowColumns(dof);
4354 const real_t* dep_coef = deps.GetRowEntries(dof);
4357 buffer.elems.clear();
4358 for (
int j = 0; j < deps.RowSize(dof); j++)
4360 buffer.AddRow(pmatrix[dep_col[j]], dep_coef[j]);
4363 pmatrix[dof] = buffer;
4365 finalized[dof] =
true;
4370 const bool shared = (dof_group[dof] != 0);
4373 MFEM_VERIFY(!send_msg.empty(),
"");
4374 ScheduleSendRow(pmatrix[dof], dof, dof_group[dof],
4381#ifdef MFEM_DEBUG_PMATRIX
4382 static int dump = 0;
4386 snprintf(fname, 100,
"dofs%02d.txt", MyRank);
4387 std::ofstream
f(fname);
4388 DebugDumpDOFs(
f, deps, dof_group, dof_owner, finalized);
4394 MFEM_VERIFY(!send_msg.empty(),
"");
4396#ifdef MFEM_PMATRIX_STATS
4397 n_msgs_sent += send_msg.back().size();
4403 const int allnedofs = nedofs;
4406 SetVarOrderLocalDofs();
4408 const int ldof_stride = bynodes ? ndofs : 1;
4413 Array<HYPRE_BigInt>* offsets[1] = { &dof_offs };
4414 pmesh->GenerateOffsets(1, loc_sizes, offsets);
4419 std::vector<PMatrixRow> pmatrix_new(ndofs);
4422 bool validMap =
true;
4423 for (
int i=0; i<all2local.Size(); ++i)
4425 if (all2local[i] >= 0)
4427 if (all2local[i] - dofnew != 1)
4432 dofnew = all2local[i];
4434 pmatrix_new[all2local[i]] = pmatrix[i];
4437 MFEM_VERIFY(validMap && dofnew == ndofs - 1,
"");
4442 *P_ = MakeVDimHypreMatrix(pmatrix_new, ndofs, num_true_dofs,
4443 dof_offs, tdof_offs);
4448 MFEM_VERIFY(R_ && nedofs == lnedofs,
"");
4450 *R_ =
new SparseMatrix(num_true_dofs*vdim, ndofs*vdim);
4454 for (
int dof = 0; dof < nvdofs; dof++)
4456 for (
int vd = 0; vd < vdim; vd++)
4458 const int valldof = dof*vdim_factor + vd*dof_stride;
4459 const int vdof = dof*vdim_factor + vd*ldof_stride;
4460 const int vtdof = (*dof_tdof)[valldof];
4461 if (vtdof >= 0) { (*R_)->Add(vtdof, vdof, 1.0); }
4467 SetTDOF2LDOFinfo(num_true_dofs, vdim_factor, dof_stride, allnedofs);
4469 Array<HYPRE_BigInt> all_dof_offs(NRanks);
4470 MPI_Allgather(&dof_offs[0], 1, HYPRE_MPI_BIG_INT, all_dof_offs.GetData(),
4471 1, HYPRE_MPI_BIG_INT, MyComm);
4473 SetRestrictionMatrixEdgesFaces(vdim_factor, ldof_stride, tdof_stride,
4474 *dof_tdof, all_dof_offs);
4480 const int nalldofs = dof_tdof->Size() / vdim;
4481 MFEM_VERIFY(nalldofs * vdim == dof_tdof->Size(),
"");
4482 for (
int edof=0; edof<nbdofs; ++edof)
4484 const int dof = ndofs - nbdofs + edof;
4485 const int alldof = nalldofs - nbdofs + edof;
4486 for (
int vd = 0; vd < vdim; vd++)
4488 const int valldof = alldof*vdim_factor + vd*dof_stride;
4489 const int vdof = dof*vdim_factor + vd*ldof_stride;
4490 const int vtdof = (*dof_tdof)[valldof];
4491 (*R_)->Add(vtdof, vdof, 1.0);
4496 for (
int tdof=0; tdof<num_true_dofs; ++tdof)
4498 if ((*R_)->RowSize(tdof) == 0)
4500 MFEM_ABORT(
"Empty row of R");
4507 Array<int> dof_tdof_new(ndofs * vdim);
4508 for (
int i=0; i<all2local.Size(); ++i)
4510 if (all2local[i] >= 0)
4512 for (
int vd = 0; vd < vdim; vd++)
4514 const int vdof = i*vdim_factor + vd*dof_stride;
4515 const int ldof = all2local[i]*vdim_factor + vd*ldof_stride;
4516 dof_tdof_new[ldof] = (*dof_tdof)[vdof];
4521 Swap(dof_tdof_new, *dof_tdof);
4526 MFEM_VERIFY(var_edge_dofs.Size() - 1 == pncmesh->GetNEdges() +
4527 pncmesh->GetNGhostEdges(),
"");
4528 MFEM_VERIFY(var_face_dofs.Size() == -1 ||
4529 var_face_dofs.Size() - 1 == pncmesh->GetNFaces() + pncmesh->GetNGhostFaces(),
4532 ghost_edge_orders.SetSize(pncmesh->GetNGhostEdges());
4533 ghost_face_orders.SetSize(pncmesh->GetNGhostFaces());
4535 for (
int i=0; i<pncmesh->GetNGhostEdges(); ++i)
4537 ghost_edge_orders[i] = GetEdgeOrder(pncmesh->GetNEdges() + i);
4540 if (pmesh->Dimension() > 2)
4542 for (
int i=0; i<pncmesh->GetNGhostFaces(); ++i)
4544 ghost_face_orders[i] = GetFaceOrder(pncmesh->GetNFaces() + i);
4549 var_edge_dofs.Swap(loc_var_edge_dofs);
4550 loc_var_edge_dofs.Clear();
4552 var_face_dofs.Swap(loc_var_face_dofs);
4553 loc_var_face_dofs.Clear();
4555 Swap(var_edge_orders, loc_var_edge_orders);
4556 Swap(var_face_orders, loc_var_face_orders);
4558 loc_var_edge_orders.SetSize(0);
4559 loc_var_face_orders.SetSize(0);
4563 *P_ = MakeVDimHypreMatrix(pmatrix, ndofs, num_true_dofs,
4564 dof_offs, tdof_offs);
4572 recv_msg.RecvDrop(rank, size, MyComm);
4576 for (
auto &msg : send_msg)
4581#ifdef MFEM_PMATRIX_STATS
4582 int n_rounds = send_msg.size();
4583 int glob_rounds, glob_msgs_sent, glob_msgs_recv;
4584 int glob_rows_sent, glob_rows_recv, glob_rows_fwd;
4586 MPI_Reduce(&n_rounds, &glob_rounds, 1, MPI_INT, MPI_SUM, 0, MyComm);
4587 MPI_Reduce(&n_msgs_sent, &glob_msgs_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4588 MPI_Reduce(&n_msgs_recv, &glob_msgs_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4589 MPI_Reduce(&n_rows_sent, &glob_rows_sent, 1, MPI_INT, MPI_SUM, 0, MyComm);
4590 MPI_Reduce(&n_rows_recv, &glob_rows_recv, 1, MPI_INT, MPI_SUM, 0, MyComm);
4591 MPI_Reduce(&n_rows_fwd, &glob_rows_fwd, 1, MPI_INT, MPI_SUM, 0, MyComm);
4595 mfem::out <<
"P matrix stats (avg per rank): "
4596 <<
real_t(glob_rounds)/NRanks <<
" rounds, "
4597 <<
real_t(glob_msgs_sent)/NRanks <<
" msgs sent, "
4598 <<
real_t(glob_msgs_recv)/NRanks <<
" msgs recv, "
4599 <<
real_t(glob_rows_sent)/NRanks <<
" rows sent, "
4600 <<
real_t(glob_rows_recv)/NRanks <<
" rows recv, "
4601 <<
real_t(glob_rows_fwd)/NRanks <<
" rows forwarded."
4606 return num_true_dofs*vdim;
4609HypreParMatrix* ParFiniteElementSpace
4610::MakeVDimHypreMatrix(
const std::vector<PMatrixRow> &rows,
4611 int local_rows,
int local_cols,
4612 Array<HYPRE_BigInt> &row_starts,
4613 Array<HYPRE_BigInt> &col_starts)
const
4615 bool assumed = HYPRE_AssumedPartitionCheck();
4618 HYPRE_BigInt first_col = col_starts[assumed ? 0 : MyRank];
4619 HYPRE_BigInt next_col = col_starts[assumed ? 1 : MyRank+1];
4622 HYPRE_Int nnz_diag = 0, nnz_offd = 0;
4623 std::map<HYPRE_BigInt, int> col_map;
4624 for (
int i = 0; i < local_rows; i++)
4626 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4628 const PMatrixElement &elem = rows[i].elems[j];
4630 if (col >= first_col && col < next_col)
4637 for (
int vd = 0; vd < vdim; vd++)
4647 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(
static_cast<int>(col_map.size()));
4649 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
4651 cmap[offd_col] = it->first;
4652 it->second = offd_col++;
4655 HYPRE_Int *I_diag = Memory<HYPRE_Int>(vdim*local_rows + 1);
4656 HYPRE_Int *I_offd = Memory<HYPRE_Int>(vdim*local_rows + 1);
4658 HYPRE_Int *J_diag = Memory<HYPRE_Int>(nnz_diag);
4659 HYPRE_Int *J_offd = Memory<HYPRE_Int>(nnz_offd);
4661 real_t *A_diag = Memory<real_t>(nnz_diag);
4662 real_t *A_offd = Memory<real_t>(nnz_offd);
4664 int vdim1 = bynodes ? vdim : 1;
4665 int vdim2 = bynodes ? 1 : vdim;
4666 int vdim_offset = bynodes ? local_cols : 1;
4669 nnz_diag = nnz_offd = 0;
4671 for (
int vd1 = 0; vd1 < vdim1; vd1++)
4673 for (
int i = 0; i < local_rows; i++)
4675 for (
int vd2 = 0; vd2 < vdim2; vd2++)
4677 I_diag[vrow] = nnz_diag;
4678 I_offd[vrow++] = nnz_offd;
4680 int vd = bynodes ? vd1 : vd2;
4681 for (
unsigned j = 0; j < rows[i].elems.size(); j++)
4683 const PMatrixElement &elem = rows[i].elems[j];
4684 if (elem.column >= first_col && elem.column < next_col)
4686 J_diag[nnz_diag] = elem.column + vd*vdim_offset - first_col;
4687 A_diag[nnz_diag++] = elem.value;
4691 J_offd[nnz_offd] = col_map[elem.column + vd*elem.stride];
4692 A_offd[nnz_offd++] = elem.value;
4698 MFEM_ASSERT(vrow == vdim*local_rows,
"");
4699 I_diag[vrow] = nnz_diag;
4700 I_offd[vrow] = nnz_offd;
4702 return new HypreParMatrix(MyComm,
4703 row_starts.Last(), col_starts.Last(),
4704 row_starts.GetData(), col_starts.GetData(),
4705 I_diag, J_diag, A_diag,
4706 I_offd, J_offd, A_offd,
4707 static_cast<HYPRE_Int
>(col_map.size()), cmap);
4710template <
typename int_type>
4711static int_type* make_i_array(
int nrows)
4713 int_type *I = Memory<int_type>(nrows+1);
4714 for (
int i = 0; i <= nrows; i++) { I[i] = -1; }
4718template <
typename int_type>
4719static int_type* make_j_array(int_type* I,
int nrows)
4722 for (
int i = 0; i < nrows; i++)
4724 if (I[i] >= 0) { nnz++; }
4726 int_type *J = Memory<int_type>(nnz);
4729 for (
int i = 0, k = 0; i <= nrows; i++)
4731 int_type col = I[i];
4733 if (col >= 0) { J[k++] = col; }
4739ParFiniteElementSpace::RebalanceMatrix(
int old_ndofs,
4740 const Table* old_elem_dof,
4741 const Table* old_elem_fos)
4743 MFEM_VERIFY(
Nonconforming(),
"Only supported for nonconforming meshes.");
4744 MFEM_VERIFY(old_dof_offsets.
Size(),
"ParFiniteElementSpace::Update needs to "
4745 "be called before ParFiniteElementSpace::RebalanceMatrix");
4747 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4748 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4751 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4757 const Array<int> &old_index = old_pncmesh->GetRebalanceOldIndex();
4758 MFEM_VERIFY(old_index.Size() == pmesh->
GetNE(),
4759 "Mesh::Rebalance was not called before "
4760 "ParFiniteElementSpace::RebalanceMatrix");
4763 HYPRE_Int* i_diag = make_i_array<HYPRE_Int>(vsize);
4764 for (
int i = 0; i < pmesh->
GetNE(); i++)
4766 if (old_index[i] >= 0)
4768 const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
4771 for (
int vd = 0; vd <
vdim; vd++)
4773 for (
int j = 0; j < dofs.Size(); j++)
4783 HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
4786 Array<int> new_elements;
4787 Array<long> old_remote_dofs;
4788 old_pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
4791 HYPRE_BigInt* i_offd = make_i_array<HYPRE_BigInt>(vsize);
4792 for (
int i = 0, pos = 0; i < new_elements.Size(); i++)
4795 const long* old_dofs = &old_remote_dofs[pos];
4796 pos += dofs.Size() *
vdim;
4798 for (
int vd = 0; vd <
vdim; vd++)
4800 for (
int j = 0; j < dofs.Size(); j++)
4803 if (i_diag[row] == i_diag[row+1])
4805 i_offd[row] = old_dofs[j + vd * dofs.Size()];
4812#ifndef HYPRE_MIXEDINT
4813 HYPRE_Int *i_offd_hi = i_offd;
4816 HYPRE_Int *i_offd_hi = Memory<HYPRE_Int>(vsize + 1);
4817 std::copy(i_offd, i_offd + vsize + 1, i_offd_hi);
4818 Memory<HYPRE_BigInt>(i_offd, vsize + 1,
true).Delete();
4822 int offd_cols = i_offd_hi[vsize];
4823 Array<Pair<HYPRE_BigInt, int> > cmap_offd(offd_cols);
4824 for (
int i = 0; i < offd_cols; i++)
4826 cmap_offd[i].one = j_offd[i];
4827 cmap_offd[i].two = i;
4830#ifndef HYPRE_MIXEDINT
4831 HYPRE_Int *j_offd_hi = j_offd;
4833 HYPRE_Int *j_offd_hi = Memory<HYPRE_Int>(offd_cols);
4834 Memory<HYPRE_BigInt>(j_offd, offd_cols,
true).Delete();
4840 for (
int i = 0; i < offd_cols; i++)
4842 cmap[i] = cmap_offd[i].one;
4843 j_offd_hi[cmap_offd[i].two] = i;
4847 M =
new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
4848 i_diag, j_diag, i_offd_hi, j_offd_hi, cmap, offd_cols);
4853ParFiniteElementSpace::ParallelDerefinementMatrix(
int old_ndofs,
4854 const Table* old_elem_dof,
4855 const Table *old_elem_fos)
4857 int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
4859 MFEM_VERIFY(
Nonconforming(),
"Not implemented for conforming meshes.");
4860 MFEM_VERIFY(old_dof_offsets[nrk],
"Missing previous (finer) space.");
4863 MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
4864 "Previous space is not finer.");
4872 Mesh::GeometryList elem_geoms(*
mesh);
4874 Array<int> dofs, old_dofs, old_vdofs;
4877 ParNCMesh* old_pncmesh = pmesh->
pncmesh;
4884 for (
int i = 0; i < elem_geoms.Size(); i++)
4890 const CoarseFineTransformations &dtrans =
4891 old_pncmesh->GetDerefinementTransforms();
4892 const Array<int> &old_ranks = old_pncmesh->GetDerefineOldRanks();
4896 std::map<int, std::vector<HYPRE_BigInt>> to_send;
4897 std::map<int, std::vector<HYPRE_BigInt>> to_recv;
4900 std::unordered_map<int, std::array<size_t, 2>> recv_messages;
4902 HYPRE_BigInt old_offset = HYPRE_AssumedPartitionCheck()
4903 ? old_dof_offsets[0] : old_dof_offsets[MyRank];
4907 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4909 const Embedding &emb = dtrans.embeddings[k];
4911 const int fine_rank = old_ranks[k];
4912 const int coarse_rank = (emb.parent < 0) ?
FlipIndexSign(emb.parent)
4913 : old_pncmesh->ElementRank(emb.parent);
4915 if (coarse_rank != MyRank && fine_rank == MyRank)
4917 old_elem_dof->GetRow(k, dofs);
4920 std::vector<HYPRE_BigInt>& send_buf = to_send[coarse_rank];
4921 auto pos = send_buf.size();
4922 send_buf.resize(pos + dofs.Size());
4923 for (
int i = 0; i < dofs.Size(); i++)
4925 send_buf[pos + i] = old_offset + dofs[i];
4928 else if (coarse_rank == MyRank && fine_rank != MyRank)
4930 MFEM_ASSERT(emb.parent >= 0,
"");
4933 std::vector<HYPRE_BigInt>& recv_buf = to_recv[fine_rank];
4934 auto& msg = recv_messages[k];
4935 msg[0] = recv_buf.size();
4936 recv_buf.resize(recv_buf.size() + ldof[geom] *
vdim);
4937 msg[1] = recv_buf.size();
4943 std::vector<MPI_Request> requests;
4944 requests.reserve(to_send.size() + to_recv.size());
4946 for (
auto &v : to_recv)
4948 requests.emplace_back();
4949 MPI_Irecv(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
4954 for (
auto &v : to_send)
4956 requests.emplace_back();
4957 MPI_Isend(v.second.data(), v.second.size(), HYPRE_MPI_BIG_INT, v.first,
4963 for (
int i = 0; i < elem_geoms.Size(); i++)
4969 SparseMatrix *diag =
new SparseMatrix(
ndofs*
vdim, old_ndofs*
vdim);
4971 Array<char> mark(diag->Height());
4976 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
4978 const Embedding &emb = dtrans.embeddings[k];
4979 if (emb.parent < 0) {
continue; }
4981 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
4982 int fine_rank = old_ranks[k];
4984 if (coarse_rank == MyRank && fine_rank == MyRank)
4987 DenseMatrix &lR = localR[geom](emb.matrix);
4990 old_elem_dof->GetRow(k, old_dofs);
4992 for (
int vd = 0; vd <
vdim; vd++)
4994 old_dofs.Copy(old_vdofs);
4997 for (
int i = 0; i < lR.Height(); i++)
4999 if (!std::isfinite(lR(i, 0))) {
continue; }
5004 if (is_dg || !mark[m])
5007 diag->SetRow(r, old_vdofs, row);
5017 MPI_Waitall(requests.size(), requests.data(), MPI_STATUSES_IGNORE);
5020 SparseMatrix *offd =
new SparseMatrix(
ndofs*
vdim, 1);
5022 std::map<HYPRE_BigInt, int> col_map;
5023 for (
int k = 0; k < dtrans.embeddings.Size(); k++)
5025 const Embedding &emb = dtrans.embeddings[k];
5026 if (emb.parent < 0) {
continue; }
5028 int coarse_rank = old_pncmesh->ElementRank(emb.parent);
5029 int fine_rank = old_ranks[k];
5031 if (coarse_rank == MyRank && fine_rank != MyRank)
5034 DenseMatrix &lR = localR[geom](emb.matrix);
5038 auto& odofs = to_recv.at(fine_rank);
5039 auto &msg = recv_messages[k];
5040 MFEM_ASSERT(msg[1] > msg[0],
"");
5042 for (
int vd = 0; vd <
vdim; vd++)
5044 MFEM_ASSERT(ldof[geom],
"");
5045 HYPRE_BigInt *remote_dofs = odofs.data() + msg[0] + vd * ldof[geom];
5047 for (
int i = 0; i < lR.Height(); i++)
5049 if (!std::isfinite(lR(i, 0))) {
continue; }
5053 if (is_dg || !mark[m])
5056 MFEM_ASSERT(ldof[geom] == row.Size(),
"");
5057 for (
int j = 0; j < ldof[geom]; j++)
5059 if (row[j] == 0.0) {
continue; }
5060 int &lcol = col_map[remote_dofs[j]];
5061 if (!lcol) { lcol =
static_cast<int>(col_map.size()); }
5062 offd->_Set_(m, lcol-1, row[j]);
5072 offd->SetWidth(
static_cast<int>(col_map.size()));
5075 HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(offd->Width());
5076 for (
auto it = col_map.begin(); it != col_map.end(); ++it)
5078 cmap[it->second-1] = it->first;
5085 int width = offd->Width();
5086 Array<Pair<HYPRE_BigInt, int> > reorder(width);
5087 for (
int i = 0; i < width; i++)
5089 reorder[i].one = cmap[i];
5094 Array<int> reindex(width);
5095 for (
int i = 0; i < width; i++)
5097 reindex[reorder[i].two] = i;
5098 cmap[i] = reorder[i].one;
5101 int *J = offd->GetJ();
5102 for (
int i = 0; i < offd->NumNonZeroElems(); i++)
5104 J[i] = reindex[J[i]];
5106 offd->SortColumnIndices();
5109 HypreParMatrix* new_R;
5110 new_R =
new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
5111 dof_offsets, old_dof_offsets, diag, offd, cmap,
5114 new_R->SetOwnerFlags(new_R->OwnsDiag(), new_R->OwnsOffd(), 1);
5119void ParFiniteElementSpace::Destroy()
5130 delete Pconf; Pconf = NULL;
5131 delete Rconf; Rconf = NULL;
5134 delete gcomm; gcomm = NULL;
5143void ParFiniteElementSpace::CopyProlongationAndRestriction(
5144 const FiniteElementSpace &fes,
const Array<int> *perm)
5148 MFEM_VERIFY(pfes != NULL,
"");
5149 MFEM_VERIFY(P == NULL,
"");
5150 MFEM_VERIFY(R == NULL,
"");
5153 pfes->Dof_TrueDof_Matrix();
5155 SparseMatrix *perm_mat = NULL, *perm_mat_tr = NULL;
5161 int n = perm->Size();
5162 perm_mat =
new SparseMatrix(n, fes.GetVSize());
5163 for (
int i=0; i<n; ++i)
5167 perm_mat->Set(i, j, s);
5169 perm_mat->Finalize();
5173 if (pfes->P != NULL)
5176 else { P =
new HypreParMatrix(*pfes->P); }
5179 else if (perm != NULL)
5185 P =
new HypreParMatrix(MyComm, glob_nrows, glob_ncols, row_starts,
5186 col_starts, perm_mat);
5189 if (pfes->R != NULL)
5191 if (perm) { R =
Mult(*pfes->R, *perm_mat_tr); }
5192 else { R =
new SparseMatrix(*pfes->R); }
5194 else if (perm != NULL)
5215 MFEM_ASSERT(c_pfes != NULL,
"coarse_fes must be a parallel space");
5226 false, Tgf.OwnsOperator(),
false));
5227 Tgf.SetOperatorOwner(
false);
5237 MPI_Allreduce(MPI_IN_PLACE, &int_orders_changed, 1, MPI_INT,
5242 MPI_Allreduce(MPI_IN_PLACE, &var, 1, MPI_INT, MPI_MAX, MyComm);
5259 MFEM_ABORT(
"Error in update sequence. Space needs to be updated after "
5260 "each mesh modification.");
5269 Table* old_elem_dof = NULL;
5270 Table* old_elem_fos = NULL;
5281 Swap(dof_offsets, old_dof_offsets);
5306 old_elem_fos, old_ndofs));
5309 old_elem_dof = NULL;
5310 old_elem_fos = NULL;
5323 Th.
Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
5333 false,
false,
true));
5340 Th.
Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
5348 delete old_elem_dof;
5349 delete old_elem_fos;
5357 "p-refinement is not supported in this space");
5362 for (
int i = 0; i<pmesh->
GetNE(); i++)
5366 pfes_prev->Update(
false);
5369 for (
auto ref : refs)
5384void ParFiniteElementSpace::UpdateMeshPointer(
Mesh *new_mesh)
5387 MFEM_VERIFY(new_pmesh != NULL,
5388 "ParFiniteElementSpace::UpdateMeshPointer(...) must be a ParMesh");
5398 MPI_Allreduce(MPI_IN_PLACE, &order, 1, MPI_INT, MPI_MAX, MyComm);
5421 const int npref = ghost_orders.Size();
5422 for (
int i=0; i<npref; ++i)
5424 const int elem = ghost_orders[i].element;
5425 const int order = ghost_orders[i].order;
5431 for (
auto edge : edges) { edge_orders[edge] |= mask; }
5438 for (
auto face : faces) { face_orders[face] |= mask; }
5450 const int face = pncmesh->
GetNFaces() + i;
5453 if (orders == 0) {
continue; }
5457 for (
int order = 0; orders != 0; order++, orders >>= 1)
5466 MFEM_VERIFY(orderV0 > 0,
"");
5473 for (
auto edge : edges)
5475 edge_orders[edge] |= mask;
5482 : gc(gc_), local(local_)
5487 for (
int g=1; g<group_ldof.
Size(); ++g)
5491 n_external += group_ldof.
RowSize(g);
5494 int tsize = lsize - n_external;
5500 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5518 :
Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
5520 gc(pfes.GroupComm()),
5526 for (
int gr = 1; gr < group_ldof.
Size(); gr++)
5545 for ( ; j < end; j++)
5551 for ( ; j <
Height(); j++)
5569 const int in_layout = 2;
5580 for (
int i = 0; i < m; i++)
5583 if (end > j) { std::copy(xdata+j-i, xdata+end-i, ydata+j); }
5586 if (
Width() > (j-m)) { std::copy(xdata+j-m, xdata+
Width(), ydata+j); }
5588 const int out_layout = 0;
5611 for (
int i = 0; i < m; i++)
5614 if (end > j) { std::copy(xdata+j, xdata+end, ydata+j-i); }
5617 if (
Height() > j) { std::copy(xdata+j, xdata+
Height(), ydata+j-m); }
5619 const int out_layout = 2;
5642 MFEM_ASSERT(pfes.
Conforming(),
"internal error");
T Max() const
Find the maximal element in the array, using the comparison operator < for class T.
const T * HostRead() const
Shortcut for mfem::Read(a.GetMemory(), a.Size(), false).
void Sort()
Sorts the array in ascending order. This requires operator< to be defined for T.
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
void LoseData()
NULL-ifies the data.
int Size() const
Return the logical size of the array.
void MakeRef(T *data_, int size_, bool own_data=false)
Make this Array a reference to a pointer.
void DeleteAll()
Delete the whole array.
int Append(const T &el)
Append element 'el' to array, resize if necessary.
void Swap(Array &other)
Swap the contents of the Array with other.
T * HostWrite()
Shortcut for mfem::Write(a.GetMemory(), a.Size(), false).
real_t * GetData() const
Returns the matrix data array. Warning: this method casts away constness.
void RestrictTranspose(const Array< T > &x_tdof, Array< T > &x_ldof) const
Transpose of Restrict(): copy the true-dof data x_tdof into the owned local dofs of x_ldof and set th...
void Restrict(const Array< T > &x_ldof, Array< T > &x_tdof) const
Kernel: copy owned ldofs from x_ldof to ltdofs in x_tdof, i.e. x_tdof[i] = x_ldof[ltdof_ldof[i]].
void ProlongateTranspose(const Array< T > &x_ldof, Array< T > &x_tdof, Op op=Op::Sum) const
Transpose of Prolongate(): reduce the local-dof data x_ldof into the true-dof data x_tdof,...
void Prolongate(const Array< T > &x_tdof, Array< T > &x_ldof) const
Prolongate the true-dof data x_tdof to the local-dof data x_ldof.
static bool Allows(unsigned long b_mask)
Return true if any of the backends in the backend mask, b_mask, are allowed.
Abstract data type element.
Geometry::Type GetGeometryType() const
Base class for operators that extracts Face degrees of freedom.
Collection of finite elements from the same family in multiple dimensions. This class is used to matc...
virtual const int * DofOrderForOrientation(Geometry::Type GeomType, int Or) const =0
Returns an array, say p, that maps a local permuted index i to a local base index: base_i = p[i].
const int * GetDofOrdering(Geometry::Type geom, int p, int ori) const
Variable order version of DofOrderForOrientation().
int GetOrder() const
Return the order (polynomial degree) of the FE collection, corresponding to the order/degree returned...
virtual int GetContType() const =0
int HasFaceDofs(Geometry::Type geom, int p) const
virtual int DofForGeometry(Geometry::Type GeomType) const =0
int GetNumDof(Geometry::Type geom, int p) const
Variable order version of DofForGeometry().
const FiniteElement * GetFE(Geometry::Type geom, int p) const
Variable order version of FiniteElementForGeometry().
@ DISCONTINUOUS
Field is discontinuous across element interfaces.
@ TANGENTIAL
Tangential components of vector field.
virtual const FiniteElement * FiniteElementForGeometry(Geometry::Type GeomType) const =0
void SubDofOrder(Geometry::Type Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
GridFunction interpolation operator applicable after mesh refinement.
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
friend void Mesh::Swap(Mesh &, bool)
DofTransformation DoFTrans
bool IsVariableOrder() const
Returns true if the space contains elements of varying polynomial orders.
Array< StatelessDofTransformation * > DoFTransArray
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Compute the full set of vdofs corresponding to each entry in dofs.
void GetEdgeInteriorDofs(int i, Array< int > &dofs) const
Returns the indices of the degrees of freedom for the interior of the specified edge.
bool IsVariableOrderH1() const
Returns true if the space is H1 and has variable-order elements.
Array< int > face_min_nghb_order
DofTransformation * GetElementDofs(int elem, Array< int > &dofs) const
Returns indices of degrees of freedom of element 'elem'. The returned indices are offsets into an ldo...
std::shared_ptr< PRefinementTransferOperator > PTh
NURBSExtension * NURBSext
virtual int GetFaceDofs(int face, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified face, including the DOFs for the edge...
virtual void GetExteriorVDofs(Array< int > &exterior_vdofs, int component=-1) const
Mark degrees of freedom associated with exterior faces of the mesh. For spaces with 'vdim' > 1,...
int GetEdgeOrder(int edge, int variant=0) const
int GetFaceOrder(int face, int variant=0) const
Returns the polynomial degree of the i'th face finite element.
int GetNumBorderDofs(Geometry::Type geom, int order) const
static int MinOrder(VarOrderBits bits)
Return the minimum order (least significant bit set) in the bit mask.
int GetNDofs() const
Returns number of degrees of freedom. This is the number of Local Degrees of Freedom.
bool orders_changed
True if at least one element order changed (variable-order space only).
friend class PRefinementTransferOperator
void GetTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
Construct and return an Operator that can be used to transfer GridFunction data from coarse_fes,...
virtual const Operator * GetProlongationMatrix() const
int GetEntityDofs(int entity, int index, Array< int > &dofs, Geometry::Type master_geom=Geometry::INVALID, int variant=0) const
Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
Array< char > var_edge_orders
std::unique_ptr< Operator > R_transpose
Operator computing the action of the transpose of the restriction.
void UpdateElementOrders()
Resize the elem_order array on mesh change.
const FiniteElementCollection * fec
Associated FE collection (not owned).
int VDofToDof(int vdof) const
Compute the inverse of the Dof to VDof mapping for a single index vdof.
DofTransformation * GetElementVDofs(int i, Array< int > &vdofs) const
Returns indices of degrees of freedom for the i'th element. The returned indices are offsets into an ...
virtual const FiniteElement * GetFE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i'th element in t...
int FirstFaceDof(int face, int variant=0) const
void GetLocalDerefinementMatrices(Geometry::Type geom, DenseTensor &localR) const
int GetNE() const
Returns number of elements in the mesh.
int vdim
Vector dimension (number of unknowns per degree of freedom).
Table var_face_dofs
NOTE: also used for spaces with mixed faces.
bool lastUpdatePRef
Flag to indicate whether the last update was for p-refinement.
std::unordered_map< key_face, std::unique_ptr< FaceRestriction >, TupleHasher > L2F
int GetEdgeDofs(int edge, Array< int > &dofs, int variant=0) const
Returns the indices of the degrees of freedom for the specified edge, including the DOFs for the vert...
void GetEssentialBdrEdgesFaces(const Array< int > &bdr_attr_is_ess, std::set< int > &edges, std::set< int > &faces) const
OperatorHandle Th
Transformation to apply to GridFunctions after space Update().
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof, const Table *old_elem_fos)
std::uint64_t VarOrderBits
Bit-mask representing a set of orders needed by an edge/face.
int ndofs
Number of degrees of freedom. Number of unknowns is ndofs * vdim.
void GetBoundaryLoopEdgeDofs(const Array< int > &boundary_element_indices, Array< int > &boundary_edge_dofs, Array< int > *dof_edges=nullptr, Array< int > *dof_boundary_elements=nullptr) const
Extract the edge degrees of freedom of a boundary "loop".
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
int GetElementOrder(int i) const
Returns the order of the i'th finite element.
Mesh * mesh
The mesh that FE space lives on (not owned).
void SetElementOrder(int i, int p)
Sets the order of the i'th finite element.
const FiniteElementCollection * FEColl() const
int GetNVariants(int entity, int index) const
Return number of possible DOF variants for edge/face (var. order spaces).
int GetVSize() const
Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
DofTransformation * GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element 'bel'. The returned indices are offsets in...
bool IsDGSpace() const
Return whether or not the space is discontinuous (L2)
Array< int > edge_min_nghb_order
Minimum order among neighboring elements.
static int DecodeDof(int dof)
Helper to return the DOF associated with a sign encoded DOF.
bool PRefinementSupported()
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_vdofs, int component=-1) const
Mark degrees of freedom associated with boundary elements with the specified boundary attributes (mar...
int DofToVDof(int dof, int vd, int ndofs=-1) const
Compute a single vdof corresponding to the index dof and the vector index vd.
int FindEdgeDof(int edge, int ndof) const
void BuildElementToDofTable() const
Abstract class for all finite elements.
int GetDof() const
Returns the number of degrees of freedom in the finite element.
static const int Dimension[NumGeom]
static bool IsTensorProduct(Type geom)
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize().
void ReduceEnd(T *ldata, int layout, void(*Op)(OpData< T >)) const
Finalize reduction operation started with the host version of ReduceBegin().
const GroupTopology & GetGroupTopology() const
Get a const reference to the associated GroupTopology object.
void Reduce(T *ldata, void(*Op)(OpData< T >)) const
Reduce within each group where the master is the root, host version.
void BcastEnd(T *ldata, int layout) const
Finalize a broadcast started with the host version of BcastBegin().
void ReduceBegin(const T *ldata) const
Begin reduction operation within each group where the master is the root, host version.
void Create(const Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int, double and float.
void Bcast(T *ldata, int layout) const
Broadcast within each group where the master is the root.
void SetLTDofTable(const Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
void BcastBegin(T *ldata, int layout) const
Begin a broadcast within each group where the master is the root, host version.
void Finalize()
Allocate internal buffers after the GroupLDofTable is defined.
const DeviceGroupCommunicator & GetDeviceComm() const
Return the device communicator, 'device_gc', constructing it if it was not already constructed.
bool IAmMaster(int g) const
Return true if I am master for group 'g'.
int GetGroupSize(int g) const
Get the number of processors in a group.
int GetGroupMaster(int g) const
Return the neighbor index of the group master for a given group. Neighbor 0 is the local processor.
int GetGroupMasterRank(int g) const
Return the rank of the group master for group 'g'.
Wrapper for hypre's ParCSR matrix class.
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
HypreParMatrix * LeftDiagMult(const SparseMatrix &D, HYPRE_BigInt *row_starts=NULL) const
Multiply the HypreParMatrix on the left by a block-diagonal parallel matrix D and return the result a...
Identity Operator I: x -> x.
Operator that extracts face degrees of freedom for L2 interface spaces.
Operation GetLastOperation() const
Return type of last modification of the mesh.
int GetNEdges() const
Return the number of edges.
void GetBdrElementFace(int i, int *f, int *o) const
NURBSExtension * NURBSext
Optional NURBS mesh extension.
void GetFaceInfos(int Face, int *Inf1, int *Inf2) const
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Geometry::Type GetFaceGeometry(int i) const
Return the Geometry::Type associated with face i.
Geometry::Type GetBdrElementGeometry(int i) const
bool Nonconforming() const
int GetNFaces() const
Return the number of faces in a 3D mesh.
int GetNE() const
Returns number of elements.
int Dimension() const
Dimension of the reference space used within the elements.
void GetFaceElements(int Face, int *Elem1, int *Elem2) const
Return the indices of the elements sharing face Face.
void GetFaceVertices(int i, Array< int > &vert) const
Returns the indices of the vertices of face i.
bool HasGeometry(Geometry::Type geom) const
Return true iff the given geom is encountered in the mesh. Geometries of dimensions lower than Dimens...
Geometry::Type GetTypicalFaceGeometry() const
If the local mesh is not empty, return GetFaceGeometry(0); otherwise return a typical face geometry p...
Geometry::Type GetElementBaseGeometry(int i) const
static int WorldRank()
Return the MPI rank in MPI_COMM_WORLD.
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2], bool oriented=true) const
Return Mesh vertex indices of an edge identified by 'edge_id'.
const NCList & GetNCList(int entity)
Return vertex/edge/face list (entity = 0/1/2, respectively).
int GetEdgeNCOrientation(const MeshId &edge_id) const
int GetFaceVerticesEdges(const MeshId &face_id, int vert_index[4], int edge_index[4], int edge_orientation[4]) const
Geometry::Type GetFaceGeometry(int index) const
Return face geometry type. index is the Mesh face number.
int GetNVertices() const
Return the number of vertices in the NCMesh.
int MyRank
used in parallel, or when loading a parallel file in serial
int GetNFaces() const
Return the number of (2D) faces in the NCMesh.
int GetNEdges() const
Return the number of edges in the NCMesh.
NURBSExtension generally contains multiple NURBSPatch objects spanning an entire Mesh....
Pointer to an Operator of a specified type.
void SetOperatorOwner(bool own=true)
Set the ownership flag for the held Operator.
Operator * Ptr() const
Access the underlying Operator pointer.
void Reset(OpType *A, bool own_A=true)
Reset the OperatorHandle to the given OpType pointer, A.
Operator::Type Type() const
Get the currently set operator type id.
int width
Dimension of the input / number of columns in the matrix.
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
int height
Dimension of the output / number of rows in the matrix.
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
@ ANY_TYPE
ID for the base class Operator, i.e. any type.
@ MFEM_SPARSEMAT
ID for class SparseMatrix.
@ Hypre_ParCSR
ID for class HypreParMatrix.
int NumRows() const
Get the number of rows (size of output) of the Operator. Synonym with Height().
Abstract parallel finite element space.
void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes, OperatorHandle &T) const override
Construct and return an Operator that can be used to transfer true-dof data from coarse_fes,...
HYPRE_BigInt GetGlobalScalarTDofNumber(int sldof)
void GetBoundaryLoopEdgeDofs(const Array< int > &boundary_element_indices, Array< int > &ess_tdof_list, Array< int > &boundary_edge_dofs_out, Array< int > *ldof_marker=nullptr, Array< int > *dof_edges=nullptr, Array< int > *dof_boundary_elements=nullptr, Array< int > *ess_edge_list=nullptr)
Extract the edge degrees of freedom of a boundary "loop" on a parallel mesh (see the serial FiniteEle...
void GetSharedTriangleDofs(int group, int fi, Array< int > &dofs) const
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
int GetMaxElementOrder() const override
Returns the maximum polynomial order over all elements globally.
const FaceRestriction * GetFaceRestriction(ElementDofOrdering f_ordering, FaceType type, L2FaceValues mul=L2FaceValues::DoubleValued) const override
HYPRE_BigInt * GetTrueDofOffsets() const
void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1) const override
void PrintPartitionStats()
void GetExteriorTrueDofs(Array< int > &ext_tdof_list, int component=-1) const override
void GetExteriorVDofs(Array< int > &ext_dofs, int component=-1) const override
Determine the external degrees of freedom.
HYPRE_BigInt GlobalVSize() const
const Operator * GetRestrictionOperator() const override
int GetLocalTDofNumber(int ldof) const
friend struct ParDerefineMatrixOp
void Synchronize(Array< int > &ldof_marker) const
Given an integer array on the local degrees of freedom, perform a bitwise OR between the shared dofs.
void ApplyGhostElementOrdersToEdgesAndFaces(Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const override
ParFiniteElementSpace(const ParFiniteElementSpace &orig, ParMesh *pmesh=NULL, const FiniteElementCollection *fec=NULL)
Copy constructor: deep copy all data from orig except the ParMesh, the FiniteElementCollection,...
void DivideByGroupSize(real_t *vec)
Scale a vector of true dofs.
void ExchangeFaceNbrData()
HYPRE_BigInt GlobalTrueVSize() const
int GetTrueVSize() const override
Return the number of local vector true dofs.
const FiniteElement * GetFaceNbrFE(int i, int ndofs=0) const
void GhostFaceOrderToEdges(const Array< VarOrderBits > &face_orders, Array< VarOrderBits > &edge_orders) const override
HYPRE_BigInt GetMyDofOffset() const
HYPRE_BigInt * GetDofOffsets() const
Array< HYPRE_BigInt > face_nbr_glob_dof_map
bool Nonconforming() const
void GetEssentialTrueDofsVar(const Array< int > &bdr_attr_is_ess, const Array< int > &ess_dofs, Array< int > &true_ess_dofs, int component) const
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
const Operator * GetProlongationMatrix() const override
bool HaveDofSigns() const
Return true if the parallel FE space has DOFs with signs opposite of the DOFs in the respective seria...
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on scalar dofs, i.e. for VDim = 1.
bool OrderPropagation(const std::set< int > &edges, const std::set< int > &faces, Array< VarOrderBits > &edge_orders, Array< VarOrderBits > &face_orders) const override
void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const override
Determine the boundary degrees of freedom.
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
void GetFaceNbrElementVDofs(int i, Array< int > &vdofs, DofTransformation &doftrans) const
int GetFaceDofs(int i, Array< int > &dofs, int variant=0) const override
void ApplyDofSigns(real_t *h_data) const
Apply the DOF signs to the given host data h_data which must be of size GetVSize() if HaveDofSigns() ...
HYPRE_BigInt GetMyTDofOffset() const
void GetSharedQuadrilateralDofs(int group, int fi, Array< int > &dofs) const
void GetElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetElementDofs(), but with a user-provided DofTransformation object.
void PRefineAndUpdate(const Array< pRefinement > &refs, bool want_transfer=true) override
const SparseMatrix * GetRestrictionMatrix() const override
Get the R matrix which restricts a local dof vector to true dof vector.
HYPRE_BigInt GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
void Update(bool want_transform=true) override
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
int TrueVSize() const
Obsolete, kept for backward compatibility.
void MarkIntermediateEntityDofs(int entity, Array< bool > &intermediate) const
void Lose_Dof_TrueDof_Matrix()
const FiniteElement * GetFaceNbrFaceFE(int i) const
const FiniteElement * GetFE(int i) const override
Table face_nbr_element_dof
void GetBdrElementDofs(int i, Array< int > &dofs, DofTransformation &doftrans) const override
The same as GetBdrElementDofs(), but with a user-provided DofTransformation object.
Operator that extracts Face degrees of freedom in parallel.
Class for parallel meshes.
int GroupNQuadrilaterals(int group) const
Table send_face_nbr_elements
void ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
int GroupVertex(int group, int i) const
Accessors for entities within a shared group structure.
void GetFaceNbrElementFaces(int i, Array< int > &faces, Array< int > &orientation) const
void GetGlobalFaceIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
void GetGlobalEdgeIndices(Array< HYPRE_BigInt > &gi) const
AMR meshes are not supported.
Array< Element * > face_nbr_elements
int GroupNTriangles(int group) const
int GroupNEdges(int group) const
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[], Array< HYPRE_BigInt > *offsets[]) const
Array< int > face_nbr_elements_offset
int GetNFaceNeighbors() const
void GroupQuadrilateral(int group, int i, int &face, int &o) const
int GetFaceNbrRank(int fn) const
void GroupTriangle(int group, int i, int &face, int &o) const
void GroupEdge(int group, int i, int &edge, int &o) const
int GroupNVertices(int group) const
Operator that extracts Face degrees of freedom for NCMesh in parallel.
Operator that extracts Face degrees of freedom for NCMesh in parallel.
void SendRebalanceDofs(int old_ndofs, const Table &old_element_dofs, long old_global_offset, FiniteElementSpace *space)
Use the communication pattern from last Rebalance() to send element DOFs.
int GetFaceOrientation(int index) const
Return (shared) face orientation relative to its owner element.
GroupId GetEntityGroupId(int entity, int index)
int GetNGhostEdges() const
void DecodeGroups(std::istream &is, Array< GroupId > &ids)
const CommGroup & GetGroup(GroupId id) const
Return a list of ranks contained in the group of the given ID.
void FindEdgesOfGhostElement(int elem, Array< int > &edges)
void FindFacesOfGhostElement(int elem, Array< int > &faces)
int GetNGhostFaces() const
void EncodeGroups(std::ostream &os, const Array< GroupId > &ids)
void FindEdgesOfGhostFace(int face, Array< int > &edges)
void AdjustMeshIds(Array< MeshId > ids[], int rank)
std::vector< int > CommGroup
void DecodeMeshIds(std::istream &is, Array< MeshId > ids[])
void EncodeMeshIds(std::ostream &os, Array< MeshId > ids[])
int GetMyRank() const
Return the MPI rank for this process.
int GetNGhostVertices() const
bool GroupContains(GroupId id, int rank) const
Return true if group 'id' contains the given rank.
void CommunicateGhostData(const Array< VarOrderElemInfo > &sendData, Array< VarOrderElemInfo > &recvData)
Parallel version of NURBSExtension.
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, treating all entries as booleans (zero=false, nonzero=true).
Table stores the connectivity of elements of TYPE I to elements of TYPE II. For example,...
void AddConnections(int r, const int *c, int nc)
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
void AddConnection(int r, int c)
int Size() const
Returns the number of TYPE I elements.
int Size_of_connections() const
Returns the number of connections in the table.
void AddColumnsInRow(int r, int ncol)
void AddAColumnInRow(int r)
void SetDims(int rows, int nnz)
Set the rows and the number of all connections for the table.
The transpose of a given operator. Switches the roles of the methods Mult() and MultTranspose().
General triple product operator x -> A*B*C*x, with ownership of the factors.
virtual const real_t * HostRead() const
Shortcut for mfem::Read(vec.GetMemory(), vec.Size(), false).
MemoryView< Array< real_t > > GetArrayView()
Create a mutable (non-const) view of the Vector as Array<real_t> that can be used to pass Vector obje...
void SetSubVector(const Array< int > &dofs, const real_t value)
Set the entries listed in dofs to the given value.
int Size() const
Returns the size of the vector.
virtual real_t * HostWrite()
Shortcut for mfem::Write(vec.GetMemory(), vec.Size(), false).
const int * ess_tdof_list
int index(int i, int j, int nx, int ny)
void write(std::ostream &os, T value)
Write 'value' to stream.
T read(std::istream &is)
Read a value from the stream and return it.
Linear1DFiniteElement SegmentFE
void mfem_error(const char *msg)
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
void Transpose(const Table &A, Table &At, int ncols_A_)
Transpose a Table.
MFEM_HOST_DEVICE int FlipIndexSign(int i)
Signed indices i -> -1 - i are used as a convention to encode orientation.
MFEM_HOST_DEVICE int UnsignIndex(int i)
void Swap(T &a, T &b)
Swap objects of type T. The operation is performed using the most specialized swap function from the ...
BiLinear2DFiniteElement QuadrilateralFE
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
void SortPairs(Pair< A, B > *pairs, int size)
Sort an array of Pairs with respect to the first element.
ElementDofOrdering
Constants describing the possible orderings of the DOFs in one element.
std::function< real_t(const Vector &)> f(real_t mass_coeff)
MFEM_EXPORT Linear2DFiniteElement TriangleFE
@ DEREFINEMENT_MATRIX_CONSTRUCTION_DATA
real_t p(const Vector &x, real_t t)
@ DEVICE_MASK
Biwise-OR of all device backends.
MeshIdAndType GetMeshIdAndType(int index) const
Return a mesh id and type for a given nc index.
static void WaitAllSent(MapT &rank_msg)
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
static bool IProbe(int &rank, int &size, MPI_Comm comm)