MFEM  v4.3.0
Finite element discretization library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
constraints.cpp
Go to the documentation of this file.
1 // Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced
2 // at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3 // LICENSE and NOTICE for details. LLNL-CODE-806117.
4 //
5 // This file is part of the MFEM library. For more information and source code
6 // availability visit https://mfem.org.
7 //
8 // MFEM is free software; you can redistribute it and/or modify it under the
9 // terms of the BSD-3 license. We welcome feedback and contributions, see file
10 // CONTRIBUTING.md for details.
11 
12 #include "constraints.hpp"
13 
14 #include "../fem/fespace.hpp"
15 #include "../fem/pfespace.hpp"
16 
17 #include <set>
18 
19 namespace mfem
20 {
21 
22 Eliminator::Eliminator(const SparseMatrix& B, const Array<int>& lagrange_tdofs_,
23  const Array<int>& primary_tdofs_,
24  const Array<int>& secondary_tdofs_)
25  :
26  lagrange_tdofs(lagrange_tdofs_),
27  primary_tdofs(primary_tdofs_),
28  secondary_tdofs(secondary_tdofs_)
29 {
30  MFEM_VERIFY(lagrange_tdofs.Size() == secondary_tdofs.Size(),
31  "Dof sizes don't match!");
32 
33  Bp.SetSize(lagrange_tdofs.Size(), primary_tdofs.Size());
34  B.GetSubMatrix(lagrange_tdofs, primary_tdofs, Bp);
35 
36  Bs.SetSize(lagrange_tdofs.Size(), secondary_tdofs.Size());
37  B.GetSubMatrix(lagrange_tdofs, secondary_tdofs, Bs);
38  BsT.Transpose(Bs);
39 
40  ipiv.SetSize(Bs.Height());
41  Bsinverse.data = Bs.HostReadWrite();
42  Bsinverse.ipiv = ipiv.HostReadWrite();
43  Bsinverse.Factor(Bs.Height());
44 
45  ipivT.SetSize(Bs.Height());
46  BsTinverse.data = BsT.HostReadWrite();
47  BsTinverse.ipiv = ipivT.HostReadWrite();
48  BsTinverse.Factor(Bs.Height());
49 }
50 
51 void Eliminator::Eliminate(const Vector& in, Vector& out) const
52 {
53  Bp.Mult(in, out);
54  Bsinverse.Solve(Bs.Height(), 1, out);
55  out *= -1.0;
56 }
57 
59 {
60  Vector work(in);
61  BsTinverse.Solve(Bs.Height(), 1, work);
62  Bp.MultTranspose(work, out);
63  out *= -1.0;
64 }
65 
67 {
68  out = in;
69  Bsinverse.Solve(Bs.Height(), 1, out);
70 }
71 
73 {
74  out = in;
75  BsTinverse.Solve(Bs.Height(), 1, out);
76 }
77 
79 {
80  mat.SetSize(Bp.Height(), Bp.Width());
81  mat = Bp;
82  Bsinverse.Solve(Bs.Height(), Bp.Width(), mat.GetData());
83  mat *= -1.0;
84 }
85 
87  Array<Eliminator*>& eliminators_)
88  :
89  Operator(A.Height()),
90  Aop(A),
91  eliminators(eliminators_)
92 {
93 }
94 
96 {
97  MFEM_ASSERT(in.Size() == width, "Wrong vector size!");
98  MFEM_ASSERT(out.Size() == height, "Wrong vector size!");
99 
100  out = in;
101 
102  for (int k = 0; k < eliminators.Size(); ++k)
103  {
104  Eliminator* elim = eliminators[k];
105  Vector subvec_in;
106  Vector subvec_out(elim->SecondaryDofs().Size());
107  in.GetSubVector(elim->PrimaryDofs(), subvec_in);
108  elim->Eliminate(subvec_in, subvec_out);
109  out.SetSubVector(elim->SecondaryDofs(), subvec_out);
110  }
111 }
112 
114 {
115  MFEM_ASSERT(in.Size() == height, "Wrong vector size!");
116  MFEM_ASSERT(out.Size() == width, "Wrong vector size!");
117 
118  out = in;
119 
120  for (int k = 0; k < eliminators.Size(); ++k)
121  {
122  Eliminator* elim = eliminators[k];
123  Vector subvec_in;
124  Vector subvec_out(elim->PrimaryDofs().Size());
125  in.GetSubVector(elim->SecondaryDofs(), subvec_in);
126  elim->EliminateTranspose(subvec_in, subvec_out);
127  out.AddElementVector(elim->PrimaryDofs(), subvec_out);
128  out.SetSubVector(elim->SecondaryDofs(), 0.0);
129  }
130 }
131 
133 {
135 
136  for (int i = 0; i < height; ++i)
137  {
138  out->Add(i, i, 1.0);
139  }
140 
141  for (int k = 0; k < eliminators.Size(); ++k)
142  {
143  Eliminator* elim = eliminators[k];
144  DenseMatrix mat;
145  elim->ExplicitAssembly(mat);
146  for (int iz = 0; iz < elim->SecondaryDofs().Size(); ++iz)
147  {
148  int i = elim->SecondaryDofs()[iz];
149  for (int jz = 0; jz < elim->PrimaryDofs().Size(); ++jz)
150  {
151  int j = elim->PrimaryDofs()[jz];
152  out->Add(i, j, mat(iz, jz));
153  }
154  out->Set(i, i, 0.0);
155  }
156  }
157 
158  out->Finalize();
159  return out;
160 }
161 
162 void EliminationProjection::BuildGTilde(const Vector& r, Vector& rtilde) const
163 {
164  MFEM_ASSERT(rtilde.Size() == Aop.Height(), "Sizes don't match!");
165 
166  rtilde = 0.0;
167  for (int k = 0; k < eliminators.Size(); ++k)
168  {
169  Eliminator* elim = eliminators[k];
170  Vector subr;
171  r.GetSubVector(elim->LagrangeDofs(), subr);
172  Vector bsinvr(subr.Size());
173  elim->LagrangeSecondary(subr, bsinvr);
174  rtilde.AddElementVector(elim->SecondaryDofs(), bsinvr);
175  }
176 }
177 
179  const Vector& disprhs, const Vector& disp, Vector& lagrangem) const
180 {
181  lagrangem = 0.0;
182  MFEM_ASSERT(disp.Size() == Aop.Height(), "Sizes don't match!");
183 
184  Vector fullrhs(Aop.Height());
185  Aop.Mult(disp, fullrhs);
186  fullrhs -= disprhs;
187  fullrhs *= -1.0;
188  for (int k = 0; k < eliminators.Size(); ++k)
189  {
190  Eliminator* elim = eliminators[k];
191  Vector localsec;
192  fullrhs.GetSubVector(elim->SecondaryDofs(), localsec);
193  Vector locallagrange(localsec.Size());
194  elim->LagrangeSecondaryTranspose(localsec, locallagrange);
195  lagrangem.AddElementVector(elim->LagrangeDofs(), locallagrange);
196  }
197 }
198 
199 #ifdef MFEM_USE_MPI
200 
202 {
203  delete h_explicit_operator;
204  for (auto elim : eliminators)
205  {
206  delete elim;
207  }
208  delete projector;
209  delete prec;
210  delete krylov;
211 }
212 
214 {
215  SparseMatrix * explicit_projector = projector->AssembleExact();
216  HypreParMatrix * h_explicit_projector =
218  hA.GetRowStarts(), explicit_projector);
219  h_explicit_projector->CopyRowStarts();
220  h_explicit_projector->CopyColStarts();
221 
222  h_explicit_operator = RAP(&hA, h_explicit_projector);
223  // next line because of square projector
227 
228  delete explicit_projector;
229  delete h_explicit_projector;
230 }
231 
233  Array<int>& primary_dofs,
234  Array<int>& secondary_dofs)
235  :
236  ConstrainedSolver(A.GetComm(), A, B),
237  hA(A),
238  krylov(nullptr),
239  prec(nullptr)
240 {
241  MFEM_VERIFY(secondary_dofs.Size() == B.Height(),
242  "Wrong number of dofs for elimination!");
243  Array<int> lagrange_dofs(secondary_dofs.Size());
244  for (int i = 0; i < lagrange_dofs.Size(); ++i)
245  {
246  lagrange_dofs[i] = i;
247  }
248  eliminators.Append(new Eliminator(B, lagrange_dofs, primary_dofs,
249  secondary_dofs));
252 }
253 
255  Array<int>& constraint_rowstarts)
256  :
257  ConstrainedSolver(A.GetComm(), A, B),
258  hA(A),
259  krylov(nullptr),
260  prec(nullptr)
261 {
262  if (!B.Empty())
263  {
264  int * I = B.GetI();
265  int * J = B.GetJ();
266  double * data = B.GetData();
267 
268  for (int k = 0; k < constraint_rowstarts.Size() - 1; ++k)
269  {
270  int constraint_size = constraint_rowstarts[k + 1] -
271  constraint_rowstarts[k];
272  Array<int> lagrange_dofs(constraint_size);
273  Array<int> primary_dofs;
274  Array<int> secondary_dofs(constraint_size);
275  secondary_dofs = -1;
276  // loop through rows, identify one secondary dof for each row
277  for (int i = constraint_rowstarts[k]; i < constraint_rowstarts[k + 1]; ++i)
278  {
279  lagrange_dofs[i - constraint_rowstarts[k]] = i;
280  for (int jptr = I[i]; jptr < I[i + 1]; ++jptr)
281  {
282  int j = J[jptr];
283  double val = data[jptr];
284  if (std::abs(val) > 1.e-12 && secondary_dofs.Find(j) == -1)
285  {
286  secondary_dofs[i - constraint_rowstarts[k]] = j;
287  break;
288  }
289  }
290  }
291  // loop through rows again, assigning non-secondary dofs as primary
292  for (int i = constraint_rowstarts[k]; i < constraint_rowstarts[k + 1]; ++i)
293  {
294  MFEM_ASSERT(secondary_dofs[i - constraint_rowstarts[k]] >= 0,
295  "Secondary dofs don't match rows!");
296  for (int jptr = I[i]; jptr < I[i + 1]; ++jptr)
297  {
298  int j = J[jptr];
299  if (secondary_dofs.Find(j) == -1)
300  {
301  primary_dofs.Append(j);
302  }
303  }
304  }
305  primary_dofs.Sort();
306  primary_dofs.Unique();
307  eliminators.Append(new Eliminator(B, lagrange_dofs, primary_dofs,
308  secondary_dofs));
309  }
310  }
313 }
314 
315 void EliminationSolver::Mult(const Vector& rhs, Vector& sol) const
316 {
317  if (!prec)
318  {
320  }
321  if (!krylov)
322  {
323  krylov = BuildKrylov();
326  }
331 
332  Vector rtilde(rhs.Size());
333  if (constraint_rhs.Size() > 0)
334  {
336  }
337  else
338  {
339  rtilde = 0.0;
340  }
341  Vector temprhs(rhs);
342  hA.Mult(-1.0, rtilde, 1.0, temprhs);
343 
344  Vector reducedrhs(rhs.Size());
345  projector->MultTranspose(temprhs, reducedrhs);
346  Vector reducedsol(rhs.Size());
347  reducedsol = 0.0;
348  krylov->Mult(reducedrhs, reducedsol);
352 
353  projector->Mult(reducedsol, sol);
355  sol += rtilde;
356 }
357 
358 void PenaltyConstrainedSolver::Initialize(HypreParMatrix& A, HypreParMatrix& B)
359 {
360  HypreParMatrix * hBT = B.Transpose();
361  HypreParMatrix * hBTB = ParMult(hBT, &B, true);
362  // this matrix doesn't get cleanly deleted?
363  // (hypre comm pkg)
364  (*hBTB) *= penalty;
365  penalized_mat = ParAdd(&A, hBTB);
366  delete hBTB;
367  delete hBT;
368 }
369 
371  HypreParMatrix& A, SparseMatrix& B, double penalty_)
372  :
373  ConstrainedSolver(A.GetComm(), A, B),
374  penalty(penalty_),
375  constraintB(B),
376  krylov(nullptr),
377  prec(nullptr)
378 {
379  int rank, size;
380  MPI_Comm_rank(A.GetComm(), &rank);
381  MPI_Comm_size(A.GetComm(), &size);
382 
383  int constraint_running_total = 0;
384  int local_constraints = B.Height();
385  MPI_Scan(&local_constraints, &constraint_running_total, 1, MPI_INT,
386  MPI_SUM, A.GetComm());
387  int global_constraints = 0;
388  if (rank == size - 1) { global_constraints = constraint_running_total; }
389  MPI_Bcast(&global_constraints, 1, MPI_INT, size - 1, A.GetComm());
390 
391  HYPRE_BigInt glob_num_rows = global_constraints;
392  HYPRE_BigInt glob_num_cols = A.N();
393  HYPRE_BigInt row_starts[2] = { constraint_running_total - local_constraints,
394  constraint_running_total
395  };
396  HYPRE_BigInt col_starts[2] = { A.ColPart()[0], A.ColPart()[1] };
397  HypreParMatrix hB(A.GetComm(), glob_num_rows, glob_num_cols,
398  row_starts, col_starts, &B);
399  hB.CopyRowStarts();
400  hB.CopyColStarts();
401  Initialize(A, hB);
402 }
403 
405  HypreParMatrix& A, HypreParMatrix& B, double penalty_)
406  :
407  ConstrainedSolver(A.GetComm(), A, B),
408  penalty(penalty_),
409  constraintB(B),
410  krylov(nullptr),
411  prec(nullptr)
412 {
413  Initialize(A, B);
414 }
415 
417 {
418  delete penalized_mat;
419  delete prec;
420  delete krylov;
421 }
422 
424 {
425  if (!prec)
426  {
428  }
429  if (!krylov)
430  {
431  krylov = BuildKrylov();
434  }
435 
436  // form penalized right-hand side
437  Vector penalized_rhs(b);
438  if (constraint_rhs.Size() > 0)
439  {
440  Vector temp(x.Size());
442  temp *= penalty;
443  penalized_rhs += temp;
444  }
445 
446  // actually solve
451  krylov->Mult(penalized_rhs, x);
455 
457  if (constraint_rhs.Size() > 0)
458  {
460  }
462 }
463 
464 #endif
465 
466 /// because IdentityOperator isn't a Solver
467 class IdentitySolver : public Solver
468 {
469 public:
470  IdentitySolver(int size) : Solver(size) { }
471  void Mult(const Vector& x, Vector& y) const { y = x; }
472  void SetOperator(const Operator& op) { }
473 };
474 
475 void SchurConstrainedSolver::Initialize()
476 {
477  offsets[0] = 0;
478  offsets[1] = A.Height();
479  offsets[2] = A.Height() + B.Height();
480 
481  block_op = new BlockOperator(offsets);
482  block_op->SetBlock(0, 0, &A);
483  block_op->SetBlock(1, 0, &B);
484  tr_B = new TransposeOperator(&B);
485  block_op->SetBlock(0, 1, tr_B);
486 
487  block_pc = new BlockDiagonalPreconditioner(block_op->RowOffsets()),
488  rel_tol = 1.e-6;
489 }
490 
491 #ifdef MFEM_USE_MPI
493  Operator& A_, Operator& B_,
494  Solver& primal_pc_)
495  :
496  ConstrainedSolver(comm, A_, B_),
497  offsets(3),
498  primal_pc(&primal_pc_),
499  dual_pc(nullptr)
500 {
501  Initialize();
503  dual_pc = new IdentitySolver(block_op->RowOffsets()[2] -
504  block_op->RowOffsets()[1]);
507 }
508 #endif
509 
511  Solver& primal_pc_)
512  :
513  ConstrainedSolver(A_, B_),
514  offsets(3),
515  primal_pc(&primal_pc_),
516  dual_pc(nullptr)
517 {
518  Initialize();
520  dual_pc = new IdentitySolver(block_op->RowOffsets()[2] -
521  block_op->RowOffsets()[1]);
524 }
525 
526 #ifdef MFEM_USE_MPI
527 // protected constructor
529  Operator& B_)
530  :
531  ConstrainedSolver(comm, A_, B_),
532  offsets(3),
533  primal_pc(nullptr),
534  dual_pc(nullptr)
535 {
536  Initialize();
537 }
538 #endif
539 
540 // protected constructor
542  :
543  ConstrainedSolver(A_, B_),
544  offsets(3),
545  primal_pc(nullptr),
546  dual_pc(nullptr)
547 {
548  Initialize();
549 }
550 
552 {
553  delete block_op;
554  delete tr_B;
555  delete block_pc;
556  delete dual_pc;
557 }
558 
560  Vector& y) const
561 {
562  GMRESSolver * gmres;
563 #ifdef MFEM_USE_MPI
564  if (GetComm() != MPI_COMM_NULL)
565  {
566  gmres = new GMRESSolver(GetComm());
567  }
568  else
569 #endif
570  {
571  gmres = new GMRESSolver;
572  }
573  gmres->SetOperator(*block_op);
574  gmres->SetRelTol(rel_tol);
575  gmres->SetAbsTol(abs_tol);
576  gmres->SetMaxIter(max_iter);
577  gmres->SetPrintLevel(print_level);
578  gmres->SetPreconditioner(
579  const_cast<BlockDiagonalPreconditioner&>(*block_pc));
580 
581  gmres->Mult(x, y);
582  final_iter = gmres->GetNumIterations();
583  delete gmres;
584 }
585 
586 #ifdef MFEM_USE_MPI
588  HypreParMatrix& hA_,
589  HypreParMatrix& hB_,
590  int dimension,
591  bool reorder)
592  :
593  SchurConstrainedSolver(comm, hA_, hB_),
594  hA(hA_),
595  hB(hB_)
596 {
597  auto h_primal_pc = new HypreBoomerAMG(hA);
598  h_primal_pc->SetPrintLevel(0);
599  if (dimension > 0)
600  {
601  h_primal_pc->SetSystemsOptions(dimension, reorder);
602  }
603  primal_pc = h_primal_pc;
604 
605  HypreParMatrix * scaledB = new HypreParMatrix(hB);
606  Vector diagA;
607  hA.GetDiag(diagA);
608  HypreParMatrix * scaledBT = scaledB->Transpose();
609  scaledBT->InvScaleRows(diagA);
610  schur_mat = ParMult(scaledB, scaledBT);
611  schur_mat->CopyRowStarts();
612  schur_mat->CopyColStarts();
613  auto h_dual_pc = new HypreBoomerAMG(*schur_mat);
614  h_dual_pc->SetPrintLevel(0);
615  dual_pc = h_dual_pc;
616  delete scaledB;
617  delete scaledBT;
618 
621 }
622 
624 {
625  delete schur_mat;
626  delete primal_pc;
627 }
628 #endif
629 
630 void ConstrainedSolver::Initialize()
631 {
632  height = A.Height() + B.Height();
633  width = A.Width() + B.Height();
634 
635  workb.SetSize(A.Height());
636  workx.SetSize(A.Height());
638  constraint_rhs = 0.0;
640 }
641 
642 #ifdef MFEM_USE_MPI
644  :
645  IterativeSolver(comm), A(A_), B(B_)
646 {
647  Initialize();
648 }
649 #endif
650 
652  :
653  A(A_), B(B_)
654 {
655  Initialize();
656 }
657 
659 {
660  MFEM_VERIFY(r.Size() == multiplier_sol.Size(), "Vector is wrong size!");
661  constraint_rhs = r;
662 }
663 
664 void ConstrainedSolver::Mult(const Vector& f, Vector &x) const
665 {
666  Vector pworkb(A.Height() + B.Height());
667  Vector pworkx(A.Height() + B.Height());
668  pworkb = 0.0;
669  pworkx = 0.0;
670  for (int i = 0; i < f.Size(); ++i)
671  {
672  pworkb(i) = f(i);
673  pworkx(i) = x(i);
674  }
675  for (int i = 0; i < B.Height(); ++i)
676  {
677  pworkb(f.Size() + i) = constraint_rhs(i);
678  }
679 
680  LagrangeSystemMult(pworkb, pworkx);
681 
682  for (int i = 0; i < f.Size(); ++i)
683  {
684  x(i) = pworkx(i);
685  }
686  for (int i = 0; i < B.Height(); ++i)
687  {
688  multiplier_sol(i) = pworkx(f.Size() + i);
689  }
690 }
691 
693  Vector& x_and_lambda) const
694 {
695  workb.MakeRef(const_cast<Vector&>(f_and_r), 0);
696  workx.MakeRef(x_and_lambda, 0);
697  Vector ref_constraint_rhs(f_and_r.GetData() + A.Height(), B.Height());
698  constraint_rhs = ref_constraint_rhs;
699  Mult(workb, workx);
700  Vector ref_constraint_sol(x_and_lambda.GetData() + A.Height(), B.Height());
701  GetMultiplierSolution(ref_constraint_sol);
702 }
703 
704 /* Helper routine to reduce code duplication - given a node (which MFEM
705  sometimes calls a "dof"), this returns what normal people call a dof but
706  which MFEM sometimes calls a "vdof" - note that MFEM's naming conventions
707  regarding this are not entirely consistent. In parallel, this always
708  returns the "truedof" in parallel numbering. */
710  int node, bool parallel, int d=0)
711 {
712 #ifdef MFEM_USE_MPI
713  if (parallel)
714  {
715  ParFiniteElementSpace* pfespace =
716  dynamic_cast<ParFiniteElementSpace*>(&fespace);
717  if (pfespace)
718  {
719  const int vdof = pfespace->DofToVDof(node, d);
720  return pfespace->GetLocalTDofNumber(vdof);
721  }
722  else
723  {
724  MFEM_ABORT("Asked for parallel form of serial object!");
725  return -1;
726  }
727  }
728  else
729 #endif
730  {
731  return fespace.DofToVDof(node, d);
732  }
733 }
734 
736  Array<int>& constrained_att,
737  Array<int>& constraint_rowstarts,
738  bool parallel)
739 {
740  int dim = fespace.GetVDim();
741 
742  // dof_constraint maps a dof (column of the constraint matrix) to
743  // a block-constraint
744  // the indexing is by tdof, but a single tdof uniquely identifies a node
745  // so we only store one tdof independent of dimension
746  std::map<int, int> dof_bconstraint;
747  // constraints[j] is a map from attribute to row number,
748  // the j itself is the index of a block-constraint
749  std::vector<std::map<int, int> > constraints;
750  int n_bconstraints = 0;
751  int n_rows = 0;
752  for (int att : constrained_att)
753  {
754  // identify tdofs on constrained boundary
755  std::set<int> constrained_tdofs;
756  for (int i = 0; i < fespace.GetNBE(); ++i)
757  {
758  if (fespace.GetBdrAttribute(i) == att)
759  {
760  Array<int> nodes;
761  // get nodes on boundary (MFEM sometimes calls these dofs, what
762  // we call dofs it calls vdofs)
763  fespace.GetBdrElementDofs(i, nodes);
764  for (auto k : nodes)
765  {
766  // get the (local) dof number corresponding to
767  // the x-coordinate dof for node k
768  int tdof = CanonicalNodeNumber(fespace, k, parallel);
769  if (tdof >= 0) { constrained_tdofs.insert(tdof); }
770  }
771  }
772  }
773  // fill in the maps identifying which constraints (rows) correspond to
774  // which tdofs
775  for (auto k : constrained_tdofs)
776  {
777  auto it = dof_bconstraint.find(k);
778  if (it == dof_bconstraint.end())
779  {
780  // build new block constraint
781  dof_bconstraint[k] = n_bconstraints++;
782  constraints.emplace_back();
783  constraints.back()[att] = n_rows++;
784  }
785  else
786  {
787  // add tdof to existing block constraint
788  constraints[it->second][att] = n_rows++;
789  }
790  }
791  }
792 
793  // reorder so block-constraints eliminated together are grouped together in
794  // adjacent rows
795  {
796  std::map<int, int> reorder_rows;
797  int new_row = 0;
798  constraint_rowstarts.DeleteAll();
799  constraint_rowstarts.Append(0);
800  for (auto& it : dof_bconstraint)
801  {
802  int bconstraint_index = it.second;
803  bool nconstraint = false;
804  for (auto& att_it : constraints[bconstraint_index])
805  {
806  auto rrit = reorder_rows.find(att_it.second);
807  if (rrit == reorder_rows.end())
808  {
809  nconstraint = true;
810  reorder_rows[att_it.second] = new_row++;
811  }
812  }
813  if (nconstraint) { constraint_rowstarts.Append(new_row); }
814  }
815  MFEM_VERIFY(new_row == n_rows, "Remapping failed!");
816  for (auto& constraint_map : constraints)
817  {
818  for (auto& it : constraint_map)
819  {
820  it.second = reorder_rows[it.second];
821  }
822  }
823  }
824 
825  SparseMatrix * out = new SparseMatrix(n_rows, fespace.GetTrueVSize());
826 
827  // fill in constraint matrix with normal vector information
828  Vector nor(dim);
829  // how many times we have seen a node (key is truek)
830  std::map<int, int> node_visits;
831  for (int i = 0; i < fespace.GetNBE(); ++i)
832  {
833  int att = fespace.GetBdrAttribute(i);
834  if (constrained_att.FindSorted(att) != -1)
835  {
837  const FiniteElement * fe = fespace.GetBE(i);
838  const IntegrationRule& nodes = fe->GetNodes();
839 
840  Array<int> dofs;
841  fespace.GetBdrElementDofs(i, dofs);
842  MFEM_VERIFY(dofs.Size() == nodes.Size(),
843  "Something wrong in finite element space!");
844 
845  for (int j = 0; j < dofs.Size(); ++j)
846  {
847  Tr->SetIntPoint(&nodes[j]);
848  // the normal returned in the next line is scaled by h, which is
849  // probably what we want in most applications
850  CalcOrtho(Tr->Jacobian(), nor);
851 
852  int k = dofs[j];
853  int truek = CanonicalNodeNumber(fespace, k, parallel);
854  if (truek >= 0)
855  {
856  auto nv_it = node_visits.find(truek);
857  if (nv_it == node_visits.end())
858  {
859  node_visits[truek] = 1;
860  }
861  else
862  {
863  node_visits[truek]++;
864  }
865  int visits = node_visits[truek];
866  int bconstraint = dof_bconstraint[truek];
867  int row = constraints[bconstraint][att];
868  for (int d = 0; d < dim; ++d)
869  {
870  int inner_truek = CanonicalNodeNumber(fespace, k,
871  parallel, d);
872  if (visits == 1)
873  {
874  out->Add(row, inner_truek, nor[d]);
875  }
876  else
877  {
878  out->SetColPtr(row);
879  const double pv = out->SearchRow(inner_truek);
880  const double scaling = ((double) (visits - 1)) /
881  ((double) visits);
882  // incremental average, based on how many times
883  // this node has been visited
884  out->Set(row, inner_truek,
885  scaling * pv + (1.0 / visits) * nor[d]);
886  }
887 
888  }
889  }
890  }
891  }
892  }
893  out->Finalize();
894 
895  return out;
896 }
897 
898 #ifdef MFEM_USE_MPI
900  Array<int>& constrained_att,
901  Array<int>& constraint_rowstarts)
902 {
903  return BuildNormalConstraints(fespace, constrained_att,
904  constraint_rowstarts, true);
905 }
906 #endif
907 
908 }
Abstract class for all finite elements.
Definition: fe.hpp:243
HypreParMatrix & hA
T * HostReadWrite()
Shortcut for mfem::ReadWrite(a.GetMemory(), a.Size(), false).
Definition: array.hpp:320
int Size() const
Return the logical size of the array.
Definition: array.hpp:134
void Eliminate(const Vector &in, Vector &out) const
Definition: constraints.cpp:51
void Add(const int i, const int j, const double a)
Definition: sparsemat.cpp:2577
MPI_Comm GetComm() const
MPI communicator.
Definition: hypre.hpp:472
Class for an integration rule - an Array of IntegrationPoint.
Definition: intrules.hpp:90
void Mult(const Vector &x, Vector &y) const override
Solve for given .
int DofToVDof(int dof, int vd, int ndofs=-1) const
Definition: fespace.cpp:233
void Unique()
Removes duplicities from a sorted array. This requires operator== to be defined for T...
Definition: array.hpp:252
int GetNumIterations() const
Definition: solvers.hpp:103
MPI_Comm GetComm() const
Return the associated MPI communicator, or MPI_COMM_NULL if no communicator is set.
Definition: solvers.hpp:120
HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P)
Returns the matrix P^t * A * P.
Definition: hypre.cpp:2485
Array< int > & RowOffsets()
Return the row offsets for block starts.
SparseMatrix * ParBuildNormalConstraints(ParFiniteElementSpace &fespace, Array< int > &constrained_att, Array< int > &constraint_rowstarts)
Parallel wrapper for BuildNormalConstraints.
Solve constrained system by solving original mixed system; see ConstrainedSolver. ...
EliminationSolver(HypreParMatrix &A, SparseMatrix &B, Array< int > &primary_dofs, Array< int > &secondary_dofs)
Constructor, with explicit splitting into primary/secondary dofs.
void Mult(const Vector &x, Vector &y) const override
Operator application: y=A(x).
Definition: constraints.cpp:95
virtual void Finalize(int skip_zeros=1)
Finalize the matrix initialization, switching the storage format from LIL to CSR. ...
Definition: sparsemat.hpp:467
SparseMatrix * BuildNormalConstraints(FiniteElementSpace &fespace, Array< int > &constrained_att, Array< int > &constraint_rowstarts, bool parallel)
Build a matrix constraining normal components to zero.
void SetSize(int s)
Resize the vector to size s.
Definition: vector.hpp:513
double & SearchRow(const int col)
Perform a fast search for an entry in the &quot;current row&quot;. See SetColPtr().
Definition: sparsemat.hpp:790
bool Empty() const
Check if the SparseMatrix is empty.
Definition: sparsemat.hpp:177
SchurConstrainedHypreSolver(MPI_Comm comm, HypreParMatrix &hA_, HypreParMatrix &hB_, int dimension=0, bool reorder=false)
void Mult(const Table &A, const Table &B, Table &C)
C = A * B (as boolean matrices)
Definition: table.cpp:476
int Width() const
Get the width (size of input) of the Operator. Synonym with NumCols().
Definition: operator.hpp:72
int CanonicalNodeNumber(FiniteElementSpace &fespace, int node, bool parallel, int d=0)
int * GetJ()
Return the array J.
Definition: sparsemat.hpp:185
EliminationProjection(const Operator &A, Array< Eliminator * > &eliminators)
Definition: constraints.cpp:86
An abstract class to solve the constrained system subject to the constraint .
Definition: constraints.hpp:53
void SetIntPoint(const IntegrationPoint *ip)
Set the integration point ip that weights and Jacobians will be evaluated at.
Definition: eltrans.hpp:85
void GetSubVector(const Array< int > &dofs, Vector &elemvect) const
Extract entries listed in dofs to the output Vector elemvect.
Definition: vector.cpp:525
void Initialize(HypreParMatrix &A, HypreParMatrix &B)
Data type dense matrix using column-major storage.
Definition: densemat.hpp:23
int Size() const
Returns the size of the vector.
Definition: vector.hpp:190
int GetBdrAttribute(int i) const
Definition: fespace.hpp:614
int * GetI()
Return the array I.
Definition: sparsemat.hpp:180
virtual void Mult(const Vector &x, Vector &y) const =0
Operator application: y=A(x).
Abstract parallel finite element space.
Definition: pfespace.hpp:28
double * HostReadWrite()
Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
Definition: densemat.hpp:405
HypreParMatrix * h_explicit_operator
Eliminator(const SparseMatrix &B, const Array< int > &lagrange_dofs, const Array< int > &primary_tdofs, const Array< int > &secondary_tdofs)
Definition: constraints.cpp:22
int GetLocalTDofNumber(int ldof) const
Definition: pfespace.cpp:819
void MultTranspose(const Vector &x, Vector &y) const override
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
double * GetData() const
Returns the matrix data array.
Definition: densemat.hpp:115
double * GetData() const
Return a pointer to the beginning of the Vector data.
Definition: vector.hpp:199
void CalcOrtho(const DenseMatrix &J, Vector &n)
Definition: densemat.cpp:2303
HypreParMatrix * ParMult(const HypreParMatrix *A, const HypreParMatrix *B, bool own_matrix)
Definition: hypre.cpp:2464
virtual Solver * BuildPreconditioner() const =0
Build preconditioner for penalized system.
void GetSubMatrix(const Array< int > &rows, const Array< int > &cols, DenseMatrix &subm) const
Definition: sparsemat.cpp:2676
void DeleteAll()
Delete the whole array.
Definition: array.hpp:841
Array< Eliminator * > eliminators
Operator & GetBlock(int i, int j)
Return a reference to block i,j.
virtual void MultTranspose(const Vector &x, Vector &y) const
Action of the transpose operator: y=A^t(x). The default behavior in class Operator is to generate an ...
Definition: operator.hpp:93
ConstrainedSolver(MPI_Comm comm, Operator &A_, Operator &B_)
double GetFinalNorm() const
Definition: solvers.hpp:105
double * GetData()
Return the element data, i.e. the array A.
Definition: sparsemat.hpp:190
virtual void Mult(const Vector &f, Vector &x) const override
Solve for given .
HYPRE_BigInt N() const
Returns the global number of columns.
Definition: hypre.hpp:523
void EliminateZeroRows()
If a row contains only zeros, set its diagonal to 1.
Definition: hypre.hpp:696
void LagrangeSecondary(const Vector &in, Vector &out) const
Maps Lagrange multipliers to secondary dofs, applies .
Definition: constraints.cpp:66
HYPRE_BigInt * GetRowStarts() const
Return the parallel row partitioning array.
Definition: hypre.hpp:583
virtual void Mult(const Vector &b, Vector &x) const
Operator application: y=A(x).
Definition: solvers.cpp:884
The BoomerAMG solver in hypre.
Definition: hypre.hpp:1387
void MultTranspose(const double *x, double *y) const
Multiply a vector with the transpose matrix.
Definition: densemat.cpp:203
void BuildGTilde(const Vector &g, Vector &gtilde) const
void SetPrintLevel(int print_lvl)
Definition: solvers.cpp:71
virtual void SetOperator(const Operator &op)
Also calls SetOperator for the preconditioner if there is one.
Definition: solvers.cpp:98
double f(const Vector &xvec)
Definition: lor_mms.hpp:32
int GetNBE() const
Returns number of boundary elements in the mesh.
Definition: fespace.hpp:576
Data type sparse matrix.
Definition: sparsemat.hpp:41
int Height() const
Get the height (size of output) of the Operator. Synonym with NumRows().
Definition: operator.hpp:66
int Append(const T &el)
Append element &#39;el&#39; to array, resize if necessary.
Definition: array.hpp:746
Perform elimination of a single constraint.
virtual IterativeSolver * BuildKrylov() const =0
Select krylov solver for penalized system.
double b
Definition: lissajous.cpp:42
const DenseMatrix & Jacobian()
Return the Jacobian matrix of the transformation at the currently set IntegrationPoint, using the method SetIntPoint().
Definition: eltrans.hpp:111
const Array< int > & PrimaryDofs() const
void SetMaxIter(int max_it)
Definition: solvers.hpp:100
void RecoverMultiplier(const Vector &primalrhs, const Vector &primalvars, Vector &lm) const
virtual void SetConstraintRHS(const Vector &r)
Set the right-hand side r for the constraint B x = r.
const IntegrationRule & GetNodes() const
Get a const reference to the nodes of the element.
Definition: fe.hpp:390
ElementTransformation * GetBdrElementTransformation(int i) const
Returns ElementTransformation for the i-th boundary element.
Definition: fespace.hpp:609
HypreParMatrix * Transpose() const
Returns the transpose of *this.
Definition: hypre.cpp:1471
virtual int GetTrueVSize() const
Return the number of vector true (conforming) dofs.
Definition: fespace.hpp:543
void ExplicitAssembly(DenseMatrix &mat) const
Return explicitly assembled in mat.
Definition: constraints.cpp:78
void GetDiag(Vector &diag) const
Get the local diagonal of the matrix.
Definition: hypre.cpp:1344
void Sort()
Sorts the array in ascending order. This requires operator&lt; to be defined for T.
Definition: array.hpp:244
void Solve(int m, int n, double *X) const
Definition: densemat.cpp:3019
virtual void GetBdrElementDofs(int bel, Array< int > &dofs) const
Returns indices of degrees of freedom for boundary element &#39;bel&#39;.
Definition: fespace.cpp:2418
int GetVDim() const
Returns vector dimension.
Definition: fespace.hpp:534
int GetConverged() const
Definition: solvers.hpp:104
void AddElementVector(const Array< int > &dofs, const Vector &elemvect)
Add elements of the elemvect Vector to the entries listed in dofs. Negative dof values cause the -dof...
Definition: vector.cpp:617
void EliminateTranspose(const Vector &in, Vector &out) const
Transpose of Eliminate(), applies .
Definition: constraints.cpp:58
void BuildExplicitOperator()
Internal utility routine; assembles eliminated matrix explicitly.
void SetAbsTol(double atol)
Definition: solvers.hpp:99
void SetRelTol(double rtol)
Definition: solvers.hpp:98
PenaltyConstrainedSolver(HypreParMatrix &A, SparseMatrix &B, double penalty_)
Abstract base class for iterative solver.
Definition: solvers.hpp:66
void Transpose()
(*this) = (*this)^t
Definition: densemat.cpp:1374
void GetMultiplierSolution(Vector &lambda) const
Return the Lagrange multiplier solution in lambda.
Definition: constraints.hpp:76
Class FiniteElementSpace - responsible for providing FEM view of the mesh, mainly managing the set of...
Definition: fespace.hpp:87
void SetColPtr(const int row) const
Initialize the SparseMatrix for fast access to the entries of the given row which becomes the &quot;curren...
Definition: sparsemat.hpp:736
int Find(const T &el) const
Return the first index where &#39;el&#39; is found; return -1 if not found.
Definition: array.hpp:805
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition: array.hpp:674
void Set(const int i, const int j, const double a)
Definition: sparsemat.cpp:2558
HYPRE_Int HYPRE_BigInt
virtual void LagrangeSystemMult(const Vector &f_and_r, Vector &x_and_lambda) const
Solve for (x, lambda) given (f, r)
GMRES method.
Definition: solvers.hpp:348
SchurConstrainedSolver(MPI_Comm comm, Operator &A_, Operator &B_, Solver &primal_pc_)
HypreParMatrix * ParAdd(const HypreParMatrix *A, const HypreParMatrix *B)
Returns the matrix A + B.
Definition: hypre.cpp:2425
virtual Solver * BuildPreconditioner() const =0
Build preconditioner for eliminated system.
int height
Dimension of the output / number of rows in the matrix.
Definition: operator.hpp:27
EliminationProjection * projector
void Mult(const Vector &x, Vector &y) const override
Solve for given .
HYPRE_BigInt GetGlobalNumRows() const
Return the global number of rows.
Definition: hypre.hpp:573
HYPRE_BigInt * ColPart()
Returns the column partitioning.
Definition: hypre.hpp:511
void InvScaleRows(const Vector &s)
Scale the local row i by 1./s(i)
Definition: hypre.cpp:1841
IterativeSolver * krylov
int dim
Definition: ex24.cpp:53
bool Factor(int m, double TOL=0.0)
Compute the LU factorization of the current matrix.
Definition: densemat.cpp:2863
virtual void LagrangeSystemMult(const Vector &x, Vector &y) const override
Solve for (x, lambda) given (f, r)
virtual void SetOperator(const Operator &op)=0
Set/update the solver for the given operator.
virtual IterativeSolver * BuildKrylov() const =0
Select krylov solver for eliminated system.
Vector data type.
Definition: vector.hpp:60
void Mult(const double *x, double *y) const
Matrix vector multiplication.
Definition: densemat.cpp:175
const Array< int > & LagrangeDofs() const
void MakeRef(Vector &base, int offset, int size)
Reset the Vector to be a reference to a sub-vector of base.
Definition: vector.hpp:577
HYPRE_Int Mult(HypreParVector &x, HypreParVector &y, double alpha=1.0, double beta=0.0) const
Computes y = alpha * A * x + beta * y.
Definition: hypre.cpp:1529
virtual void SetPreconditioner(Solver &pr)
This should be called before SetOperator.
Definition: solvers.cpp:92
BlockDiagonalPreconditioner * block_pc
Base class for solvers.
Definition: operator.hpp:648
const FiniteElement * GetBE(int i) const
Returns pointer to the FiniteElement in the FiniteElementCollection associated with i&#39;th boundary fac...
Definition: fespace.cpp:2686
SparseMatrix * AssembleExact() const
Assemble this projector as a (processor-local) SparseMatrix.
void SetSize(int s)
Change the size of the DenseMatrix to s x s.
Definition: densemat.hpp:105
OutStream out(std::cout)
Global stream used by the library for standard output. Initially it uses the same std::streambuf as s...
Definition: globals.hpp:66
Abstract operator.
Definition: operator.hpp:24
Wrapper for hypre&#39;s ParCSR matrix class.
Definition: hypre.hpp:277
TransposeOperator * tr_B
void LagrangeSecondaryTranspose(const Vector &in, Vector &out) const
Transpose of LagrangeSecondary()
Definition: constraints.cpp:72
int width
Dimension of the input / number of columns in the matrix.
Definition: operator.hpp:28
void SetBlock(int iRow, int iCol, Operator *op, double c=1.0)
Add a block op in the block-entry (iblock, jblock).
const Array< int > & SecondaryDofs() const
void SetDiagonalBlock(int iblock, Operator *op)
Add a square block op in the block-entry (iblock, iblock).
double * data
Definition: densemat.hpp:532