G+Smo  24.08.0
Geometry + Simulation Modules
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
gsPatchPreconditionersCreator.hpp
Go to the documentation of this file.
1 
13 #pragma once
14 
15 #include <gsSolver/gsSumOp.h>
16 #include <gsSolver/gsProductOp.h>
17 #include <gsSolver/gsKroneckerOp.h>
18 #include <gsSolver/gsMatrixOp.h>
21 
22 namespace gismo
23 {
24 
25 namespace {
26 
27 template<typename T>
28 gsBoundaryConditions<T> boundaryConditionsForDirection( const gsBoundaryConditions<T>& bc, index_t direction )
29 {
30  gsBoundaryConditions<T> result;
31 
32  for ( index_t i = 1; i <= 2; ++i)
33  {
34  patchSide global(0,i+2*direction), local(0,i);
35  const boundary_condition<T>* cond = bc.getConditionFromSide(global);
36  if (cond)
37  result.addCondition(local,cond->type(),cond->function());
38  }
39  return result;
40 }
41 
42 template<typename T>
43 void eliminateDirichlet1D(const gsBoundaryConditions<T>& bc,
44  const gsOptionList& opt, gsSparseMatrix<T> & result)
45 {
46  dirichlet::strategy ds = (dirichlet::strategy)opt.askInt("DirichletStrategy",dirichlet::elimination);
47  if (ds == dirichlet::elimination)
48  {
49  patchSide west(0,boundary::west), east(0,boundary::east);
50  index_t i = 0;
51  if (bc.getConditionFromSide( west ) && bc.getConditionFromSide( west )->type() == condition_type::dirichlet ) i += 1;
52  if (bc.getConditionFromSide( east ) && bc.getConditionFromSide( east )->type() == condition_type::dirichlet ) i += 2;
53  if (i%2 + i/2 >= result.rows() || i%2 + i/2 >= result.cols())
54  result.resize(0,0);
55  else switch ( i )
56  {
57  case 0: break;
58  case 1: result = result.block( 1, 1, result.rows()-1, result.cols()-1 ); break;
59  case 2: result = result.block( 0, 0, result.rows()-1, result.cols()-1 ); break;
60  case 3: result = result.block( 1, 1, result.rows()-2, result.cols()-2 ); break;
61  }
62  }
63  else
64  GISMO_ERROR("Unknown Dirichlet strategy.");
65 }
66 
67 template<typename T>
68 gsSparseMatrix<T> assembleMass(const gsBasis<T>& basis)
69 {
70  gsExprAssembler<T> mass(1,1);
71  gsMultiBasis<T> mb(basis);
72  mass.setIntegrationElements(mb);
73  typename gsExprAssembler<T>::space u = mass.getSpace(mb);
74  mass.initMatrix();
75  mass.assemble( u * u.tr() );
76  gsSparseMatrix<T> result;
77  mass.matrix_into(result);
78  return result;
79 }
80 
81 template<typename T>
82 gsSparseMatrix<T> assembleStiffness(const gsBasis<T>& basis)
83 {
84  gsExprAssembler<T> stiff(1,1);
85  gsMultiBasis<T> mb(basis);
86  stiff.setIntegrationElements(mb);
87  typename gsExprAssembler<T>::space u = stiff.getSpace(mb);
88  stiff.initMatrix();
89  stiff.assemble( grad(u) * grad(u).tr() );
90  gsSparseMatrix<T> result;
91  stiff.matrix_into(result);
92  return result;
93 }
94 
95 
96 template<typename T>
97 std::vector< gsSparseMatrix<T> > assembleTensorMass(
98  const gsBasis<T>& basis,
99  const gsBoundaryConditions<T>& bc,
100  const gsOptionList& opt
101  )
102 {
103  const index_t d = basis.dim();
104  std::vector< gsSparseMatrix<T> > result(d);
105  for ( index_t i=0; i!=d; ++i )
106  {
107  result[i] = assembleMass(basis.component(d-1-i));
108  eliminateDirichlet1D(boundaryConditionsForDirection(bc,d-1-i), opt, result[i]);
109  }
110  return result;
111 }
112 
113 template<typename T>
114 std::vector< gsSparseMatrix<T> > assembleTensorStiffness(
115  const gsBasis<T>& basis,
116  const gsBoundaryConditions<T>& bc,
117  const gsOptionList& opt
118  )
119 {
120  const index_t d = basis.dim();
121  std::vector< gsSparseMatrix<T> > result(d);
122  for ( index_t i=0; i!=d; ++i )
123  {
124  result[i] = assembleStiffness(basis.component(d-1-i));
125  eliminateDirichlet1D(boundaryConditionsForDirection(bc,d-1-i), opt, result[i]);
126  }
127  return result;
128 }
129 
130 } // anonymous namespace
131 
132 template<typename T>
134  const gsBasis<T>& basis,
135  const gsBoundaryConditions<T>& bc,
136  const gsOptionList& opt
137  )
138 {
139  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
140  const index_t d = local_mass.size();
141  gsSparseMatrix<T> result = local_mass[d-1];
142  for (index_t i=d-2; i>-1; --i)
143  result = local_mass[i].kron(result);
144  return result;
145 }
146 
147 template<typename T>
148 typename gsPatchPreconditionersCreator<T>::OpUPtr gsPatchPreconditionersCreator<T>::massMatrixOp(
149  const gsBasis<T>& basis,
150  const gsBoundaryConditions<T>& bc,
151  const gsOptionList& opt
152  )
153 {
154  const index_t d = basis.dim();
155 
156  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
157 
158  std::vector<OpPtr> local_mass_op(d);
159  for (index_t i=0; i<d; ++i)
160  local_mass_op[i] = makeMatrixOp(local_mass[i].moveToPtr());
161 
162  return gsKroneckerOp<T>::make(local_mass_op);
163 }
164 
165 template<typename T>
166 typename gsPatchPreconditionersCreator<T>::OpUPtr gsPatchPreconditionersCreator<T>::massMatrixInvOp(
167  const gsBasis<T>& basis,
168  const gsBoundaryConditions<T>& bc,
169  const gsOptionList& opt
170  )
171 {
172  const index_t d = basis.dim();
173 
174  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
175 
176  std::vector<OpPtr> local_mass_op(d);
177  for (index_t i=0; i<d; ++i)
178  local_mass_op[i] = makeSparseCholeskySolver(local_mass[i]);
179 
180  return gsKroneckerOp<T>::make(local_mass_op);
181 }
182 
183 template<typename T>
185  const gsBasis<T>& basis,
186  const gsBoundaryConditions<T>& bc,
187  const gsOptionList& opt,
188  T alpha,
189  T beta
190  )
191 {
192  const index_t d = basis.dim();
193 
194  std::vector< gsSparseMatrix<T> > local_stiff = assembleTensorStiffness(basis, bc, opt);
195  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
196 
197  if ( beta!=1 )
198  {
199  for (index_t i=0; i!=d; ++i)
200  local_stiff[i] *= beta;
201  }
202 
203  gsSparseMatrix<T> K = give(local_stiff[d-1]);
204  gsSparseMatrix<T> M = give(local_mass [d-1]);
205 
206  for (index_t i=d-2; i>-1; --i)
207  {
208  K = local_mass[i].kron(K);
209  K += local_stiff[i].kron(M);
210  if ( i != 0 || alpha != 0 )
211  M = local_mass[i].kron(M);
212  }
213  if (alpha==1)
214  K += M;
215  else if (alpha!=0)
216  K += alpha * M;
217  return K;
218 }
219 
220 template<typename T>
221 typename gsPatchPreconditionersCreator<T>::OpUPtr gsPatchPreconditionersCreator<T>::stiffnessMatrixOp(
222  const gsBasis<T>& basis,
223  const gsBoundaryConditions<T>& bc,
224  const gsOptionList& opt,
225  T alpha,
226  T beta
227  )
228 {
229  const index_t d = basis.dim();
230 
231  std::vector< gsSparseMatrix<T> > local_stiff = assembleTensorStiffness(basis, bc, opt);
232  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
233 
234  std::vector<OpUPtr> local_stiff_op(d);
235  std::vector<OpPtr > local_mass_op (d);
236  for (index_t i=0; i<d; ++i)
237  {
238  if (beta!=1)
239  local_stiff[i] *= beta;
240  local_stiff_op[i] = makeMatrixOp(local_stiff[i].moveToPtr());
241  local_mass_op [i] = makeMatrixOp(local_mass [i].moveToPtr());
242  }
243  OpUPtr K = give(local_stiff_op[0]);
244  OpPtr M = give(local_mass_op [0]);
245 
246  for (index_t i=1; i<d; ++i)
247  {
248  K = gsSumOp<T>::make(
249  gsKroneckerOp<T>::make( give(K), local_mass_op [i] ),
250  gsKroneckerOp<T>::make( M, give(local_stiff_op[i]) )
251  );
252  if ( i < d-1 || alpha != 0 )
253  M = gsKroneckerOp<T>::make(M, local_mass_op[i]);
254  }
255  if (alpha==1)
256  K = gsSumOp<T>::make( give(K), M );
257  else if (alpha!=0)
258  K = gsSumOp<T>::make( give(K), gsScaledOp<T>::make( M, alpha ) );
259 
260  return K;
261 }
262 
263 template<typename T>
264 typename gsPatchPreconditionersCreator<T>::OpUPtr gsPatchPreconditionersCreator<T>::fastDiagonalizationOp(
265  const gsBasis<T>& basis,
266  const gsBoundaryConditions<T>& bc,
267  const gsOptionList& opt,
268  T alpha,
269  T beta,
270  T gamma
271  )
272 {
273  GISMO_ASSERT ( beta != 0, "gsPatchPreconditionersCreator<T>::fastDiagonalizationOp() does not work for beta==0." );
274 
275  const index_t d = basis.dim();
276 
277  // Assemble univariate
278  std::vector< gsSparseMatrix<T> > local_stiff = assembleTensorStiffness(basis, bc, opt);
279  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
280 
281  if (beta!=0)
282  {
283  for (index_t i=0; i!=d; ++i)
284  local_stiff[i] *= beta;
285  }
286 
287  // Determine overall size
288  index_t sz = 1;
289  for ( index_t i=0; i<d; ++i )
290  sz *= local_stiff[i].rows();
291 
292  // Initialize the diagonal with 1
293  gsMatrix<T> diag;
294  diag.setConstant(sz,1,alpha); // This is the pure-mass part!
295 
296  index_t glob = sz; // Indexing value for setting up the Kronecker product
297 
298  typedef typename gsMatrix<T>::GenSelfAdjEigenSolver EVSolver;
299  typedef typename EVSolver::EigenvectorsType evMatrix;
300  typedef typename EVSolver::RealVectorType evVector;
301  EVSolver ges;
302 
303  std::vector<OpPtr> Qop(d);
304  std::vector<OpPtr> QTop(d);
305  gsMatrix<T> ev;
306 
307  T avg_term = gamma;
308 
309  // Now, setup the Q's and update the D's
310  for ( index_t i=0; i<d; ++i )
311  {
312  // Solve generalized eigenvalue problem
313  ges.compute(local_stiff[i], local_mass[i], gsEigen::ComputeEigenvectors);
314  // Q^T M Q = I, or M = Q^{-T} Q^{-1}
315  // Q^T K Q = D, or K = Q^{-T} D Q^{-1}
316 
317  if (gamma != (T)(0))
318  {
319  gsMatrix<T> etrans = ges.eigenvectors().transpose()*local_mass[i]*gsMatrix<T>::Ones(local_mass[i].rows(),1);
320  GISMO_ASSERT((etrans.block(1, 0, etrans.rows()-1, 1).array() < (T)(1)/100000000).all(),
321  "gsPatchPreconditionerCreator::fastDiagonalizationOp: gamma!=0 only allowed for pure Neumann.");
322  avg_term *= etrans(0,0) * etrans(0,0);
323  }
324 
325  // From the eigenvalues, we setup the matrix D already in an Kroneckerized way.
326  const evVector & D = ges.eigenvalues();
327 
328  const index_t loc = D.rows();
329  glob /= loc;
330  const index_t glob2 = sz / loc / glob;
331 
332  for ( index_t l=0; l<loc; ++l )
333  for ( index_t m=0; m<glob; ++m )
334  for ( index_t n=0; n<glob2; ++n )
335  diag( m + l*glob + n*loc*glob, 0 ) += D(l,0);
336 
337  // Finally, we store the eigenvectors
338  ev.swap(const_cast<evMatrix&>(ges.eigenvectors()));
339 
340  // These are the operators representing the eigenvectors
341  typename gsMatrixOp< gsMatrix<T> >::Ptr matrOp = makeMatrixOp( ev.moveToPtr() );
342  Qop [i] = matrOp;
343  // Here we are safe as long as we do not want to apply QTop after Qop got destroyed.
344  QTop[i] = makeMatrixOp( matrOp->matrix().transpose() );
345  }
346 
347  GISMO_ASSERT( glob == 1,
348  "gsPatchPreconditionerCreator::fastDiagonalizationOp: Internal error." );
349 
350  diag(0,0) += avg_term;
351 
352  for ( index_t l=0; l<sz; ++l )
353  diag( l, 0 ) = (T)(1)/diag( l, 0 );
354 
355  memory::unique_ptr< gsEigen::DiagonalMatrix<T,Dynamic> > diag_mat( new gsEigen::DiagonalMatrix<T,Dynamic>( give(diag) ) );
356 
357  return gsProductOp<T>::make(
359  makeMatrixOp(give(diag_mat)),
361  );
362 }
363 
364 namespace {
365 
366 // Get the tilde basis
367 template<typename T>
368 void tildeSpaceBasis_oneside(const gsTensorBSplineBasis<1,T>& basis, bool isLeftHandSide, gsMatrix<T>& tildeBasis, gsMatrix<T>& complBasis, bool bc = 0, const bool odd = true)
369 {
370  // bc == false: Neumann (or any other not-eliminating bc), bc == true: Dirichlet
371 
372  const index_t b = (index_t)bc;
373  const index_t p = basis.degree();
374  const T h = basis.knots().maxIntervalLength();
375 
376  if (p-b<=0)
377  {
378  tildeBasis.resize(0,0); complBasis.resize(0,0);
379  return;
380  }
381 
382  const T u = (isLeftHandSide ? basis.knots().first() : basis.knots().last());
383  gsMatrix<T> U(1,1);
384  U(0,0) = u;
385 
386  std::vector< gsMatrix<T> > allDerivs;
387  basis.evalAllDers_into(U, p-1, allDerivs);
388 
389  // Collect all derivatives in matrix (rows: derivatives, columns: basis functions)
390  // normalize with h^(deriv)
391  // use only odd derivatives if odd = true and only even derivatives if odd = false
392  // skip last (on left) or first (on right) basis function since it's always in S-tilde
393  gsMatrix<T> derivs;
394  // If we have Dirichlet bc, we reduce the number of rows and columns by 1. We basically
395  // eliminate the first row and the first column (first basis function, 0th derivative)
396  // for the right-side, we have to remove the last basis function.
397  derivs.setZero(p-b, p-b);
398 
399  const index_t offset = (isLeftHandSide ? b : 1);
400 
401  index_t i_start;
402  if (odd) i_start = 1;
403  else i_start = 2*b;
404 
405  for (index_t i = i_start; i < p; i += 2)
406  for (index_t j = 0; j < p-b; ++j)
407  derivs(i-b, j) = math::pow(h, i) * allDerivs[i](j+offset);
408 
409  typename gsMatrix<T>::JacobiSVD svd = derivs.jacobiSvd(gsEigen::ComputeFullV);
410 
411  index_t n_tilde;
412  if (odd) n_tilde = (p + 1) / 2 - b;
413  else n_tilde = p / 2 - b;
414 
415  tildeBasis = svd.matrixV().rightCols(n_tilde);
416  complBasis = svd.matrixV().leftCols(p - b - n_tilde);
417 }
418 
419 // Compute a basis for S-tilde and one for its orthogonal complement
420 template<typename T>
421 void tildeSpaceBasis(const gsBasis<T>& basis, gsSparseMatrix<T>& B_tilde, gsSparseMatrix<T>& B_compl, const gsBoundaryConditions<T>& bc, const bool odd = true)
422 {
423  GISMO_ASSERT( nullptr != (dynamic_cast<const gsTensorBSplineBasis<1,T>*>(&basis)),
424  "gsPatchPreconditionersCreator<T>::getTildeSpaceBasisTransformation and "
425  "gsPatchPreconditionersCreator<T>::subspaceCorrectedMassSmootherOp only work with tensor-B-spline bases." );
426  const gsTensorBSplineBasis<1,T>& bbasis = static_cast<const gsTensorBSplineBasis<1,T>&>(basis);
427 
428  patchSide west(0,boundary::west), east(0,boundary::east);
429  bool bwest = ( bc.getConditionFromSide( west ) && bc.getConditionFromSide( west )->type() == condition_type::dirichlet );
430  bool beast = ( bc.getConditionFromSide( east ) && bc.getConditionFromSide( east )->type() == condition_type::dirichlet );
431 
432  gsMatrix<T> b_L, b_compl_L;
433  gsMatrix<T> b_R, b_compl_R;
434 
435  // Contruct space with vanishing odd derivatives
436  tildeSpaceBasis_oneside(bbasis, true, b_L, b_compl_L, bwest, odd);
437  tildeSpaceBasis_oneside(bbasis, false, b_R, b_compl_R, beast, odd);
438 
439  const index_t n = bbasis.size() - (index_t)bwest - (index_t)beast;
440  const index_t n_L = b_L.cols();
441  const index_t m_L = b_L.rows();
442  const index_t n_R = b_R.cols();
443  const index_t m_R = b_R.rows();
444  const index_t n_c_L = b_compl_L.cols();
445  const index_t m_c_L = b_compl_L.rows();
446  const index_t n_c_R = b_compl_R.cols();
447  const index_t m_c_R = b_compl_R.rows();
448  const index_t n_I = n - n_L - n_R - n_c_L - n_c_R;
449 
450  //GISMO_ENSURE ( n_I >= 0, "tildeSpaceBasis: Too few knots for that spline degree." );
451  if ( n_I <= 0 )
452  {
453  static bool warned = false;
454  if (!warned)
455  {
456  gsWarn << "tildeSpaceBasis was called with too few knots for that spline degree.\n"
457  << "So, we assume that S_tilde is empty.\n";
458  warned = true;
459  }
460 
461  B_tilde.resize(n,0);
462 
463  gsSparseEntries<T> E_compl;
464  E_compl.reserve(n);
465  for (index_t i = 0; i < n; ++i)
466  E_compl.add(i,i,1.0);
467  B_compl.resize(n,n);
468  B_compl.setFrom(E_compl);
469  return;
470  }
471 
472  gsSparseEntries<T> E_tilde, E_compl;
473 
474  // put b_L into upper left block of S-tilde basis
475  for (index_t j = 0; j < n_L; ++j)
476  {
477  for (index_t i = 0; i < m_L; ++i)
478  E_tilde.add(i, j, b_L(i, j));
479  }
480  // fill identity matrix into interior part of S-tilde basis
481  for (index_t j = 0; j < n_I; ++j)
482  {
483  E_tilde.add(m_L + j, n_L + j, 1.0);
484  }
485  // put b_R into lower right block of S-tilde basis
486  for (index_t j = 0; j < n_R; ++j)
487  {
488  for (index_t i = 0; i < m_R; ++i)
489  E_tilde.add(m_L + n_I + i, n_L + n_I + j, b_R(i, j));
490  }
491  B_tilde.resize(n, n_L + n_I + n_R);
492  B_tilde.setFrom(E_tilde);
493 
494  // put b_compl_L into upper left block of complement basis
495  for (index_t j = 0; j < n_c_L; ++j)
496  {
497  for (index_t i = 0; i < m_c_L; ++i)
498  E_compl.add(i, j, b_compl_L(i, j));
499  }
500  // put b_compl_R into lower right block of complement basis
501  for (index_t j = 0; j < n_c_R; ++j)
502  {
503  for (index_t i = 0; i < m_c_R; ++i)
504  E_compl.add(m_c_L + n_I + i, n_c_L + j, b_compl_R(i, j));
505  }
506  B_compl.resize(n, n_c_L + n_c_R);
507  B_compl.setFrom(E_compl);
508 
509 }
510 
511 template<typename T>
512 void constructTildeSpaceBasis(
513  const gsBasis<T>& basis,
514  const gsBoundaryConditions<T>& bc,
515  const gsOptionList& opt,
516  std::vector< gsSparseMatrix<T> >& B_tilde,
517  std::vector< gsSparseMatrix<T> >& B_l2compl,
518  const bool odd = true
519  )
520 {
521  const index_t d = basis.dim();
522  B_tilde.resize(d);
523  B_l2compl.resize(d);
524  dirichlet::strategy ds = (dirichlet::strategy)opt.askInt("DirichletStrategy",dirichlet::elimination);
525  if (ds == dirichlet::elimination)
526  {
527  for ( index_t i=0; i<d; ++i )
528  tildeSpaceBasis(basis.component(d-1-i), B_tilde[i], B_l2compl[i], boundaryConditionsForDirection(bc,d-1-i), odd);
529  }
530  else
531  GISMO_ERROR("Unknown Dirichlet strategy.");
532 
533 }
534 
535 // Constructs a matrix for swapping a tensor product
536 // from A (x) B (x) C (x) D (x) E to A (x) D (x) C (x) B (x) E,
537 // where only the dimensions of those matrices have to be given.
538 // Note that also those A, B, etc. could be tensor products (here as dimension just provide the products)
539 // Note that also those A, B, etc. could vanish. Then just provide a 1 as dimension, i.e., a scalar.
540 // So, literally every thinkable swap is possible.
541 template<typename T>
542 gsSparseMatrix<T> kroneckerSwap( index_t e, index_t d, index_t c, index_t b, index_t a )
543 {
544  const index_t sz = a*b*c*d*e;
545  gsSparseMatrix<T> result(sz,sz);
546  gsSparseEntries<T> entries;
547  entries.reserve(sz);
548  for ( index_t i=0; i<a; ++i )
549  for ( index_t j=0; j<b; ++j )
550  for ( index_t k=0; k<c; ++k )
551  for ( index_t l=0; l<d; ++l )
552  for ( index_t m=0; m<e; ++m )
553  entries.add( i+a*(j+b*(k+c*(l+d*m))), i+a*(l+d*(k+c*(j+b*m))), 1. );
554 
555  result.setFrom(entries);
556  result.makeCompressed();
557 
558  return result;
559 }
560 
561 } // anonymous namespace
562 
563 template<typename T>
564 std::pair< std::vector< gsSparseMatrix<T> >, std::vector< gsSparseMatrix<T> > > gsPatchPreconditionersCreator<T>::getTildeSpaceBasisTransformation(
565  const gsBasis<T>& basis,
566  const gsBoundaryConditions<T>& bc,
567  const gsOptionList& opt
568  )
569 {
570  std::vector< gsSparseMatrix<T> > B_tilde, B_l2compl;
571  constructTildeSpaceBasis( basis, bc, opt, B_tilde, B_l2compl, !opt.askSwitch( "UseVanishingEvenDerivatives", false ) );
572  return std::pair< std::vector< gsSparseMatrix<T> >, std::vector< gsSparseMatrix<T> > >( B_tilde, B_l2compl );
573 }
574 
575 template<typename T>
576 typename gsPatchPreconditionersCreator<T>::OpUPtr gsPatchPreconditionersCreator<T>::subspaceCorrectedMassSmootherOp(
577  const gsBasis<T>& basis,
578  const gsBoundaryConditions<T>& bc,
579  const gsOptionList& opt,
580  T sigma,
581  T alpha,
582  T beta
583  )
584 {
585  GISMO_ASSERT ( beta != 0, "gsPatchPreconditionersCreator<T>::subspaceCorrectedMassSmootherOp() does not work for beta==0." );
586 
587  // Get some properties
588  const index_t d = basis.dim();
589  const T h = basis.getMinCellLength();
590 
591  // Assemble univariate
592  std::vector< gsSparseMatrix<T> > local_stiff = assembleTensorStiffness(basis, bc, opt);
593  std::vector< gsSparseMatrix<T> > local_mass = assembleTensorMass(basis, bc, opt);
594 
595  if (beta != 1)
596  {
597  for (index_t i=0; i!=d; ++i)
598  local_stiff[i] *= beta;
599  }
600 
601  // Setup of basis
602  std::vector< gsSparseMatrix<T> > B_tilde(d), B_l2compl(d);
603  constructTildeSpaceBasis(basis, bc, opt, B_tilde, B_l2compl);
604 
605  std::vector< gsSparseMatrix<T> > M_compl(d), K_compl(d), B_compl(d);
606  std::vector< typename gsLinearOperator<T>::Ptr > M_tilde_inv(d);
607  for ( index_t i=0; i<d; ++i )
608  {
609  // Transform the complement
610  typename gsLinearOperator<T>::Ptr M_inv = makeSparseCholeskySolver(local_mass[i]);
611  gsMatrix<T> B_compl_dense;
612  M_inv->apply( B_l2compl[i], B_compl_dense );
613  B_compl[i] = B_compl_dense.sparseView();
614 
615  // Setup of matrices and corresponding solvers
616  gsSparseMatrix<T> M_tilde = B_tilde[i].transpose() * local_mass[i] * B_tilde[i];
617  M_tilde_inv[i] = makeSparseCholeskySolver( M_tilde );
618  M_compl[i] = B_compl[i].transpose() * local_mass[i] * B_compl[i];
619  K_compl[i] = B_compl[i].transpose() * local_stiff[i] * B_compl[i];
620  }
621 
622  // Setup of final operator
623  typename gsSumOp<T>::uPtr result = gsSumOp<T>::make();
624 
625  for ( index_t type = 0; type < (1<<d); ++ type )
626  {
627  std::vector< typename gsLinearOperator<T>::Ptr > correction(0);
628  gsSparseMatrix<T> transfer;
629 
630  std::vector< gsSparseMatrix<T>* > transfers(d);
631 
632  index_t numberInteriors = 0;
633 
634  // Setup of transfer
635  for ( index_t j = 0; j<d; ++ j )
636  {
637  if ( type & ( 1 << j ) )
638  transfers[j] = &(B_compl[d-1-j]);
639  else
640  {
641  transfers[j] = &(B_tilde[d-1-j]);
642  ++numberInteriors;
643  }
644 
645  if ( j == 0 )
646  transfer = *(transfers[j]);
647  else
648  transfer = transfers[j]->kron(transfer);
649 
650  }
651 
652  // If the subspace is not present, ignore it.
653  if ( transfer.cols() == 0 )
654  continue;
655 
656  // Setup of swap, where the boundary part is shifted to the begin
657 
658  index_t left = 1, current = transfers[d-1]->cols(), right = 1;
659  for ( index_t j = 0; j < d-1; ++j )
660  left *= transfers[j]->cols();
661 
662  for ( index_t j = d-1; j >= 0; --j )
663  {
664  if ( type & ( 1 << j ) )
665  {
666  transfer = transfer * kroneckerSwap<T>( right, current, left, 1, 1 );
667  if ( j > 0 )
668  {
669  left *= current;
670  current = transfers[j-1]->cols();
671  left /= current;
672  }
673  }
674  else
675  {
676  if ( j > 0 )
677  {
678  right *= current;
679  current = transfers[j-1]->cols();
680  left /= current;
681  }
682  }
683 
684  }
685 
686  // Setup of interior correction
687  for ( index_t j = d-1; j>=0; --j )
688  {
689  if ( ! ( type & ( 1 << j ) ) )
690  correction.push_back( M_tilde_inv[d-1-j] );
691  }
692 
693  // If we are in the interior, we have to do the scaling here as there is no boundary correction.
694  if ( numberInteriors == d )
695  correction[0] = gsScaledOp<T>::make( correction[0], (T)(1)/( alpha + beta*(T)(numberInteriors)/(sigma*h*h) ) );
696 
697  // Setup of bondary correction
698  if ( numberInteriors < d )
699  {
700  gsSparseMatrix<T> bc_matrix;
701 
702  {
703  gsSparseMatrix<T> s(0,0);
704  for ( index_t k = d-1; k>=0; --k )
705  {
706  if ( type & ( 1 << k ) )
707  {
708  if ( s.rows() == 0 )
709  s = M_compl[d-1-k];
710  else
711  s = M_compl[d-1-k].kron(s);
712  }
713  }
714  bc_matrix = ( alpha + beta*(T)(numberInteriors)/(sigma*h*h) ) * s;
715  }
716 
717  for ( index_t j = d-1; j>=0; --j )
718  {
719  if ( type & ( 1 << j ) )
720  {
721  gsSparseMatrix<T> s(0,0);
722  for ( index_t k = d-1; k>=0; --k )
723  {
724  if ( type & ( 1 << k ) )
725  {
726  gsSparseMatrix<T>* chosenMatrix;
727  if ( j == k )
728  chosenMatrix = &(K_compl[d-1-k]);
729  else
730  chosenMatrix = &(M_compl[d-1-k]);
731 
732  if ( s.rows() == 0 )
733  s = *chosenMatrix;
734  else
735  s = s.kron(*chosenMatrix);
736  }
737  }
738  bc_matrix += s;
739  }
740  }
741 
742  correction.push_back(makeSparseCholeskySolver(bc_matrix));
743  }
744 
745  typename gsMatrixOp< gsSparseMatrix<T> >::Ptr transOp = makeMatrixOp(transfer.moveToPtr());
746  // Setup of whole operator
747  // The correction is the Kronecker-product of the operators in the vector correction.
748  result->addOperator(
750  makeMatrixOp( transOp->matrix().transpose() ),
751  gsKroneckerOp<T>::make( correction ),
752  transOp
753  )
754  );
755  }
756 
757  return give(result);
758 
759 }
760 
761 } // namespace gismo
static uPtr make(std::vector< BasePtr > ops)
Make command returning a smart pointer.
Definition: gsKroneckerOp.h:55
static uPtr make()
Make command returning a smart pointer.
Definition: gsProductOp.h:77
static uPtr make()
Make command returning a smart pointer.
Definition: gsSumOp.h:70
static OpUPtr fastDiagonalizationOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions(), T alpha=0, T beta=1, T gamma=0)
Definition: gsPatchPreconditionersCreator.hpp:264
static OpUPtr massMatrixOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions())
Definition: gsPatchPreconditionersCreator.hpp:148
Dirichlet type.
Definition: gsBoundaryConditions.h:31
static OpUPtr subspaceCorrectedMassSmootherOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions(), T sigma=(T)(12)/(T)(100), T alpha=0, T beta=1)
Definition: gsPatchPreconditionersCreator.hpp:576
uPtr moveToPtr()
This function returns a smart pointer to the matrix. After calling it, the matrix object becomes empt...
Definition: gsSparseMatrix.h:249
Provides a linear operator representing the Kronecker product of linear operators.
Simple adapter class to use a matrix (or matrix-like object) as a linear operator. Needed for the iterative method classes.
Definition: gsMatrixOp.h:33
short_t degree(short_t i) const
Degree with respect to the i-th variable. If the basis is a tensor product of (piecewise) polynomial ...
Definition: gsBSplineBasis.h:322
NestedMatrix matrix() const
Returns the matrix.
Definition: gsMatrixOp.h:84
Class for representing a Kronecker product of operators of type gsLinearOperator. ...
Definition: gsKroneckerOp.h:28
S give(S &x)
Definition: gsMemory.h:266
#define index_t
Definition: gsConfig.h:32
static uPtr make(BasePtr op, T scalar=1)
Make function returning a smart pointer.
Definition: gsLinearOperator.h:93
#define GISMO_ASSERT(cond, message)
Definition: gsDebug.h:89
static gsSparseMatrix< T > stiffnessMatrix(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions(), T alpha=0, T beta=1)
Definition: gsPatchPreconditionersCreator.hpp:184
virtual T getMinCellLength() const
Get the minimum mesh size, as expected for inverse inequalities.
Definition: gsBasis.hpp:663
virtual void evalAllDers_into(const gsMatrix< T > &u, int n, std::vector< gsMatrix< T > > &result, bool sameElement=false) const
Evaluate the nonzero functions and their derivatives up to order n at points u into result...
Definition: gsBSplineBasis.hpp:864
#define gsWarn
Definition: gsDebug.h:50
const KnotVectorType & knots(int i=0) const
Returns the knot vector of the basis.
Definition: gsBSplineBasis.h:369
static OpUPtr stiffnessMatrixOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions(), T alpha=0, T beta=1)
Definition: gsPatchPreconditionersCreator.hpp:221
static OpUPtr massMatrixInvOp(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions())
Definition: gsPatchPreconditionersCreator.hpp:166
memory::shared_ptr< gsLinearOperator > Ptr
Shared pointer for gsLinearOperator.
Definition: gsLinearOperator.h:33
memory::unique_ptr< gsSumOp > uPtr
Unique pointer for gsSumOp.
Definition: gsSumOp.h:33
GISMO_DEPRECATED index_t direction(index_t s)
Returns the parametric direction that corresponds to side s.
Definition: gsBoundary.h:1048
Class for representing the product of objects of type gsLinearOperator as gsLinearOperator.
Definition: gsProductOp.h:33
void addOperator(BasePtr op)
Add another operator.
Definition: gsSumOp.h:86
static std::pair< std::vector< gsSparseMatrix< T > >, std::vector< gsSparseMatrix< T > > > getTildeSpaceBasisTransformation(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions())
Definition: gsPatchPreconditionersCreator.hpp:564
A univariate B-spline basis.
Definition: gsBSplineBasis.h:69
Class containing a set of boundary conditions.
Definition: gsBoundaryConditions.h:341
EIGEN_STRONG_INLINE grad_expr< E > grad(const E &u)
The gradient of a variable.
Definition: gsExpressions.h:4492
#define GISMO_ERROR(message)
Definition: gsDebug.h:118
uPtr moveToPtr()
This function returns a smart pointer to the matrix. After calling it, the matrix object becomes empt...
Definition: gsMatrix.h:170
gsSparseMatrix kron(const gsSparseMatrix &other) const
Returns the Kronecker product of this with other.
Definition: gsSparseMatrix.h:414
virtual void apply(const gsMatrix< T > &input, gsMatrix< T > &x) const =0
apply the operator on the input vector and store the result in x
bool askSwitch(const std::string &label, const bool &value=false) const
Reads value for option label from options.
Definition: gsOptionList.cpp:128
Class which holds a list of parameters/options, and provides easy access to them. ...
Definition: gsOptionList.h:32
A class representing the product of gsLinearOperator s.
static gsSparseMatrix< T > massMatrix(const gsBasis< T > &basis, const gsBoundaryConditions< T > &bc=gsBoundaryConditions< T >(), const gsOptionList &opt=gsAssembler< T >::defaultOptions())
Definition: gsPatchPreconditionersCreator.hpp:133
Allows an operator to be multiplied with a scalar.
Definition: gsLinearOperator.h:77
Simple adapter classes to use matrices or linear solvers as gsLinearOperators.
Provides the sum of gsLinearOperator s as a gsLinearOperator.
Generic expressions matrix assembly.
A basis represents a family of scalar basis functions defined over a common parameter domain...
Definition: gsBasis.h:78
Provides declaration of TensorBSplineBasis abstract interface.