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