MFEM  v3.3.2
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
pfespace.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
2 // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
3 // reserved. See file COPYRIGHT for details.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability see http://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the GNU Lesser General Public License (as published by the Free
10 // Software Foundation) version 2.1 dated February 1999.
11 
12 #include "../config/config.hpp"
13 
14 #ifdef MFEM_USE_MPI
15 
16 #include "pfespace.hpp"
17 #include "../general/sort_pairs.hpp"
18 #include "../mesh/mesh_headers.hpp"
19 
20 #include <climits> // INT_MAX
21 #include <limits>
22 #include <list>
23 
24 namespace mfem
25 {
26 
28  ParMesh *pm, const FiniteElementCollection *f, int dim, int ordering)
29  : FiniteElementSpace(pm, f, dim, ordering)
30 {
31  mesh = pmesh = pm;
32 
33  MyComm = pmesh->GetComm();
34  MPI_Comm_size(MyComm, &NRanks);
35  MPI_Comm_rank(MyComm, &MyRank);
36 
37  num_face_nbr_dofs = -1;
38 
39  P = NULL;
40  Pconf = NULL;
41  R = NULL;
42  gcomm = NULL;
43 
44  Construct();
45 
46  // Apply the ldof_signs to the elem_dof Table
47  if (Conforming() && !NURBSext)
48  {
49  Array<int> dofs;
50  for (int i = 0; i < elem_dof->Size(); i++)
51  {
52  dofs.MakeRef(elem_dof->GetRow(i), elem_dof->RowSize(i));
53  ApplyLDofSigns(dofs);
54  }
55  }
56 }
57 
58 void ParFiniteElementSpace::Construct()
59 {
60  if (NURBSext)
61  {
62  if (own_ext)
63  {
64  // the FiniteElementSpace constructor created a serial
65  // NURBSExtension of higher order than the mesh NURBSExtension
66 
68  NURBSext, dynamic_cast<ParNURBSExtension *>(pmesh->NURBSext));
69  // serial NURBSext is destroyed by the above constructor
70  NURBSext = pNe;
71  UpdateNURBS();
72  }
73  ConstructTrueNURBSDofs();
74  GenerateGlobalOffsets();
75  }
76  else if (Conforming())
77  {
78  ConstructTrueDofs();
79  GenerateGlobalOffsets();
80  }
81  else // Nonconforming()
82  {
83  // get P matrix and also initialize DOF offsets etc.
84  GetParallelConformingInterpolation();
85  }
86 }
87 
88 void ParFiniteElementSpace::GetGroupComm(
89  GroupCommunicator &gc, int ldof_type, Array<int> *ldof_sign)
90 {
91  int gr;
92  int ng = pmesh->GetNGroups();
93  int nvd, ned, nfd;
94  Array<int> dofs;
95 
96  int group_ldof_counter;
97  Table &group_ldof = gc.GroupLDofTable();
98 
101  nfd = (fdofs) ? (fdofs[1]-fdofs[0]) : (0);
102 
103  if (ldof_sign)
104  {
105  ldof_sign->SetSize(GetNDofs());
106  *ldof_sign = 1;
107  }
108 
109  // count the number of ldofs in all groups (excluding the local group 0)
110  group_ldof_counter = 0;
111  for (gr = 1; gr < ng; gr++)
112  {
113  group_ldof_counter += nvd * pmesh->GroupNVertices(gr);
114  group_ldof_counter += ned * pmesh->GroupNEdges(gr);
115  group_ldof_counter += nfd * pmesh->GroupNFaces(gr);
116  }
117  if (ldof_type)
118  {
119  group_ldof_counter *= vdim;
120  }
121  // allocate the I and J arrays in group_ldof
122  group_ldof.SetDims(ng, group_ldof_counter);
123 
124  // build the full group_ldof table
125  group_ldof_counter = 0;
126  group_ldof.GetI()[0] = group_ldof.GetI()[1] = 0;
127  for (gr = 1; gr < ng; gr++)
128  {
129  int j, k, l, m, o, nv, ne, nf;
130  const int *ind;
131 
132  nv = pmesh->GroupNVertices(gr);
133  ne = pmesh->GroupNEdges(gr);
134  nf = pmesh->GroupNFaces(gr);
135 
136  // vertices
137  if (nvd > 0)
138  {
139  for (j = 0; j < nv; j++)
140  {
141  k = pmesh->GroupVertex(gr, j);
142 
143  dofs.SetSize(nvd);
144  m = nvd * k;
145  for (l = 0; l < nvd; l++, m++)
146  {
147  dofs[l] = m;
148  }
149 
150  if (ldof_type)
151  {
152  DofsToVDofs(dofs);
153  }
154 
155  for (l = 0; l < dofs.Size(); l++)
156  {
157  group_ldof.GetJ()[group_ldof_counter++] = dofs[l];
158  }
159  }
160  }
161 
162  // edges
163  if (ned > 0)
164  {
165  for (j = 0; j < ne; j++)
166  {
167  pmesh->GroupEdge(gr, j, k, o);
168 
169  dofs.SetSize(ned);
170  m = nvdofs+k*ned;
172  for (l = 0; l < ned; l++)
173  {
174  if (ind[l] < 0)
175  {
176  dofs[l] = m + (-1-ind[l]);
177  if (ldof_sign)
178  {
179  (*ldof_sign)[dofs[l]] = -1;
180  }
181  }
182  else
183  {
184  dofs[l] = m + ind[l];
185  }
186  }
187 
188  if (ldof_type)
189  {
190  DofsToVDofs(dofs);
191  }
192 
193  for (l = 0; l < dofs.Size(); l++)
194  {
195  group_ldof.GetJ()[group_ldof_counter++] = dofs[l];
196  }
197  }
198  }
199 
200  // faces
201  if (nfd > 0)
202  {
203  for (j = 0; j < nf; j++)
204  {
205  pmesh->GroupFace(gr, j, k, o);
206 
207  dofs.SetSize(nfd);
208  m = nvdofs+nedofs+fdofs[k];
210  mesh->GetFaceBaseGeometry(k), o);
211  for (l = 0; l < nfd; l++)
212  {
213  if (ind[l] < 0)
214  {
215  dofs[l] = m + (-1-ind[l]);
216  if (ldof_sign)
217  {
218  (*ldof_sign)[dofs[l]] = -1;
219  }
220  }
221  else
222  {
223  dofs[l] = m + ind[l];
224  }
225  }
226 
227  if (ldof_type)
228  {
229  DofsToVDofs(dofs);
230  }
231 
232  for (l = 0; l < dofs.Size(); l++)
233  {
234  group_ldof.GetJ()[group_ldof_counter++] = dofs[l];
235  }
236  }
237  }
238 
239  group_ldof.GetI()[gr+1] = group_ldof_counter;
240  }
241 
242  gc.Finalize();
243 }
244 
245 void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
246 {
247  for (int i = 0; i < dofs.Size(); i++)
248  {
249  if (dofs[i] < 0)
250  {
251  if (ldof_sign[-1-dofs[i]] < 0)
252  {
253  dofs[i] = -1-dofs[i];
254  }
255  }
256  else
257  {
258  if (ldof_sign[dofs[i]] < 0)
259  {
260  dofs[i] = -1-dofs[i];
261  }
262  }
263  }
264 }
265 
267 {
268  if (elem_dof)
269  {
270  elem_dof->GetRow(i, dofs);
271  return;
272  }
274  if (Conforming())
275  {
276  ApplyLDofSigns(dofs);
277  }
278 }
279 
281 {
282  if (bdrElem_dof)
283  {
284  bdrElem_dof->GetRow(i, dofs);
285  return;
286  }
288  if (Conforming())
289  {
290  ApplyLDofSigns(dofs);
291  }
292 }
293 
295 {
297  if (Conforming())
298  {
299  ApplyLDofSigns(dofs);
300  }
301 }
302 
304  int group, int ei, Array<int> &dofs) const
305 {
306  int l_edge, ori;
307  MFEM_ASSERT(0 <= ei && ei < pmesh->GroupNEdges(group), "invalid edge index");
308  pmesh->GroupEdge(group, ei, l_edge, ori);
309  if (ori > 0) // ori = +1 or -1
310  {
311  GetEdgeDofs(l_edge, dofs);
312  }
313  else
314  {
315  Array<int> rdofs;
316  fec->SubDofOrder(Geometry::SEGMENT, 1, 1, dofs);
317  GetEdgeDofs(l_edge, rdofs);
318  for (int i = 0; i < dofs.Size(); i++)
319  {
320  const int di = dofs[i];
321  dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
322  }
323  }
324 }
325 
327  int group, int fi, Array<int> &dofs) const
328 {
329  int l_face, ori;
330  MFEM_ASSERT(0 <= fi && fi < pmesh->GroupNFaces(group), "invalid face index");
331  pmesh->GroupFace(group, fi, l_face, ori);
332  if (ori == 0)
333  {
334  GetFaceDofs(l_face, dofs);
335  }
336  else
337  {
338  Array<int> rdofs;
339  fec->SubDofOrder(pmesh->GetFaceBaseGeometry(l_face), 2, ori, dofs);
340  GetFaceDofs(l_face, rdofs);
341  for (int i = 0; i < dofs.Size(); i++)
342  {
343  const int di = dofs[i];
344  dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
345  }
346  }
347 }
348 
349 void ParFiniteElementSpace::GenerateGlobalOffsets() const
350 {
351  HYPRE_Int ldof[2];
352  Array<HYPRE_Int> *offsets[2] = { &dof_offsets, &tdof_offsets };
353 
354  ldof[0] = GetVSize();
355  ldof[1] = TrueVSize();
356 
357  pmesh->GenerateOffsets(2, ldof, offsets);
358 
359  if (HYPRE_AssumedPartitionCheck())
360  {
361  if (Conforming())
362  {
363  // Communicate the neighbor offsets in tdof_nb_offsets
364  GroupTopology &gt = GetGroupTopo();
365  int nsize = gt.GetNumNeighbors()-1;
366  MPI_Request *requests = new MPI_Request[2*nsize];
367  MPI_Status *statuses = new MPI_Status[2*nsize];
368  tdof_nb_offsets.SetSize(nsize+1);
369  tdof_nb_offsets[0] = tdof_offsets[0];
370 
371  int request_counter = 0;
372  // send and receive neighbors' local tdof offsets
373  for (int i = 1; i <= nsize; i++)
374  {
375  MPI_Irecv(&tdof_nb_offsets[i], 1, HYPRE_MPI_INT,
376  gt.GetNeighborRank(i), 5365, MyComm,
377  &requests[request_counter++]);
378  }
379  for (int i = 1; i <= nsize; i++)
380  {
381  MPI_Isend(&tdof_nb_offsets[0], 1, HYPRE_MPI_INT,
382  gt.GetNeighborRank(i), 5365, MyComm,
383  &requests[request_counter++]);
384  }
385  MPI_Waitall(request_counter, requests, statuses);
386 
387  delete [] statuses;
388  delete [] requests;
389  }
390  else
391  {
392  // Do nothing
393  }
394  }
395 }
396 
397 void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
398 {
399  if (P) { return; }
400 
401  if (Nonconforming())
402  {
403  GetParallelConformingInterpolation();
404  return;
405  }
406 
407  int ldof = GetVSize();
408  int ltdof = TrueVSize();
409 
410  HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
411  HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
412  int diag_counter;
413 
414  HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
415  HYPRE_Int *j_offd = new HYPRE_Int[ldof-ltdof];
416  int offd_counter;
417 
418  HYPRE_Int *cmap = new HYPRE_Int[ldof-ltdof];
419 
420  HYPRE_Int *col_starts = GetTrueDofOffsets();
421  HYPRE_Int *row_starts = GetDofOffsets();
422 
423  Array<Pair<HYPRE_Int, int> > cmap_j_offd(ldof-ltdof);
424 
425  i_diag[0] = i_offd[0] = 0;
426  diag_counter = offd_counter = 0;
427  for (int i = 0; i < ldof; i++)
428  {
429  int ltdof = GetLocalTDofNumber(i);
430  if (ltdof >= 0)
431  {
432  j_diag[diag_counter++] = ltdof;
433  }
434  else
435  {
436  cmap_j_offd[offd_counter].one = GetGlobalTDofNumber(i);
437  cmap_j_offd[offd_counter].two = offd_counter;
438  offd_counter++;
439  }
440  i_diag[i+1] = diag_counter;
441  i_offd[i+1] = offd_counter;
442  }
443 
444  SortPairs<HYPRE_Int, int>(cmap_j_offd, offd_counter);
445 
446  for (int i = 0; i < offd_counter; i++)
447  {
448  cmap[i] = cmap_j_offd[i].one;
449  j_offd[cmap_j_offd[i].two] = i;
450  }
451 
452  P = new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
453  i_diag, j_diag, i_offd, j_offd, cmap, offd_counter);
454 
455  SparseMatrix Pdiag;
456  P->GetDiag(Pdiag);
457  R = Transpose(Pdiag);
458 }
459 
461 {
462  if (Nonconforming())
463  {
464  MFEM_ABORT("Not implemented for NC mesh.");
465  }
466 
467  GroupTopology &gt = GetGroupTopo();
468 
469  for (int i = 0; i < ldof_group.Size(); i++)
470  {
471  if (gt.IAmMaster(ldof_group[i])) // we are the master
472  {
473  vec[ldof_ltdof[i]] /= gt.GetGroupSize(ldof_group[i]);
474  }
475  }
476 }
477 
479 {
480  if (Nonconforming())
481  {
482  // MFEM_WARNING("Not implemented for NC mesh.");
483  return NULL;
484  }
485 
486  GroupCommunicator *gc = new GroupCommunicator(GetGroupTopo());
487  if (NURBSext)
488  {
489  gc->Create(pNURBSext()->ldof_group);
490  }
491  else
492  {
493  GetGroupComm(*gc, 0);
494  }
495  return gc;
496 }
497 
499 {
500  if (Nonconforming())
501  {
502  MFEM_ABORT("Not implemented for NC mesh.");
503  }
504 
505  if (ldof_marker.Size() != GetVSize())
506  {
507  mfem_error("ParFiniteElementSpace::Synchronize");
508  }
509 
510  // implement allreduce(|) as reduce(|) + broadcast
511  gcomm->Reduce<int>(ldof_marker, GroupCommunicator::BitOR);
512  gcomm->Bcast(ldof_marker);
513 }
514 
516  Array<int> &ess_dofs,
517  int component) const
518 {
519  FiniteElementSpace::GetEssentialVDofs(bdr_attr_is_ess, ess_dofs, component);
520 
521  if (Conforming())
522  {
523  // Make sure that processors without boundary elements mark
524  // their boundary dofs (if they have any).
525  Synchronize(ess_dofs);
526  }
527 }
528 
530  &bdr_attr_is_ess,
531  Array<int> &ess_tdof_list,
532  int component)
533 {
534  Array<int> ess_dofs, true_ess_dofs;
535 
536  GetEssentialVDofs(bdr_attr_is_ess, ess_dofs, component);
537  GetRestrictionMatrix()->BooleanMult(ess_dofs, true_ess_dofs);
538  MarkerToList(true_ess_dofs, ess_tdof_list);
539 }
540 
542 {
543  if (Nonconforming())
544  {
545  Dof_TrueDof_Matrix(); // inline method
546 
547  return ldof_ltdof[ldof]; // NOTE: contains -1 for slaves/DOFs we don't own
548  }
549  else
550  {
551  if (GetGroupTopo().IAmMaster(ldof_group[ldof]))
552  {
553  return ldof_ltdof[ldof];
554  }
555  else
556  {
557  return -1;
558  }
559  }
560 }
561 
563 {
564  if (Nonconforming())
565  {
566  MFEM_VERIFY(ldof_ltdof[ldof] >= 0, "ldof " << ldof << " not a true DOF.");
567 
568  return GetMyTDofOffset() + ldof_ltdof[ldof];
569  }
570  else
571  {
572  if (HYPRE_AssumedPartitionCheck())
573  {
574  return ldof_ltdof[ldof] +
575  tdof_nb_offsets[GetGroupTopo().GetGroupMaster(ldof_group[ldof])];
576  }
577  else
578  {
579  return ldof_ltdof[ldof] +
580  tdof_offsets[GetGroupTopo().GetGroupMasterRank(ldof_group[ldof])];
581  }
582  }
583 }
584 
586 {
587  if (Nonconforming())
588  {
589  MFEM_ABORT("Not implemented for NC mesh.");
590  }
591 
592  if (HYPRE_AssumedPartitionCheck())
593  {
595  {
596  return ldof_ltdof[sldof] +
597  tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
598  ldof_group[sldof])] / vdim;
599  }
600  else
601  {
602  return (ldof_ltdof[sldof*vdim] +
603  tdof_nb_offsets[GetGroupTopo().GetGroupMaster(
604  ldof_group[sldof*vdim])]) / vdim;
605  }
606  }
607 
609  {
610  return ldof_ltdof[sldof] +
611  tdof_offsets[GetGroupTopo().GetGroupMasterRank(
612  ldof_group[sldof])] / vdim;
613  }
614  else
615  {
616  return (ldof_ltdof[sldof*vdim] +
617  tdof_offsets[GetGroupTopo().GetGroupMasterRank(
618  ldof_group[sldof*vdim])]) / vdim;
619  }
620 }
621 
623 {
624  return HYPRE_AssumedPartitionCheck() ? dof_offsets[0] : dof_offsets[MyRank];
625 }
626 
628 {
629  return HYPRE_AssumedPartitionCheck()? tdof_offsets[0] : tdof_offsets[MyRank];
630 }
631 
633 {
634  if (Conforming())
635  {
636  if (!Pconf) { Pconf = new ConformingProlongationOperator(*this); }
637  return Pconf;
638  }
639  else
640  {
641  return Dof_TrueDof_Matrix();
642  }
643 }
644 
646 {
647  if (num_face_nbr_dofs >= 0) { return; }
648 
649  pmesh->ExchangeFaceNbrData();
650 
651  int num_face_nbrs = pmesh->GetNFaceNeighbors();
652 
653  if (num_face_nbrs == 0)
654  {
655  num_face_nbr_dofs = 0;
656  return;
657  }
658 
659  MPI_Request *requests = new MPI_Request[2*num_face_nbrs];
660  MPI_Request *send_requests = requests;
661  MPI_Request *recv_requests = requests + num_face_nbrs;
662  MPI_Status *statuses = new MPI_Status[num_face_nbrs];
663 
664  Array<int> ldofs;
665  Array<int> ldof_marker(GetVSize());
666  ldof_marker = -1;
667 
668  Table send_nbr_elem_dof;
669 
670  send_nbr_elem_dof.MakeI(pmesh->send_face_nbr_elements.Size_of_connections());
671  send_face_nbr_ldof.MakeI(num_face_nbrs);
672  face_nbr_ldof.MakeI(num_face_nbrs);
673  int *send_el_off = pmesh->send_face_nbr_elements.GetI();
674  int *recv_el_off = pmesh->face_nbr_elements_offset;
675  for (int fn = 0; fn < num_face_nbrs; fn++)
676  {
677  int *my_elems = pmesh->send_face_nbr_elements.GetRow(fn);
678  int num_my_elems = pmesh->send_face_nbr_elements.RowSize(fn);
679 
680  for (int i = 0; i < num_my_elems; i++)
681  {
682  GetElementVDofs(my_elems[i], ldofs);
683  for (int j = 0; j < ldofs.Size(); j++)
684  if (ldof_marker[ldofs[j]] != fn)
685  {
686  ldof_marker[ldofs[j]] = fn;
688  }
689  send_nbr_elem_dof.AddColumnsInRow(send_el_off[fn] + i, ldofs.Size());
690  }
691 
692  int nbr_rank = pmesh->GetFaceNbrRank(fn);
693  int tag = 0;
694  MPI_Isend(&send_face_nbr_ldof.GetI()[fn], 1, MPI_INT, nbr_rank, tag,
695  MyComm, &send_requests[fn]);
696 
697  MPI_Irecv(&face_nbr_ldof.GetI()[fn], 1, MPI_INT, nbr_rank, tag,
698  MyComm, &recv_requests[fn]);
699  }
700 
701  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
703 
705 
706  MPI_Waitall(num_face_nbrs, send_requests, statuses);
708 
709  // send/receive the I arrays of send_nbr_elem_dof/face_nbr_element_dof,
710  // respectively (they contain the number of dofs for each face-neighbor
711  // element)
712  face_nbr_element_dof.MakeI(recv_el_off[num_face_nbrs]);
713 
714  int *send_I = send_nbr_elem_dof.GetI();
715  int *recv_I = face_nbr_element_dof.GetI();
716  for (int fn = 0; fn < num_face_nbrs; fn++)
717  {
718  int nbr_rank = pmesh->GetFaceNbrRank(fn);
719  int tag = 0;
720  MPI_Isend(send_I + send_el_off[fn], send_el_off[fn+1] - send_el_off[fn],
721  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
722 
723  MPI_Irecv(recv_I + recv_el_off[fn], recv_el_off[fn+1] - recv_el_off[fn],
724  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
725  }
726 
727  MPI_Waitall(num_face_nbrs, send_requests, statuses);
728  send_nbr_elem_dof.MakeJ();
729 
730  ldof_marker = -1;
731 
732  for (int fn = 0; fn < num_face_nbrs; fn++)
733  {
734  int *my_elems = pmesh->send_face_nbr_elements.GetRow(fn);
735  int num_my_elems = pmesh->send_face_nbr_elements.RowSize(fn);
736 
737  for (int i = 0; i < num_my_elems; i++)
738  {
739  GetElementVDofs(my_elems[i], ldofs);
740  for (int j = 0; j < ldofs.Size(); j++)
741  {
742  if (ldof_marker[ldofs[j]] != fn)
743  {
744  ldof_marker[ldofs[j]] = fn;
745  send_face_nbr_ldof.AddConnection(fn, ldofs[j]);
746  }
747  }
748  send_nbr_elem_dof.AddConnections(
749  send_el_off[fn] + i, ldofs, ldofs.Size());
750  }
751  }
753  send_nbr_elem_dof.ShiftUpI();
754 
755  // convert the ldof indices in send_nbr_elem_dof
756  int *send_J = send_nbr_elem_dof.GetJ();
757  for (int fn = 0, j = 0; fn < num_face_nbrs; fn++)
758  {
759  int num_ldofs = send_face_nbr_ldof.RowSize(fn);
760  int *ldofs = send_face_nbr_ldof.GetRow(fn);
761  int j_end = send_I[send_el_off[fn+1]];
762 
763  for (int i = 0; i < num_ldofs; i++)
764  {
765  ldof_marker[ldofs[i]] = i;
766  }
767 
768  for ( ; j < j_end; j++)
769  {
770  send_J[j] = ldof_marker[send_J[j]];
771  }
772  }
773 
774  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
776 
777  // send/receive the J arrays of send_nbr_elem_dof/face_nbr_element_dof,
778  // respectively (they contain the element dofs in enumeration local for
779  // the face-neighbor pair)
780  int *recv_J = face_nbr_element_dof.GetJ();
781  for (int fn = 0; fn < num_face_nbrs; fn++)
782  {
783  int nbr_rank = pmesh->GetFaceNbrRank(fn);
784  int tag = 0;
785 
786  MPI_Isend(send_J + send_I[send_el_off[fn]],
787  send_I[send_el_off[fn+1]] - send_I[send_el_off[fn]],
788  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
789 
790  MPI_Irecv(recv_J + recv_I[recv_el_off[fn]],
791  recv_I[recv_el_off[fn+1]] - recv_I[recv_el_off[fn]],
792  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
793  }
794 
795  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
796 
797  // shift the J array of face_nbr_element_dof
798  for (int fn = 0, j = 0; fn < num_face_nbrs; fn++)
799  {
800  int shift = face_nbr_ldof.GetI()[fn];
801  int j_end = recv_I[recv_el_off[fn+1]];
802 
803  for ( ; j < j_end; j++)
804  {
805  recv_J[j] += shift;
806  }
807  }
808 
809  MPI_Waitall(num_face_nbrs, send_requests, statuses);
810 
811  // send/receive the J arrays of send_face_nbr_ldof/face_nbr_ldof,
812  // respectively
813  send_J = send_face_nbr_ldof.GetJ();
814  for (int fn = 0; fn < num_face_nbrs; fn++)
815  {
816  int nbr_rank = pmesh->GetFaceNbrRank(fn);
817  int tag = 0;
818 
819  MPI_Isend(send_face_nbr_ldof.GetRow(fn),
821  MPI_INT, nbr_rank, tag, MyComm, &send_requests[fn]);
822 
823  MPI_Irecv(face_nbr_ldof.GetRow(fn),
825  MPI_INT, nbr_rank, tag, MyComm, &recv_requests[fn]);
826  }
827 
828  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
829  MPI_Waitall(num_face_nbrs, send_requests, statuses);
830 
831  // send my_dof_offset (i.e. my_ldof_offset) to face neighbors and receive
832  // their offset in dof_face_nbr_offsets, used to define face_nbr_glob_dof_map
834  Array<HYPRE_Int> dof_face_nbr_offsets(num_face_nbrs);
835  HYPRE_Int my_dof_offset = GetMyDofOffset();
836  for (int fn = 0; fn < num_face_nbrs; fn++)
837  {
838  int nbr_rank = pmesh->GetFaceNbrRank(fn);
839  int tag = 0;
840 
841  MPI_Isend(&my_dof_offset, 1, HYPRE_MPI_INT, nbr_rank, tag,
842  MyComm, &send_requests[fn]);
843 
844  MPI_Irecv(&dof_face_nbr_offsets[fn], 1, HYPRE_MPI_INT, nbr_rank, tag,
845  MyComm, &recv_requests[fn]);
846  }
847 
848  MPI_Waitall(num_face_nbrs, recv_requests, statuses);
849 
850  // set the array face_nbr_glob_dof_map which holds the global ldof indices of
851  // the face-neighbor dofs
852  for (int fn = 0, j = 0; fn < num_face_nbrs; fn++)
853  {
854  for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
856  dof_face_nbr_offsets[fn] + face_nbr_ldof.GetJ()[j];
857  }
858 
859  MPI_Waitall(num_face_nbrs, send_requests, statuses);
860 
861  delete [] statuses;
862  delete [] requests;
863 }
864 
866  int i, Array<int> &vdofs) const
867 {
868  face_nbr_element_dof.GetRow(i, vdofs);
869 }
870 
872 {
873  // Works for NC mesh where 'i' is an index returned by
874  // ParMesh::GetSharedFace() such that i >= Mesh::GetNumFaces(), i.e. 'i' is
875  // the index of a ghost.
876  MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
877  int el1, el2, inf1, inf2;
878  pmesh->GetFaceElements(i, &el1, &el2);
879  el2 = -1 - el2;
880  pmesh->GetFaceInfos(i, &inf1, &inf2);
881  MFEM_ASSERT(0 <= el2 && el2 < face_nbr_element_dof.Size(), "");
882  const int nd = face_nbr_element_dof.RowSize(el2);
883  const int *vol_vdofs = face_nbr_element_dof.GetRow(el2);
884  const Element *face_nbr_el = pmesh->face_nbr_elements[el2];
885  const int geom = face_nbr_el->GetGeometryType();
886  const int face_dim = Geometry::Dimension[geom]-1;
887 
888  fec->SubDofOrder(geom, face_dim, inf2, vdofs);
889  // Convert local dofs to local vdofs.
890  Ordering::DofsToVDofs<Ordering::byNODES>(nd/vdim, vdim, vdofs);
891  // Convert local vdofs to global vdofs.
892  for (int j = 0; j < vdofs.Size(); j++)
893  {
894  const int ldof = vdofs[j];
895  vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
896  }
897 }
898 
900 {
901  const FiniteElement *FE =
903  pmesh->face_nbr_elements[i]->GetGeometryType());
904 
905  if (NURBSext)
906  {
907  mfem_error("ParFiniteElementSpace::GetFaceNbrFE"
908  " does not support NURBS!");
909  }
910  return FE;
911 }
912 
914 {
915  // Works in tandem with GetFaceNbrFaceVDofs() defined above.
916  MFEM_ASSERT(Nonconforming() && !NURBSext, "");
917  const int geom = (pmesh->Dimension() == 2) ?
919  return fec->FiniteElementForGeometry(geom);
920 }
921 
923 {
924  hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
925  hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
926  hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
927  P -> StealData();
928  dof_offsets.LoseData();
929  tdof_offsets.LoseData();
930 }
931 
932 void ParFiniteElementSpace::ConstructTrueDofs()
933 {
934  int i, gr, n = GetVSize();
935  GroupTopology &gt = pmesh->gtopo;
936  gcomm = new GroupCommunicator(gt);
937  Table &group_ldof = gcomm->GroupLDofTable();
938 
939  GetGroupComm(*gcomm, 1, &ldof_sign);
940 
941  // Define ldof_group and mark ldof_ltdof with
942  // -1 for ldof that is ours
943  // -2 for ldof that is in a group with another master
944  ldof_group.SetSize(n);
945  ldof_ltdof.SetSize(n);
946  ldof_group = 0;
947  ldof_ltdof = -1;
948 
949  for (gr = 1; gr < group_ldof.Size(); gr++)
950  {
951  const int *ldofs = group_ldof.GetRow(gr);
952  const int nldofs = group_ldof.RowSize(gr);
953  for (i = 0; i < nldofs; i++)
954  {
955  ldof_group[ldofs[i]] = gr;
956  }
957 
958  if (!gt.IAmMaster(gr)) // we are not the master
959  {
960  for (i = 0; i < nldofs; i++)
961  {
962  ldof_ltdof[ldofs[i]] = -2;
963  }
964  }
965  }
966 
967  // count ltdof_size
968  ltdof_size = 0;
969  for (i = 0; i < n; i++)
970  {
971  if (ldof_ltdof[i] == -1)
972  {
973  ldof_ltdof[i] = ltdof_size++;
974  }
975  }
976  gcomm->SetLTDofTable(ldof_ltdof);
977 
978  // have the group masters broadcast their ltdofs to the rest of the group
979  gcomm->Bcast(ldof_ltdof);
980 }
981 
982 void ParFiniteElementSpace::ConstructTrueNURBSDofs()
983 {
984  int n = GetVSize();
985  GroupTopology &gt = pNURBSext()->gtopo;
986  gcomm = new GroupCommunicator(gt);
987 
988  // pNURBSext()->ldof_group is for scalar space!
989  if (vdim == 1)
990  {
991  ldof_group.MakeRef(pNURBSext()->ldof_group);
992  }
993  else
994  {
995  const int *scalar_ldof_group = pNURBSext()->ldof_group;
996  ldof_group.SetSize(n);
997  for (int i = 0; i < n; i++)
998  {
999  ldof_group[i] = scalar_ldof_group[VDofToDof(i)];
1000  }
1001  }
1002 
1003  gcomm->Create(ldof_group);
1004 
1005  // ldof_sign.SetSize(n);
1006  // ldof_sign = 1;
1007  ldof_sign.DeleteAll();
1008 
1009  ltdof_size = 0;
1010  ldof_ltdof.SetSize(n);
1011  for (int i = 0; i < n; i++)
1012  {
1013  if (gt.IAmMaster(ldof_group[i]))
1014  {
1015  ldof_ltdof[i] = ltdof_size;
1016  ltdof_size++;
1017  }
1018  else
1019  {
1020  ldof_ltdof[i] = -2;
1021  }
1022  }
1023  gcomm->SetLTDofTable(ldof_ltdof);
1024 
1025  // have the group masters broadcast their ltdofs to the rest of the group
1026  gcomm->Bcast(ldof_ltdof);
1027 }
1028 
1029 inline int decode_dof(int dof, double& sign)
1030 {
1031  return (dof >= 0) ? (sign = 1, dof) : (sign = -1, (-1 - dof));
1032 }
1033 
1034 const int INVALID_DOF = INT_MAX;
1035 
1036 static void MaskSlaveDofs(Array<int> &slave_dofs, const DenseMatrix &pm,
1037  const FiniteElementCollection *fec)
1038 {
1039  // If a slave edge/face shares a vertex with its master, that vertex is
1040  // not really a slave. In serial this is easily taken care of with the
1041  // statement "if (mdof != sdof)" inside AddDependencies. In parallel this
1042  // doesn't work as 'mdof' and 'sdof' may be on different processors.
1043  // Here we detect these cases from the slave point matrix.
1044 
1045  int k, i;
1046  int nv = fec->DofForGeometry(Geometry::POINT);
1047  int ne = fec->DofForGeometry(Geometry::SEGMENT);
1048 
1049  if (pm.Width() == 2) // edge: exclude master endpoint vertices
1050  {
1051  if (nv)
1052  {
1053  for (i = 0; i < 2; i++)
1054  {
1055  if (pm(0, i) == 0.0 || pm(0, i) == 1.0)
1056  {
1057  for (k = 0; k < nv; k++) { slave_dofs[i*nv + k] = INVALID_DOF; }
1058  }
1059  }
1060  }
1061  }
1062  else // face: exclude master corner vertices + full edges if present
1063  {
1064  MFEM_ASSERT(pm.Width() == 4, "");
1065 
1066  bool corner[4];
1067  for (i = 0; i < 4; i++)
1068  {
1069  double x = pm(0,i), y = pm(1,i);
1070  corner[i] = ((x == 0.0 || x == 1.0) && (y == 0.0 || y == 1.0));
1071  }
1072  if (nv)
1073  {
1074  for (i = 0; i < 4; i++)
1075  {
1076  if (corner[i])
1077  {
1078  for (k = 0; k < nv; k++) { slave_dofs[i*nv + k] = INVALID_DOF; }
1079  }
1080  }
1081  }
1082  if (ne)
1083  {
1084  for (i = 0; i < 4; i++)
1085  {
1086  if (corner[i] && corner[(i+1) % 4])
1087  {
1088  for (k = 0; k < ne; k++)
1089  {
1090  slave_dofs[4*nv + i*ne + k] = INVALID_DOF;
1091  }
1092  }
1093  }
1094  }
1095  }
1096 }
1097 
1098 void ParFiniteElementSpace
1099 ::AddSlaveDependencies(DepList deps[], int master_rank,
1100  const Array<int> &master_dofs, int master_ndofs,
1101  const Array<int> &slave_dofs, DenseMatrix& I) const
1102 {
1103  // make each slave DOF dependent on all master DOFs
1104  for (int i = 0; i < slave_dofs.Size(); i++)
1105  {
1106  double ss, ms;
1107  int sdof = decode_dof(slave_dofs[i], ss);
1108  if (sdof == INVALID_DOF) { continue; }
1109 
1110  for (int vd = 0; vd < vdim; vd++)
1111  {
1112  DepList &dl = deps[DofToVDof(sdof, vd, ndofs)];
1113  if (dl.type < 2) // slave dependencies override 1-to-1 dependencies
1114  {
1115  Array<Dependency> tmp_list; // TODO remove, precalculate list size
1116  for (int j = 0; j < master_dofs.Size(); j++)
1117  {
1118  double coef = I(i, j);
1119  if (std::abs(coef) > 1e-12)
1120  {
1121  int mdof = decode_dof(master_dofs[j], ms);
1122  int mvdof = DofToVDof(mdof, vd, master_ndofs);
1123  tmp_list.Append(Dependency(master_rank, mvdof, coef*ms*ss));
1124  }
1125  }
1126  dl.type = 2;
1127  tmp_list.Copy(dl.list);
1128  }
1129  }
1130  }
1131 }
1132 
1133 void ParFiniteElementSpace
1134 ::Add1To1Dependencies(DepList deps[], int owner_rank,
1135  const Array<int> &owner_dofs, int owner_ndofs,
1136  const Array<int> &dependent_dofs) const
1137 {
1138  MFEM_ASSERT(owner_dofs.Size() == dependent_dofs.Size(), "");
1139 
1140  for (int vd = 0; vd < vdim; vd++)
1141  {
1142  for (int i = 0; i < owner_dofs.Size(); i++)
1143  {
1144  double osign, dsign;
1145  int odof = decode_dof(owner_dofs[i], osign);
1146  int ddof = decode_dof(dependent_dofs[i], dsign);
1147  if (odof == INVALID_DOF || ddof == INVALID_DOF) { continue; }
1148 
1149  int ovdof = DofToVDof(odof, vd, owner_ndofs);
1150  int dvdof = DofToVDof(ddof, vd, ndofs);
1151 
1152  DepList &dl = deps[dvdof];
1153  if (dl.type == 0)
1154  {
1155  dl.type = 1;
1156  dl.list.Append(Dependency(owner_rank, ovdof, osign*dsign));
1157  }
1158  else if (dl.type == 1 && dl.list[0].rank > owner_rank)
1159  {
1160  // 1-to-1 dependency already exists but lower rank takes precedence
1161  dl.list[0] = Dependency(owner_rank, ovdof, osign*dsign);
1162  }
1163  }
1164  }
1165 }
1166 
1167 void ParFiniteElementSpace
1168 ::ReorderFaceDofs(Array<int> &dofs, int orient) const
1169 {
1170  Array<int> tmp;
1171  dofs.Copy(tmp);
1172 
1173  // NOTE: assuming quad faces here
1174  int nv = fec->DofForGeometry(Geometry::POINT);
1176  const int* ind = fec->DofOrderForOrientation(Geometry::SQUARE, orient);
1177 
1178  int ve_dofs = 4*(nv + ne);
1179  for (int i = 0; i < ve_dofs; i++)
1180  {
1181  dofs[i] = INVALID_DOF;
1182  }
1183 
1184  int f_dofs = dofs.Size() - ve_dofs;
1185  for (int i = 0; i < f_dofs; i++)
1186  {
1187  if (ind[i] >= 0)
1188  {
1189  dofs[ve_dofs + i] = tmp[ve_dofs + ind[i]];
1190  }
1191  else
1192  {
1193  dofs[ve_dofs + i] = -1 - tmp[ve_dofs + (-1 - ind[i])];
1194  }
1195  }
1196 }
1197 
1198 void ParFiniteElementSpace::GetDofs(int type, int index, Array<int>& dofs) const
1199 {
1200  // helper to get vertex, edge or face DOFs
1201  switch (type)
1202  {
1203  case 0: GetVertexDofs(index, dofs); break;
1204  case 1: GetEdgeDofs(index, dofs); break;
1205  default: GetFaceDofs(index, dofs);
1206  }
1207 }
1208 
1209 void ParFiniteElementSpace::GetParallelConformingInterpolation() const
1210 {
1211  ParNCMesh* pncmesh = pmesh->pncmesh;
1212 
1213  bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
1214 
1215  // *** STEP 1: exchange shared vertex/edge/face DOFs with neighbors ***
1216 
1217  NeighborDofMessage::Map send_dofs, recv_dofs;
1218 
1219  // prepare neighbor DOF messages for shared vertices/edges/faces
1220  if (!dg)
1221  {
1222  for (int type = 0; type < 3; type++)
1223  {
1224  const NCMesh::NCList &list = pncmesh->GetSharedList(type);
1225  Array<int> dofs;
1226 
1227  int cs = list.conforming.size(), ms = list.masters.size();
1228  for (int i = 0; i < cs+ms; i++)
1229  {
1230  // loop through all (shared) conforming+master vertices/edges/faces
1231  const NCMesh::MeshId& id =
1232  (i < cs) ? (const NCMesh::MeshId&) list.conforming[i]
1233  /* */ : (const NCMesh::MeshId&) list.masters[i-cs];
1234 
1235  int owner = pncmesh->GetOwner(type, id.index), gsize;
1236  if (owner == MyRank)
1237  {
1238  // we own a shared v/e/f, send its DOFs to others in group
1239  GetDofs(type, id.index, dofs);
1240  const int *group = pncmesh->GetGroup(type, id.index, gsize);
1241  for (int j = 0; j < gsize; j++)
1242  {
1243  if (group[j] != MyRank)
1244  {
1245  NeighborDofMessage &send_msg = send_dofs[group[j]];
1246  send_msg.Init(pncmesh, fec, ndofs);
1247  send_msg.AddDofs(type, id, dofs);
1248  }
1249  }
1250  }
1251  else
1252  {
1253  // we don't own this v/e/f and expect to receive DOFs for it
1254  recv_dofs[owner].Init(pncmesh, fec, 0);
1255  }
1256  }
1257  }
1258 
1259  // send/receive all DOF messages
1260  NeighborDofMessage::IsendAll(send_dofs, MyComm);
1261  NeighborDofMessage::RecvAll(recv_dofs, MyComm);
1262  }
1263 
1264  // *** STEP 2: build dependency lists ***
1265 
1266  int num_dofs = ndofs * vdim;
1267  DepList* deps = new DepList[num_dofs]; // NOTE: 'deps' is over vdofs
1268 
1269  Array<int> master_dofs, slave_dofs;
1270  Array<int> owner_dofs, my_dofs;
1271 
1272  if (!dg)
1273  {
1274  // loop through *all* master edges/faces, constrain their slaves
1275  for (int type = 1; type < 3; type++)
1276  {
1277  const NCMesh::NCList &list = (type > 1) ? pncmesh->GetFaceList()
1278  /* */ : pncmesh->GetEdgeList();
1279  if (!list.masters.size()) { continue; }
1280 
1281  IsoparametricTransformation T;
1282  if (type > 1) { T.SetFE(&QuadrilateralFE); }
1283  else { T.SetFE(&SegmentFE); }
1284 
1285  int geom = (type > 1) ? Geometry::SQUARE : Geometry::SEGMENT;
1286  const FiniteElement* fe = fec->FiniteElementForGeometry(geom);
1287  if (!fe) { continue; }
1288 
1289  DenseMatrix I(fe->GetDof());
1290 
1291  // process masters that we own or that affect our edges/faces
1292  for (unsigned mi = 0; mi < list.masters.size(); mi++)
1293  {
1294  const NCMesh::Master &mf = list.masters[mi];
1295  if (!pncmesh->RankInGroup(type, mf.index, MyRank)) { continue; }
1296 
1297  // get master DOFs
1298  int master_ndofs, master_rank = pncmesh->GetOwner(type, mf.index);
1299  if (master_rank == MyRank)
1300  {
1301  GetDofs(type, mf.index, master_dofs);
1302  master_ndofs = ndofs;
1303  }
1304  else
1305  {
1306  recv_dofs[master_rank].GetDofs(type, mf, master_dofs,
1307  master_ndofs);
1308  }
1309 
1310  if (!master_dofs.Size()) { continue; }
1311 
1312  // constrain slaves that exist in our mesh
1313  for (int si = mf.slaves_begin; si < mf.slaves_end; si++)
1314  {
1315  const NCMesh::Slave &sf = list.slaves[si];
1316  if (pncmesh->IsGhost(type, sf.index)) { continue; }
1317 
1318  GetDofs(type, sf.index, slave_dofs);
1319  if (!slave_dofs.Size()) { continue; }
1320 
1321  sf.OrientedPointMatrix(T.GetPointMat());
1322  T.FinalizeTransformation();
1323  fe->GetLocalInterpolation(T, I);
1324 
1325  // make each slave DOF dependent on all master DOFs
1326  MaskSlaveDofs(slave_dofs, T.GetPointMat(), fec);
1327  AddSlaveDependencies(deps, master_rank, master_dofs, master_ndofs,
1328  slave_dofs, I);
1329  }
1330 
1331  // special case for master edges that we don't own but still exist
1332  // in our mesh: this is a conforming-like situation, create 1-to-1
1333  // deps
1334  if (master_rank != MyRank && !pncmesh->IsGhost(type, mf.index))
1335  {
1336  GetDofs(type, mf.index, my_dofs);
1337  Add1To1Dependencies(deps, master_rank, master_dofs, master_ndofs,
1338  my_dofs);
1339  }
1340  }
1341  }
1342 
1343  // add one-to-one dependencies between shared conforming verts/edges/faces
1344  for (int type = 0; type < 3; type++)
1345  {
1346  const NCMesh::NCList &list = pncmesh->GetSharedList(type);
1347  for (unsigned i = 0; i < list.conforming.size(); i++)
1348  {
1349  const NCMesh::MeshId &id = list.conforming[i];
1350  GetDofs(type, id.index, my_dofs);
1351 
1352  int owner_ndofs, owner = pncmesh->GetOwner(type, id.index);
1353  if (owner != MyRank)
1354  {
1355  recv_dofs[owner].GetDofs(type, id, owner_dofs, owner_ndofs);
1356  if (type == 2)
1357  {
1358  int fo = pncmesh->GetFaceOrientation(id.index);
1359  ReorderFaceDofs(owner_dofs, fo);
1360  }
1361  Add1To1Dependencies(deps, owner, owner_dofs, owner_ndofs,
1362  my_dofs);
1363  }
1364  else
1365  {
1366  // we own this v/e/f, assert ownership of the DOFs
1367  Add1To1Dependencies(deps, owner, my_dofs, ndofs, my_dofs);
1368  }
1369  }
1370  }
1371  }
1372 
1373  // *** STEP 3: request P matrix rows that we need from neighbors ***
1374 
1375  NeighborRowRequest::Map send_requests, recv_requests;
1376 
1377  // copy communication topology from the DOF messages
1378  NeighborDofMessage::Map::iterator it;
1379  for (it = send_dofs.begin(); it != send_dofs.end(); ++it)
1380  {
1381  recv_requests[it->first];
1382  }
1383  for (it = recv_dofs.begin(); it != recv_dofs.end(); ++it)
1384  {
1385  send_requests[it->first];
1386  }
1387 
1388  // request rows we depend on
1389  for (int i = 0; i < num_dofs; i++)
1390  {
1391  const DepList &dl = deps[i];
1392  for (int j = 0; j < dl.list.Size(); j++)
1393  {
1394  const Dependency &dep = dl.list[j];
1395  if (dep.rank != MyRank)
1396  {
1397  send_requests[dep.rank].RequestRow(dep.dof);
1398  }
1399  }
1400  }
1401  NeighborRowRequest::IsendAll(send_requests, MyComm);
1402 
1403  // *** STEP 4: iteratively build the P matrix ***
1404 
1405  // DOFs that stayed independent or are ours are true DOFs
1406  ltdof_size = 0;
1407  for (int i = 0; i < num_dofs; i++)
1408  {
1409  if (deps[i].IsTrueDof(MyRank)) { ltdof_size++; }
1410  }
1411 
1412  GenerateGlobalOffsets();
1413 
1414  HYPRE_Int glob_true_dofs = tdof_offsets.Last();
1415  HYPRE_Int glob_cdofs = dof_offsets.Last();
1416 
1417  // create the local part (local rows) of the P matrix
1418 #ifdef HYPRE_BIGINT
1419  MFEM_VERIFY(glob_true_dofs >= 0 && glob_true_dofs < (1ll << 31),
1420  "64bit matrix size not supported yet in non-conforming P.");
1421 #else
1422  MFEM_VERIFY(glob_true_dofs >= 0,
1423  "overflow of non-conforming P matrix columns.");
1424 #endif
1425  SparseMatrix localP(num_dofs, glob_true_dofs); // TODO bigint
1426 
1427  // initialize the R matrix (also parallel but block-diagonal)
1428  R = new SparseMatrix(ltdof_size, num_dofs);
1429 
1430  Array<bool> finalized(num_dofs);
1431  finalized = false;
1432 
1433  // put identity in P and R for true DOFs, set ldof_ltdof
1434  HYPRE_Int my_tdof_offset = GetMyTDofOffset();
1435  ldof_ltdof.SetSize(num_dofs);
1436  ldof_ltdof = -1;
1437  for (int i = 0, true_dof = 0; i < num_dofs; i++)
1438  {
1439  if (deps[i].IsTrueDof(MyRank))
1440  {
1441  localP.Add(i, my_tdof_offset + true_dof, 1.0);
1442  R->Add(true_dof, i, 1.0);
1443  finalized[i] = true;
1444  ldof_ltdof[i] = true_dof;
1445  true_dof++;
1446  }
1447  }
1448 
1449  Array<int> cols;
1450  Vector srow;
1451 
1452  NeighborRowReply::Map recv_replies;
1453  std::list<NeighborRowReply::Map> send_replies;
1454 
1455  NeighborRowRequest::RecvAll(recv_requests, MyComm); // finish Step 3
1456 
1457  int num_finalized = ltdof_size;
1458  while (1)
1459  {
1460  // finalize all rows that can currently be finalized
1461  bool done;
1462  do
1463  {
1464  done = true;
1465  for (int dof = 0, i; dof < num_dofs; dof++)
1466  {
1467  if (finalized[dof]) { continue; }
1468 
1469  // check that rows of all constraining DOFs are available
1470  const DepList &dl = deps[dof];
1471  for (i = 0; i < dl.list.Size(); i++)
1472  {
1473  const Dependency &dep = dl.list[i];
1474  if (dep.rank == MyRank)
1475  {
1476  if (!finalized[dep.dof]) { break; }
1477  }
1478  else if (!recv_replies[dep.rank].HaveRow(dep.dof))
1479  {
1480  break;
1481  }
1482  }
1483  if (i < dl.list.Size()) { continue; }
1484 
1485  // form a linear combination of rows that 'dof' depends on
1486  for (i = 0; i < dl.list.Size(); i++)
1487  {
1488  const Dependency &dep = dl.list[i];
1489  if (dep.rank == MyRank)
1490  {
1491  localP.GetRow(dep.dof, cols, srow);
1492  }
1493  else
1494  {
1495  recv_replies[dep.rank].GetRow(dep.dof, cols, srow);
1496  }
1497  srow *= dep.coef;
1498  localP.AddRow(dof, cols, srow);
1499  }
1500 
1501  finalized[dof] = true;
1502  num_finalized++;
1503  done = false;
1504  }
1505  }
1506  while (!done);
1507 
1508  // send rows that are requested by neighbors and are available
1509  send_replies.push_back(NeighborRowReply::Map());
1510 
1511  NeighborRowRequest::Map::iterator it;
1512  for (it = recv_requests.begin(); it != recv_requests.end(); ++it)
1513  {
1514  NeighborRowRequest &req = it->second;
1515  std::set<int>::iterator row;
1516  for (row = req.rows.begin(); row != req.rows.end(); )
1517  {
1518  if (finalized[*row])
1519  {
1520  localP.GetRow(*row, cols, srow);
1521  send_replies.back()[it->first].AddRow(*row, cols, srow);
1522  req.rows.erase(row++);
1523  }
1524  else { ++row; }
1525  }
1526  }
1527  NeighborRowReply::IsendAll(send_replies.back(), MyComm);
1528 
1529  // are we finished?
1530  if (num_finalized >= num_dofs) { break; }
1531 
1532  // wait for a reply from neighbors
1533  int rank, size;
1534  NeighborRowReply::Probe(rank, size, MyComm);
1535  recv_replies[rank].Recv(rank, size, MyComm);
1536 
1537  // there may be more, receive all replies available
1538  while (NeighborRowReply::IProbe(rank, size, MyComm))
1539  {
1540  recv_replies[rank].Recv(rank, size, MyComm);
1541  }
1542  }
1543 
1544  delete [] deps;
1545  localP.Finalize();
1546 
1547  // create the parallel matrix P
1548 #ifndef HYPRE_BIGINT
1549  P = new HypreParMatrix(MyComm, num_dofs, glob_cdofs, glob_true_dofs,
1550  localP.GetI(), localP.GetJ(), localP.GetData(),
1551  dof_offsets.GetData(), tdof_offsets.GetData());
1552 #else
1553  (void) glob_cdofs;
1554  MFEM_ABORT("HYPRE_BIGINT not supported yet.");
1555 #endif
1556 
1557  R->Finalize();
1558 
1559  // make sure we can discard all send buffers
1561  NeighborRowRequest::WaitAllSent(send_requests);
1562 
1563  for (std::list<NeighborRowReply::Map>::iterator
1564  it = send_replies.begin(); it != send_replies.end(); ++it)
1565  {
1567  }
1568 }
1569 
1571 {
1572  // TODO: we should refactor the code to remove duplication in this
1573  // and the previous function
1574 
1575  if (Conforming()) { return NULL; }
1576 
1577  ParNCMesh* pncmesh = pmesh->pncmesh;
1578 
1579  bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
1580 
1581  // *** STEP 1: exchange shared vertex/edge/face DOFs with neighbors ***
1582 
1583  NeighborDofMessage::Map send_dofs, recv_dofs;
1584 
1585  // prepare neighbor DOF messages for shared vertices/edges/faces
1586  if (!dg)
1587  {
1588  for (int type = 0; type < 3; type++)
1589  {
1590  const NCMesh::NCList &list = pncmesh->GetSharedList(type);
1591  Array<int> dofs;
1592 
1593  int cs = list.conforming.size(), ms = list.masters.size();
1594  for (int i = 0; i < cs+ms; i++)
1595  {
1596  // loop through all (shared) conforming+master vertices/edges/faces
1597  const NCMesh::MeshId& id =
1598  (i < cs) ? (const NCMesh::MeshId&) list.conforming[i]
1599  /* */ : (const NCMesh::MeshId&) list.masters[i-cs];
1600 
1601  int owner = pncmesh->GetOwner(type, id.index), gsize;
1602  if (owner == MyRank)
1603  {
1604  // we own a shared v/e/f, send its DOFs to others in group
1605  GetDofs(type, id.index, dofs);
1606  const int *group = pncmesh->GetGroup(type, id.index, gsize);
1607  for (int j = 0; j < gsize; j++)
1608  {
1609  if (group[j] != MyRank)
1610  {
1611  NeighborDofMessage &send_msg = send_dofs[group[j]];
1612  send_msg.Init(pncmesh, fec, ndofs);
1613  send_msg.AddDofs(type, id, dofs);
1614  }
1615  }
1616  }
1617  else
1618  {
1619  // we don't own this v/e/f and expect to receive DOFs for it
1620  recv_dofs[owner].Init(pncmesh, fec, 0);
1621  }
1622  }
1623  }
1624 
1625  // send/receive all DOF messages
1626  NeighborDofMessage::IsendAll(send_dofs, MyComm);
1627  NeighborDofMessage::RecvAll(recv_dofs, MyComm);
1628  }
1629 
1630  // *** STEP 2: build dependency lists ***
1631 
1632  int num_dofs = ndofs * vdim;
1633  DepList* deps = new DepList[num_dofs]; // NOTE: 'deps' is over vdofs
1634 
1635  // Array<int> master_dofs, slave_dofs;
1636  Array<int> owner_dofs, my_dofs;
1637 
1638  if (!dg)
1639  {
1640  // add one-to-one dependencies between shared conforming verts/edges/faces
1641  for (int type = 0; type < 3; type++)
1642  {
1643  const NCMesh::NCList &list = pncmesh->GetSharedList(type);
1644  for (unsigned i = 0; i < list.conforming.size(); i++)
1645  {
1646  const NCMesh::MeshId &id = list.conforming[i];
1647  GetDofs(type, id.index, my_dofs);
1648 
1649  int owner_ndofs, owner = pncmesh->GetOwner(type, id.index);
1650  if (owner != MyRank)
1651  {
1652  recv_dofs[owner].GetDofs(type, id, owner_dofs, owner_ndofs);
1653  if (type == 2)
1654  {
1655  int fo = pncmesh->GetFaceOrientation(id.index);
1656  ReorderFaceDofs(owner_dofs, fo);
1657  }
1658  Add1To1Dependencies(deps, owner, owner_dofs, owner_ndofs,
1659  my_dofs);
1660  }
1661  else
1662  {
1663  // we own this v/e/f, assert ownership of the DOFs
1664  Add1To1Dependencies(deps, owner, my_dofs, ndofs, my_dofs);
1665  }
1666  }
1667  }
1668  }
1669 
1670  // *** STEP 3: request P matrix rows that we need from neighbors ***
1671 
1672  NeighborRowRequest::Map send_requests, recv_requests;
1673 
1674  // copy communication topology from the DOF messages
1675  NeighborDofMessage::Map::iterator it;
1676  for (it = send_dofs.begin(); it != send_dofs.end(); ++it)
1677  {
1678  recv_requests[it->first];
1679  }
1680  for (it = recv_dofs.begin(); it != recv_dofs.end(); ++it)
1681  {
1682  send_requests[it->first];
1683  }
1684 
1685  // request rows we depend on
1686  for (int i = 0; i < num_dofs; i++)
1687  {
1688  const DepList &dl = deps[i];
1689  for (int j = 0; j < dl.list.Size(); j++)
1690  {
1691  const Dependency &dep = dl.list[j];
1692  if (dep.rank != MyRank)
1693  {
1694  send_requests[dep.rank].RequestRow(dep.dof);
1695  }
1696  }
1697  }
1698  NeighborRowRequest::IsendAll(send_requests, MyComm);
1699 
1700  // *** STEP 4: iteratively build the P matrix ***
1701 
1702  // DOFs that stayed independent or are ours are true DOFs
1703  HYPRE_Int ltdof_sz = 0;
1704  for (int i = 0; i < num_dofs; i++)
1705  {
1706  if (deps[i].IsTrueDof(MyRank)) { ltdof_sz++; }
1707  }
1708 
1709  Array<HYPRE_Int> tdof_off, *offsets[1] = { &tdof_off };
1710  pmesh->GenerateOffsets(1, &ltdof_sz, offsets);
1711 
1712  HYPRE_Int glob_true_dofs = tdof_off.Last();
1713  HYPRE_Int glob_cdofs = dof_offsets.Last();
1714 
1715  // create the local part (local rows) of the P matrix
1716 #ifdef HYPRE_BIGINT
1717  MFEM_VERIFY(glob_true_dofs >= 0 && glob_true_dofs < (1ll << 31),
1718  "64bit matrix size not supported yet in non-conforming P.");
1719 #else
1720  MFEM_VERIFY(glob_true_dofs >= 0,
1721  "overflow of non-conforming P matrix columns.");
1722 #endif
1723  // TODO bigint
1724  SparseMatrix localP(num_dofs, internal::to_int(glob_true_dofs));
1725 
1726  Array<bool> finalized(num_dofs);
1727  finalized = false;
1728 
1729  // put identity in P for true DOFs
1730  HYPRE_Int my_tdof_offset = HYPRE_AssumedPartitionCheck() ?
1731  tdof_off[0] : tdof_off[MyRank];
1732  for (int i = 0, true_dof = 0; i < num_dofs; i++)
1733  {
1734  if (deps[i].IsTrueDof(MyRank))
1735  {
1736  localP.Add(i, my_tdof_offset + true_dof, 1.0);
1737  finalized[i] = true;
1738  true_dof++;
1739  }
1740  }
1741 
1742  Array<int> cols;
1743  Vector srow;
1744 
1745  NeighborRowReply::Map recv_replies;
1746  std::list<NeighborRowReply::Map> send_replies;
1747 
1748  NeighborRowRequest::RecvAll(recv_requests, MyComm); // finish Step 3
1749 
1750  int num_finalized = ltdof_sz;
1751  while (1)
1752  {
1753  // finalize all rows that can currently be finalized
1754  bool done;
1755  do
1756  {
1757  done = true;
1758  for (int dof = 0, i; dof < num_dofs; dof++)
1759  {
1760  if (finalized[dof]) { continue; }
1761 
1762  // check that rows of all constraining DOFs are available
1763  const DepList &dl = deps[dof];
1764  for (i = 0; i < dl.list.Size(); i++)
1765  {
1766  const Dependency &dep = dl.list[i];
1767  if (dep.rank == MyRank)
1768  {
1769  if (!finalized[dep.dof]) { break; }
1770  }
1771  else if (!recv_replies[dep.rank].HaveRow(dep.dof))
1772  {
1773  break;
1774  }
1775  }
1776  if (i < dl.list.Size()) { continue; }
1777 
1778  // form a linear combination of rows that 'dof' depends on
1779  for (i = 0; i < dl.list.Size(); i++)
1780  {
1781  const Dependency &dep = dl.list[i];
1782  if (dep.rank == MyRank)
1783  {
1784  localP.GetRow(dep.dof, cols, srow);
1785  }
1786  else
1787  {
1788  recv_replies[dep.rank].GetRow(dep.dof, cols, srow);
1789  }
1790  srow *= dep.coef;
1791  localP.AddRow(dof, cols, srow);
1792  }
1793 
1794  finalized[dof] = true;
1795  num_finalized++;
1796  done = false;
1797  }
1798  }
1799  while (!done);
1800 
1801  // send rows that are requested by neighbors and are available
1802  send_replies.push_back(NeighborRowReply::Map());
1803 
1804  NeighborRowRequest::Map::iterator it;
1805  for (it = recv_requests.begin(); it != recv_requests.end(); ++it)
1806  {
1807  NeighborRowRequest &req = it->second;
1808  std::set<int>::iterator row;
1809  for (row = req.rows.begin(); row != req.rows.end(); )
1810  {
1811  if (finalized[*row])
1812  {
1813  localP.GetRow(*row, cols, srow);
1814  send_replies.back()[it->first].AddRow(*row, cols, srow);
1815  req.rows.erase(row++);
1816  }
1817  else { ++row; }
1818  }
1819  }
1820  NeighborRowReply::IsendAll(send_replies.back(), MyComm);
1821 
1822  // are we finished?
1823  if (num_finalized >= num_dofs) { break; }
1824 
1825  // wait for a reply from neighbors
1826  int rank, size;
1827  NeighborRowReply::Probe(rank, size, MyComm);
1828  recv_replies[rank].Recv(rank, size, MyComm);
1829 
1830  // there may be more, receive all replies available
1831  while (NeighborRowReply::IProbe(rank, size, MyComm))
1832  {
1833  recv_replies[rank].Recv(rank, size, MyComm);
1834  }
1835  }
1836 
1837  delete [] deps;
1838  localP.Finalize();
1839 
1840  // create the parallel matrix P
1841  HypreParMatrix *PP;
1842 #ifndef HYPRE_BIGINT
1843  PP = new HypreParMatrix(MyComm, num_dofs, glob_cdofs, glob_true_dofs,
1844  localP.GetI(), localP.GetJ(), localP.GetData(),
1845  dof_offsets.GetData(), tdof_off.GetData());
1846 #else
1847  (void) glob_cdofs;
1848  PP = NULL;
1849  MFEM_ABORT("HYPRE_BIGINT not supported yet.");
1850 #endif
1851 
1852  // make sure we can discard all send buffers
1854  NeighborRowRequest::WaitAllSent(send_requests);
1855 
1856  for (std::list<NeighborRowReply::Map>::iterator
1857  it = send_replies.begin(); it != send_replies.end(); ++it)
1858  {
1860  }
1861  return PP;
1862 }
1863 
1864 
1865 static HYPRE_Int* make_i_array(int nrows)
1866 {
1867  HYPRE_Int *I = new HYPRE_Int[nrows+1];
1868  for (int i = 0; i <= nrows; i++) { I[i] = -1; }
1869  return I;
1870 }
1871 
1872 static HYPRE_Int* make_j_array(HYPRE_Int* I, int nrows)
1873 {
1874  int nnz = 0;
1875  for (int i = 0; i < nrows; i++)
1876  {
1877  if (I[i] >= 0) { nnz++; }
1878  }
1879  HYPRE_Int *J = new HYPRE_Int[nnz];
1880 
1881  I[nrows] = -1;
1882  for (int i = 0, k = 0; i <= nrows; i++)
1883  {
1884  HYPRE_Int col = I[i];
1885  I[i] = k;
1886  if (col >= 0) { J[k++] = col; }
1887  }
1888  return J;
1889 }
1890 
1891 HypreParMatrix*
1892 ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
1893  const Table* old_elem_dof)
1894 {
1895  MFEM_VERIFY(Nonconforming(), "Only supported for nonconforming meshes.");
1896  MFEM_VERIFY(old_dof_offsets.Size(), "ParFiniteElementSpace::Update needs to "
1897  "be called before ParFiniteElementSpace::RebalanceMatrix");
1898 
1899  HYPRE_Int old_offset = HYPRE_AssumedPartitionCheck()
1900  ? old_dof_offsets[0] : old_dof_offsets[MyRank];
1901 
1902  // send old DOFs of elements we used to own
1903  ParNCMesh* pncmesh = pmesh->pncmesh;
1904  pncmesh->SendRebalanceDofs(old_ndofs, *old_elem_dof, old_offset, this);
1905 
1906  Array<int> dofs;
1907  int vsize = GetVSize();
1908 
1909  const Array<int> &old_index = pncmesh->GetRebalanceOldIndex();
1910  MFEM_VERIFY(old_index.Size() == pmesh->GetNE(),
1911  "Mesh::Rebalance was not called before "
1912  "ParFiniteElementSpace::RebalanceMatrix");
1913 
1914  // prepare the local (diagonal) part of the matrix
1915  HYPRE_Int* i_diag = make_i_array(vsize);
1916  for (int i = 0; i < pmesh->GetNE(); i++)
1917  {
1918  if (old_index[i] >= 0) // we had this element before
1919  {
1920  const int* old_dofs = old_elem_dof->GetRow(old_index[i]);
1921  GetElementDofs(i, dofs);
1922 
1923  for (int vd = 0; vd < vdim; vd++)
1924  {
1925  for (int j = 0; j < dofs.Size(); j++)
1926  {
1927  int row = DofToVDof(dofs[j], vd);
1928  if (row < 0) { row = -1 - row; }
1929 
1930  int col = DofToVDof(old_dofs[j], vd, old_ndofs);
1931  if (col < 0) { col = -1 - col; }
1932 
1933  i_diag[row] = col;
1934  }
1935  }
1936  }
1937  }
1938  HYPRE_Int* j_diag = make_j_array(i_diag, vsize);
1939 
1940  // receive old DOFs for elements we obtained from others in Rebalance
1941  Array<int> new_elements;
1942  Array<long> old_remote_dofs;
1943  pncmesh->RecvRebalanceDofs(new_elements, old_remote_dofs);
1944 
1945  // create the offdiagonal part of the matrix
1946  HYPRE_Int* i_offd = make_i_array(vsize);
1947  for (int i = 0; i < new_elements.Size(); i++)
1948  {
1949  GetElementDofs(new_elements[i], dofs);
1950  const long* old_dofs = &old_remote_dofs[i * dofs.Size() * vdim];
1951 
1952  for (int vd = 0; vd < vdim; vd++)
1953  {
1954  for (int j = 0; j < dofs.Size(); j++)
1955  {
1956  int row = DofToVDof(dofs[j], vd);
1957  if (row < 0) { row = -1 - row; }
1958 
1959  if (i_diag[row] == i_diag[row+1]) // diag row empty?
1960  {
1961  i_offd[row] = old_dofs[j + vd * dofs.Size()];
1962  }
1963  }
1964  }
1965  }
1966  HYPRE_Int* j_offd = make_j_array(i_offd, vsize);
1967 
1968  // create the offd column map
1969  int offd_cols = i_offd[vsize];
1970  Array<Pair<HYPRE_Int, int> > cmap_offd(offd_cols);
1971  for (int i = 0; i < offd_cols; i++)
1972  {
1973  cmap_offd[i].one = j_offd[i];
1974  cmap_offd[i].two = i;
1975  }
1976  SortPairs<HYPRE_Int, int>(cmap_offd, offd_cols);
1977 
1978  HYPRE_Int* cmap = new HYPRE_Int[offd_cols];
1979  for (int i = 0; i < offd_cols; i++)
1980  {
1981  cmap[i] = cmap_offd[i].one;
1982  j_offd[cmap_offd[i].two] = i;
1983  }
1984 
1985  HypreParMatrix *M;
1986  M = new HypreParMatrix(MyComm, MyRank, NRanks, dof_offsets, old_dof_offsets,
1987  i_diag, j_diag, i_offd, j_offd, cmap, offd_cols);
1988  return M;
1989 }
1990 
1991 
1992 struct DerefDofMessage
1993 {
1994  std::vector<HYPRE_Int> dofs;
1995  MPI_Request request;
1996 };
1997 
1998 HypreParMatrix*
1999 ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
2000  const Table* old_elem_dof)
2001 {
2002  int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
2003 
2004  MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
2005  MFEM_VERIFY(old_dof_offsets[nrk], "Missing previous (finer) space.");
2006  MFEM_VERIFY(dof_offsets[nrk] <= old_dof_offsets[nrk],
2007  "Previous space is not finer.");
2008 
2009  // Note to the reader: please make sure you first read
2010  // FiniteElementSpace::RefinementMatrix, then
2011  // FiniteElementSpace::DerefinementMatrix, and only then this function.
2012  // You have been warned! :-)
2013 
2014  Array<int> dofs, old_dofs, old_vdofs;
2015  Vector row;
2016 
2017  ParNCMesh* pncmesh = pmesh->pncmesh;
2018  int geom = pncmesh->GetElementGeometry();
2019  int ldof = fec->FiniteElementForGeometry(geom)->GetDof();
2020 
2021  const CoarseFineTransformations &dtrans = pncmesh->GetDerefinementTransforms();
2022  const Array<int> &old_ranks = pncmesh->GetDerefineOldRanks();
2023 
2024  std::map<int, DerefDofMessage> messages;
2025 
2026  HYPRE_Int old_offset = HYPRE_AssumedPartitionCheck()
2027  ? old_dof_offsets[0] : old_dof_offsets[MyRank];
2028 
2029  // communicate DOFs for derefinements that straddle processor boundaries,
2030  // note that this is infrequent due to the way elements are ordered
2031  for (int k = 0; k < dtrans.embeddings.Size(); k++)
2032  {
2033  const Embedding &emb = dtrans.embeddings[k];
2034 
2035  int fine_rank = old_ranks[k];
2036  int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
2037  : pncmesh->ElementRank(emb.parent);
2038 
2039  if (coarse_rank != MyRank && fine_rank == MyRank)
2040  {
2041  old_elem_dof->GetRow(k, dofs);
2042  DofsToVDofs(dofs, old_ndofs);
2043 
2044  DerefDofMessage &msg = messages[k];
2045  msg.dofs.resize(dofs.Size());
2046  for (int i = 0; i < dofs.Size(); i++)
2047  {
2048  msg.dofs[i] = old_offset + dofs[i];
2049  }
2050 
2051  MPI_Isend(&msg.dofs[0], msg.dofs.size(), HYPRE_MPI_INT,
2052  coarse_rank, 291, MyComm, &msg.request);
2053  }
2054  else if (coarse_rank == MyRank && fine_rank != MyRank)
2055  {
2056  DerefDofMessage &msg = messages[k];
2057  msg.dofs.resize(ldof*vdim);
2058 
2059  MPI_Irecv(&msg.dofs[0], ldof*vdim, HYPRE_MPI_INT,
2060  fine_rank, 291, MyComm, &msg.request);
2061  }
2062  // TODO: coalesce Isends/Irecvs to the same rank. Typically, on uniform
2063  // derefinement, there should be just one send to MyRank-1 and one recv
2064  // from MyRank+1
2065  }
2066 
2067  DenseTensor localR;
2068  GetLocalDerefinementMatrices(geom, dtrans, localR);
2069 
2070  // create the diagonal part of the derefinement matrix
2071  SparseMatrix *diag = new SparseMatrix(ndofs*vdim, old_ndofs*vdim);
2072 
2073  Array<char> mark(diag->Height());
2074  mark = 0;
2075  for (int k = 0; k < dtrans.embeddings.Size(); k++)
2076  {
2077  const Embedding &emb = dtrans.embeddings[k];
2078  if (emb.parent < 0) { continue; }
2079 
2080  int coarse_rank = pncmesh->ElementRank(emb.parent);
2081  int fine_rank = old_ranks[k];
2082 
2083  if (coarse_rank == MyRank && fine_rank == MyRank)
2084  {
2085  DenseMatrix &lR = localR(emb.matrix);
2086 
2087  elem_dof->GetRow(emb.parent, dofs);
2088  old_elem_dof->GetRow(k, old_dofs);
2089 
2090  for (int vd = 0; vd < vdim; vd++)
2091  {
2092  old_dofs.Copy(old_vdofs);
2093  DofsToVDofs(vd, old_vdofs, old_ndofs);
2094 
2095  for (int i = 0; i < lR.Height(); i++)
2096  {
2097  if (lR(i, 0) == std::numeric_limits<double>::infinity())
2098  { continue; }
2099 
2100  int r = DofToVDof(dofs[i], vd);
2101  int m = (r >= 0) ? r : (-1 - r);
2102 
2103  if (!mark[m])
2104  {
2105  lR.GetRow(i, row);
2106  diag->SetRow(r, old_vdofs, row);
2107  mark[m] = 1;
2108  }
2109  }
2110  }
2111  }
2112  }
2113  diag->Finalize();
2114 
2115  // wait for all sends/receives to complete
2116  for (std::map<int, DerefDofMessage>::iterator
2117  it = messages.begin(); it != messages.end(); ++it)
2118  {
2119  MPI_Wait(&it->second.request, MPI_STATUS_IGNORE);
2120  }
2121 
2122  // create the offdiagonal part of the derefinement matrix
2123  SparseMatrix *offd = new SparseMatrix(ndofs*vdim, 1);
2124 
2125  std::map<HYPRE_Int, int> col_map;
2126  for (int k = 0; k < dtrans.embeddings.Size(); k++)
2127  {
2128  const Embedding &emb = dtrans.embeddings[k];
2129  if (emb.parent < 0) { continue; }
2130 
2131  int coarse_rank = pncmesh->ElementRank(emb.parent);
2132  int fine_rank = old_ranks[k];
2133 
2134  if (coarse_rank == MyRank && fine_rank != MyRank)
2135  {
2136  DenseMatrix &lR = localR(emb.matrix);
2137 
2138  elem_dof->GetRow(emb.parent, dofs);
2139 
2140  DerefDofMessage &msg = messages[k];
2141  MFEM_ASSERT(msg.dofs.size(), "");
2142 
2143  for (int vd = 0; vd < vdim; vd++)
2144  {
2145  HYPRE_Int* remote_dofs = &msg.dofs[vd*ldof];
2146 
2147  for (int i = 0; i < lR.Height(); i++)
2148  {
2149  if (lR(i, 0) == std::numeric_limits<double>::infinity())
2150  { continue; }
2151 
2152  int r = DofToVDof(dofs[i], vd);
2153  int m = (r >= 0) ? r : (-1 - r);
2154 
2155  if (!mark[m])
2156  {
2157  lR.GetRow(i, row);
2158  for (int j = 0; j < ldof; j++)
2159  {
2160  if (std::abs(row[j]) < 1e-12) { continue; }
2161  int &lcol = col_map[remote_dofs[j]];
2162  if (!lcol) { lcol = col_map.size(); }
2163  offd->_Set_(m, lcol-1, row[j]);
2164  }
2165  mark[m] = 1;
2166  }
2167  }
2168  }
2169  }
2170  }
2171  messages.clear();
2172  offd->Finalize(0);
2173  offd->SetWidth(col_map.size());
2174 
2175  // finish the matrix
2176  HYPRE_Int *cmap = new HYPRE_Int[col_map.size()];
2177  for (std::map<HYPRE_Int, int>::iterator
2178  it = col_map.begin(); it != col_map.end(); ++it)
2179  {
2180  cmap[it->second-1] = it->first;
2181  }
2182 
2183  HypreParMatrix* R;
2184  R = new HypreParMatrix(MyComm, dof_offsets[nrk], old_dof_offsets[nrk],
2185  dof_offsets, old_dof_offsets, diag, offd, cmap);
2186 
2187 #ifndef HYPRE_BIGINT
2188  diag->LoseData();
2189  offd->LoseData();
2190 #else
2191  diag->SetDataOwner(false);
2192  offd->SetDataOwner(false);
2193 #endif
2194  delete diag;
2195  delete offd;
2196 
2197  R->SetOwnerFlags(3, 3, 1);
2198 
2199  return R;
2200 }
2201 
2202 void ParFiniteElementSpace::Destroy()
2203 {
2204  ldof_group.DeleteAll();
2205  ldof_ltdof.DeleteAll();
2206  dof_offsets.DeleteAll();
2207  tdof_offsets.DeleteAll();
2208  tdof_nb_offsets.DeleteAll();
2209  // preserve old_dof_offsets
2210  ldof_sign.DeleteAll();
2211 
2212  delete P; P = NULL;
2213  delete Pconf; Pconf = NULL;
2214  delete R; R = NULL;
2215 
2216  delete gcomm; gcomm = NULL;
2217 
2218  num_face_nbr_dofs = -1;
2220  face_nbr_ldof.Clear();
2223 }
2224 
2225 void ParFiniteElementSpace::Update(bool want_transform)
2226 {
2227  if (mesh->GetSequence() == sequence)
2228  {
2229  return; // no need to update, no-op
2230  }
2231  if (want_transform && mesh->GetSequence() != sequence + 1)
2232  {
2233  MFEM_ABORT("Error in update sequence. Space needs to be updated after "
2234  "each mesh modification.");
2235  }
2236  sequence = mesh->GetSequence();
2237 
2238  if (NURBSext)
2239  {
2240  UpdateNURBS();
2241  return;
2242  }
2243 
2244  Table* old_elem_dof = NULL;
2245  int old_ndofs;
2246 
2247  // save old DOF table
2248  if (want_transform)
2249  {
2250  old_elem_dof = elem_dof;
2251  elem_dof = NULL;
2252  old_ndofs = ndofs;
2253  Swap(dof_offsets, old_dof_offsets);
2254  }
2255 
2256  Destroy();
2258 
2259  FiniteElementSpace::Construct(); // sets T to NULL, own_T to true
2260  Construct();
2261 
2263 
2264  if (want_transform)
2265  {
2266  // calculate appropriate GridFunction transformation
2267  switch (mesh->GetLastOperation())
2268  {
2269  case Mesh::REFINE:
2270  {
2271  T = RefinementMatrix(old_ndofs, old_elem_dof);
2272  break;
2273  }
2274 
2275  case Mesh::DEREFINE:
2276  {
2277  T = ParallelDerefinementMatrix(old_ndofs, old_elem_dof);
2278  if (Nonconforming())
2279  {
2280  T = new TripleProductOperator(P, R, T, false, false, true);
2281  }
2282  break;
2283  }
2284 
2285  case Mesh::REBALANCE:
2286  {
2287  T = RebalanceMatrix(old_ndofs, old_elem_dof);
2288  break;
2289  }
2290 
2291  default:
2292  break; // T stays NULL
2293  }
2294  delete old_elem_dof;
2295  }
2296 }
2297 
2298 
2300  ParFiniteElementSpace &pfes)
2301  : Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
2302  external_ldofs(),
2303  gc(pfes.GroupComm())
2304 {
2305  MFEM_VERIFY(pfes.Conforming(), "");
2306  Array<int> ldofs;
2307  Table &group_ldof = gc.GroupLDofTable();
2309  for (int gr = 1; gr < group_ldof.Size(); gr++)
2310  {
2311  if (!gc.GetGroupTopology().IAmMaster(gr))
2312  {
2313  ldofs.MakeRef(group_ldof.GetRow(gr), group_ldof.RowSize(gr));
2314  external_ldofs.Append(ldofs);
2315  }
2316  }
2317  external_ldofs.Sort();
2318  MFEM_ASSERT(external_ldofs.Size() == Height()-Width(), "");
2319 #ifdef MFEM_DEBUG
2320  for (int j = 1; j < external_ldofs.Size(); j++)
2321  {
2322  // Check for repeated ldofs.
2323  MFEM_VERIFY(external_ldofs[j-1] < external_ldofs[j], "");
2324  }
2325  int j = 0;
2326  for (int i = 0; i < external_ldofs.Size(); i++)
2327  {
2328  const int end = external_ldofs[i];
2329  for ( ; j < end; j++)
2330  {
2331  MFEM_VERIFY(j-i == pfes.GetLocalTDofNumber(j), "");
2332  }
2333  j = end+1;
2334  }
2335  for ( ; j < Height(); j++)
2336  {
2337  MFEM_VERIFY(j-external_ldofs.Size() == pfes.GetLocalTDofNumber(j), "");
2338  }
2339  // gc.PrintInfo();
2340  // pfes.Dof_TrueDof_Matrix()->PrintCommPkg();
2341 #endif
2342 }
2343 
2345 {
2346  MFEM_ASSERT(x.Size() == Width(), "");
2347  MFEM_ASSERT(y.Size() == Height(), "");
2348 
2349  const double *xdata = x.GetData();
2350  double *ydata = y.GetData();
2351  const int m = external_ldofs.Size();
2352 
2353  const int in_layout = 2; // 2 - input is ltdofs array
2354  gc.BcastBegin(const_cast<double*>(xdata), in_layout);
2355 
2356  int j = 0;
2357  for (int i = 0; i < m; i++)
2358  {
2359  const int end = external_ldofs[i];
2360  std::copy(xdata+j-i, xdata+end-i, ydata+j);
2361  j = end+1;
2362  }
2363  std::copy(xdata+j-m, xdata+Width(), ydata+j);
2364 
2365  const int out_layout = 0; // 0 - output is ldofs array
2366  gc.BcastEnd(ydata, out_layout);
2367 }
2368 
2370  const Vector &x, Vector &y) const
2371 {
2372  MFEM_ASSERT(x.Size() == Height(), "");
2373  MFEM_ASSERT(y.Size() == Width(), "");
2374 
2375  const double *xdata = x.GetData();
2376  double *ydata = y.GetData();
2377  const int m = external_ldofs.Size();
2378 
2379  gc.ReduceBegin(xdata);
2380 
2381  int j = 0;
2382  for (int i = 0; i < m; i++)
2383  {
2384  const int end = external_ldofs[i];
2385  std::copy(xdata+j, xdata+end, ydata+j-i);
2386  j = end+1;
2387  }
2388  std::copy(xdata+j, xdata+Height(), ydata+j-m);
2389 
2390  const int out_layout = 2; // 2 - output is an array on all ltdofs
2391  gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
2392 }
2393 
2394 } // namespace mfem
2395 
2396 #endif
Abstract class for Finite Elements.
Definition: fe.hpp:47
virtual const Operator * GetProlongationMatrix()
Definition: pfespace.cpp:632
int GetNFaceNeighbors() const
Definition: pmesh.hpp:155
int GetGroupMasterRank(int g) const
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.
Definition: pncmesh.cpp:1592
int Size() const
Logical size of the array.
Definition: array.hpp:110
int GetVSize() const
Definition: fespace.hpp:163
virtual void GetEssentialTrueDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_tdof_list, int component=-1)
Definition: pfespace.cpp:529
void Add(const int i, const int j, const double a)
Definition: sparsemat.cpp:1907
HYPRE_Int GetGlobalTDofNumber(int ldof) const
Returns the global tdof number of the given local degree of freedom.
Definition: pfespace.cpp:562
int GetNDofs() const
Returns number of degrees of freedom.
Definition: fespace.hpp:161
int * GetJ()
Definition: table.hpp:111
int ndofs
Number of degrees of freedom. Number of unknowns are ndofs*vdim.
Definition: fespace.hpp:77
int DofToVDof(int dof, int vd, int ndofs=-1) const
Definition: fespace.cpp:104
ConformingProlongationOperator(ParFiniteElementSpace &pfes)
Definition: pfespace.cpp:2299
void GetFaceInfos(int Face, int *Inf1, int *Inf2)
Definition: mesh.cpp:749
void AddDofs(int type, const NCMesh::MeshId &id, const Array< int > &dofs)
Add vertex/edge/face DOFs to an outgoing message.
Definition: pncmesh.cpp:1934
void SubDofOrder(int Geom, int SDim, int Info, Array< int > &dofs) const
Get the local dofs for a given sub-manifold.
Definition: fe_coll.cpp:353
Array< int > ldof_group
Definition: nurbs.hpp:352
void BuildElementToDofTable() const
Definition: fespace.cpp:175
void AddColumnsInRow(int r, int ncol)
Definition: table.hpp:75
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_dofs, int component=-1) const
Determine the boundary degrees of freedom.
Definition: pfespace.cpp:515
void MakeI(int nrows)
Next 7 methods are used together with the default constructor.
Definition: table.cpp:74
HYPRE_Int * GetDofOffsets() const
Definition: pfespace.hpp:174
void GetFaceElements(int Face, int *Elem1, int *Elem2)
Definition: mesh.cpp:743
virtual void Finalize(int skip_zeros=1)
Finalize the matrix initialization, switching the storage format from LIL to CSR. ...
Definition: sparsemat.hpp:278
int GetNGroups() const
Definition: pmesh.hpp:136
Auxiliary class used by ParFiniteElementSpace.
Definition: pfespace.hpp:288
SparseMatrix * RefinementMatrix(int old_ndofs, const Table *old_elem_dof)
Calculate GridFunction interpolation matrix after mesh refinement.
Definition: fespace.cpp:745
void GetElementVDofs(int i, Array< int > &vdofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: fespace.cpp:133
Ordering::Type ordering
Definition: fespace.hpp:74
Array< Element * > face_nbr_elements
Definition: pmesh.hpp:128
const Geometry::Type geom
int GetElementGeometry() const
Return the type of elements in the mesh.
Definition: ncmesh.hpp:234
void BooleanMult(const Array< int > &x, Array< int > &y) const
y = A * x, but treat all elements as booleans (zero=false, nonzero=true).
Definition: sparsemat.cpp:633
Lists all edges/faces in the nonconforming mesh.
Definition: ncmesh.hpp:166
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition: operator.hpp:42
int GetGroupSize(int g) const
virtual void Update(bool want_transform=true)
Definition: pfespace.cpp:2225
int VDofToDof(int vdof) const
Definition: fespace.hpp:252
T * GetData()
Returns the data.
Definition: array.hpp:92
virtual void GetEssentialVDofs(const Array< int > &bdr_attr_is_ess, Array< int > &ess_vdofs, int component=-1) const
Definition: fespace.cpp:258
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
int vdim
Vector dimension (number of unknowns per degree of freedom).
Definition: fespace.hpp:69
int Size() const
Returns the size of the vector.
Definition: vector.hpp:113
void GetRow(int i, Array< int > &row) const
Return row i in array row (the Table must be finalized)
Definition: table.cpp:179
int GetNE() const
Returns number of elements.
Definition: mesh.hpp:614
void DivideByGroupSize(double *vec)
Scale a vector of true dofs.
Definition: pfespace.cpp:460
Abstract parallel finite element space.
Definition: pfespace.hpp:31
void Synchronize(Array< int > &ldof_marker) const
Definition: pfespace.cpp:498
std::vector< MeshId > conforming
Definition: ncmesh.hpp:168
virtual const SparseMatrix * GetRestrictionMatrix()
Get the R matrix which restricts a local dof vector to true dof vector.
Definition: pfespace.hpp:250
std::map< int, NeighborRowRequest > Map
Definition: pncmesh.hpp:544
int GetLocalTDofNumber(int ldof) const
Definition: pfespace.cpp:541
int GetOwner(int type, int index) const
Return vertex/edge/face (&#39;type&#39; == 0/1/2, resp.) owner.
Definition: pncmesh.hpp:138
HypreParMatrix * GetPartialConformingInterpolation()
For a non-conforming mesh, construct and return the interpolation matrix from the partially conformin...
Definition: pfespace.cpp:1570
void AddRow(const int row, const Array< int > &cols, const Vector &srow)
Definition: sparsemat.cpp:2130
double * GetData() const
Definition: vector.hpp:121
const FiniteElementCollection * fec
Definition: fespace.hpp:66
int Size_of_connections() const
Definition: table.hpp:95
HYPRE_Int GetMyDofOffset() const
Definition: pfespace.cpp:622
const int INVALID_DOF
Definition: pfespace.cpp:1034
int GetGeometryType() const
Definition: element.hpp:47
void DeleteAll()
Delete whole array.
Definition: array.hpp:633
void AddConnections(int r, const int *c, int nc)
Definition: table.cpp:96
static void MarkerToList(const Array< int > &marker, Array< int > &list)
Convert a Boolean marker array to a list containing all marked indices.
Definition: fespace.cpp:346
bool IAmMaster(int g) const
A parallel extension of the NCMesh class.
Definition: pncmesh.hpp:65
void GetSharedEdgeDofs(int group, int ei, Array< int > &dofs) const
Definition: pfespace.cpp:303
ParNCMesh * pncmesh
Definition: pmesh.hpp:134
virtual void GetFaceDofs(int i, Array< int > &dofs) const
Definition: fespace.cpp:1267
virtual void GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i&#39;th boundary element.
Definition: pfespace.cpp:280
int GroupVertex(int group, int i)
Definition: pmesh.hpp:143
int GetNumFaces() const
Return the number of faces (3D), edges (2D) or vertices (1D).
Definition: mesh.cpp:3334
void GetFaceNbrElementVDofs(int i, Array< int > &vdofs) const
Definition: pfespace.cpp:865
void ExchangeFaceNbrData()
Definition: pmesh.cpp:1357
long GetSequence() const
Definition: mesh.hpp:1012
void GetVertexDofs(int i, Array< int > &dofs) const
Definition: fespace.cpp:1355
Communicator performing operations within groups defined by a GroupTopology with arbitrary-size data ...
const FiniteElement * GetFaceNbrFE(int i) const
Definition: pfespace.cpp:899
void GetSharedFaceDofs(int group, int fi, Array< int > &dofs) const
Definition: pfespace.cpp:326
Array< int > face_nbr_elements_offset
Definition: pmesh.hpp:126
int dim
Definition: ex3.cpp:47
virtual int GetRow(const int row, Array< int > &cols, Vector &srow) const
Extract all column indices and values from a given row.
Definition: sparsemat.cpp:2043
static const int Dimension[NumGeom]
Definition: geom.hpp:38
static void Sum(OpData< T >)
Reduce operation Sum, instantiated for int and double.
Data type sparse matrix.
Definition: sparsemat.hpp:38
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition: operator.hpp:36
int Append(const T &el)
Append element to array, resize if necessary.
Definition: array.hpp:548
void Clear()
Definition: table.cpp:363
Operator * T
Transformation to apply to GridFunctions after space Update().
Definition: fespace.hpp:99
int to_int(const std::string &str)
Definition: text.hpp:68
std::vector< Master > masters
Definition: ncmesh.hpp:169
void AddConnection(int r, int c)
Definition: table.hpp:77
void LoseData()
NULL-ifies the data.
Definition: array.hpp:104
HypreParMatrix * Dof_TrueDof_Matrix() const
The true dof-to-dof interpolation matrix.
Definition: pfespace.hpp:198
virtual void GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: fespace.cpp:1072
HYPRE_Int GetMyTDofOffset() const
Definition: pfespace.cpp:627
void Reserve(int capacity)
Ensures that the allocated size is at least the given size.
Definition: array.hpp:123
void Transpose(const Table &A, Table &At, int _ncols_A)
Transpose a Table.
Definition: table.cpp:408
Identifies a vertex/edge/face in both Mesh and NCMesh.
Definition: ncmesh.hpp:130
static void IsendAll(MapT &rank_msg, MPI_Comm comm)
Helper to send all messages in a rank-to-message map container.
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition: hypre.cpp:872
void Sort()
Sorts the array. This requires operator&lt; to be defined for T.
Definition: array.hpp:208
int GroupNVertices(int group)
Definition: pmesh.hpp:139
virtual void GetFaceDofs(int i, Array< int > &dofs) const
Definition: pfespace.cpp:294
int decode_dof(int dof, double &sign)
Definition: pfespace.cpp:1029
int Size() const
Returns the number of TYPE I elements.
Definition: table.hpp:89
int Dimension() const
Definition: mesh.hpp:641
double * GetData() const
Return element data, i.e. array A.
Definition: sparsemat.hpp:135
void GetEdgeDofs(int i, Array< int > &dofs) const
Definition: fespace.cpp:1326
int * GetI() const
Return the array I.
Definition: sparsemat.hpp:131
int GetGroupMaster(int g) const
void Swap(Array< T > &, Array< T > &)
Definition: array.hpp:494
MPI_Comm GetComm() const
Definition: pmesh.hpp:117
int GetDof() const
Returns the number of degrees of freedom in the finite element.
Definition: fe.hpp:122
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition: pfespace.cpp:2369
void AddAColumnInRow(int r)
Definition: table.hpp:74
void mfem_error(const char *msg)
Definition: error.cpp:107
void SetSize(int nsize)
Change logical size of the array, keep existing entries.
Definition: array.hpp:503
static void RecvAll(MapT &rank_msg, MPI_Comm comm)
Helper to receive all messages in a rank-to-message map container.
HYPRE_Int GetGlobalScalarTDofNumber(int sldof)
Definition: pfespace.cpp:585
Table & GroupLDofTable()
Fill-in the returned Table reference to initialize the GroupCommunicator then call Finalize()...
void ShiftUpI()
Definition: table.cpp:107
Operation GetLastOperation() const
Return type of last modification of the mesh.
Definition: mesh.hpp:1006
bool Nonconforming() const
Definition: pfespace.hpp:267
virtual int * DofOrderForOrientation(int GeomType, int Or) const =0
virtual void GetBdrElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom for i&#39;th boundary element.
Definition: fespace.cpp:1184
NURBSExtension * NURBSext
Optional NURBS mesh extension.
Definition: mesh.hpp:174
virtual void GetElementDofs(int i, Array< int > &dofs) const
Returns indexes of degrees of freedom in array dofs for i&#39;th element.
Definition: pfespace.cpp:266
void Init(ParNCMesh *pncmesh, const FiniteElementCollection *fec, int ndofs)
Definition: pncmesh.hpp:508
void GroupFace(int group, int i, int &face, int &o)
Definition: pmesh.cpp:1039
Mesh * mesh
The mesh that FE space lives on.
Definition: fespace.hpp:64
General triple product operator x -&gt; A*B*C*x, with ownership of the factors.
Definition: operator.hpp:346
void MakeJ()
Definition: table.cpp:84
int GroupNFaces(int group)
Definition: pmesh.hpp:141
void Reduce(T *ldata, void(*Op)(OpData< T >))
Reduce within each group where the master is the root.
virtual void Mult(const Vector &x, Vector &y) const
Operator application: y=A(x).
Definition: pfespace.cpp:2344
const FiniteElement * GetFaceNbrFaceFE(int i) const
Definition: pfespace.cpp:913
void Create(Array< int > &ldof_group)
Initialize the communicator from a local-dof to group map. Finalize() is called internally.
int GetFaceOrientation(int index) const
Return (shared) face orientation relative to the owner element.
Definition: pncmesh.hpp:132
T & Last()
Return the last element in the array.
Definition: array.hpp:581
HYPRE_Int * GetTrueDofOffsets() const
Definition: pfespace.hpp:175
void GetFaceNbrFaceVDofs(int i, Array< int > &vdofs) const
Definition: pfespace.cpp:871
std::map< int, NeighborDofMessage > Map
Definition: pncmesh.hpp:516
void MakeRef(T *, int)
Make this Array a reference to a pointer.
Definition: array.hpp:644
void GroupEdge(int group, int i, int &edge, int &o)
Definition: pmesh.cpp:1031
static void WaitAllSent(MapT &rank_msg)
Helper to wait for all messages in a map container to be sent.
const NCList & GetSharedList(int type)
Helper to get shared vertices/edges/faces (&#39;type&#39; == 0/1/2 resp.).
Definition: pncmesh.hpp:121
Vector data type.
Definition: vector.hpp:41
int GetFaceBaseGeometry(int i) const
Definition: mesh.cpp:3935
void GenerateOffsets(int N, HYPRE_Int loc_sizes[], Array< HYPRE_Int > *offsets[]) const
Definition: pmesh.cpp:1241
std::set< int > rows
Definition: pncmesh.hpp:539
int * GetI()
Definition: table.hpp:110
void SetLTDofTable(Array< int > &ldof_ltdof)
Initialize the internal group_ltdof Table.
std::map< int, NeighborRowReply > Map
Definition: pncmesh.hpp:565
const int * GetGroup(int type, int index, int &size) const
Definition: pncmesh.hpp:150
int RowSize(int i) const
Definition: table.hpp:105
NURBSExtension * NURBSext
Definition: fespace.hpp:87
virtual int DofForGeometry(int GeomType) const =0
static bool IProbe(int &rank, int &size, MPI_Comm comm)
Table send_face_nbr_elements
Definition: pmesh.hpp:131
GroupTopology gtopo
Definition: pmesh.hpp:121
Abstract operator.
Definition: operator.hpp:21
Wrapper for hypre&#39;s ParCSR matrix class.
Definition: hypre.hpp:175
GroupCommunicator * ScalarGroupComm()
Return a new GroupCommunicator on Dofs.
Definition: pfespace.cpp:478
int * GetJ() const
Return the array J.
Definition: sparsemat.hpp:133
BiLinear2DFiniteElement QuadrilateralFE
Class for parallel meshes.
Definition: pmesh.hpp:29
Abstract data type element.
Definition: element.hpp:27
GroupTopology gtopo
Definition: nurbs.hpp:350
int GetFaceNbrRank(int fn) const
Definition: pmesh.cpp:1818
Linear1DFiniteElement SegmentFE
Definition: segment.cpp:49
ParFiniteElementSpace(ParMesh *pm, const FiniteElementCollection *f, int dim=1, int ordering=Ordering::byNODES)
Definition: pfespace.cpp:27
static void Probe(int &rank, int &size, MPI_Comm comm)
void Bcast(T *ldata, int layout)
Broadcast within each group where the master is the root.
int GroupNEdges(int group)
Definition: pmesh.hpp:140
virtual const FiniteElement * FiniteElementForGeometry(int GeomType) const =0
void GetLocalDerefinementMatrices(int geom, const CoarseFineTransformations &dt, DenseTensor &localR)
Definition: fespace.cpp:827
static void BitOR(OpData< T >)
Reduce operation bitwise OR, instantiated for int only.
void DofsToVDofs(Array< int > &dofs, int ndofs=-1) const
Definition: fespace.cpp:68
Array< HYPRE_Int > face_nbr_glob_dof_map
Definition: pfespace.hpp:159