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