WaveBEM: Unsteady Nonlinear Potential Flow Solver for Ship-Wave Interaction.
bem_problem.cc
Go to the documentation of this file.
1 //---------------------------- step-34.cc ---------------------------
2 // $Id: step-34.cc 18734 2009-04-25 13:36:48Z heltai $
3 // Version: $Name$
4 //
5 // Copyright (C) 2009, 2010, 2011 by the deal.II authors
6 //
7 // This file is subject to QPL and may not be distributed
8 // without copyright and license information. Please refer
9 // to the file deal.II/doc/license.html for the text and
10 // further information on this license.
11 //
12 // Authors: Luca Heltai, Cataldo Manigrasso
13 //
14 //---------------------------- step-34.cc ---------------------------
15 
16 
17 #include "../include/bem_problem.h"
18 #include "../include/laplace_kernel.h"
19 
20 // @sect4{BEMProblem::BEMProblem and
21 // BEMProblem::read_parameters}
22 // The constructor initializes the
23 // variuous object in much the same
24 // way as done in the finite element
25 // programs such as step-4 or
26 // step-6. The only new ingredient
27 // here is the ParsedFunction object,
28 // which needs, at construction time,
29 // the specification of the number of
30 // components.
31 //
32 // For the exact solution the number
33 // of vector components is one, and
34 // no action is required since one is
35 // the default value for a
36 // ParsedFunction object. The wind,
37 // however, requires dim components
38 // to be specified. Notice that when
39 // declaring entries in a parameter
40 // file for the expression of the
41 // Functions::ParsedFunction, we need
42 // to specify the number of
43 // components explicitly, since the
44 // function
45 // Functions::ParsedFunction::declare_parameters
46 // is static, and has no knowledge of
47 // the number of components.
48 template <int dim>
50  BEMFMA<dim> &fma)
51  :
52  comp_dom(comp_dom), fma(fma)
53 {}
54 
55 template <int dim>
57 {
58  const unsigned int n_dofs = comp_dom.dh.n_dofs();
59 
60  neumann_matrix.reinit(n_dofs, n_dofs);
61  dirichlet_matrix.reinit(n_dofs, n_dofs);
62  system_rhs.reinit(n_dofs);
63  sol.reinit(n_dofs);
64  alpha.reinit(n_dofs);
65  serv_phi.reinit(n_dofs);
66  serv_dphi_dn.reinit(n_dofs);
67  serv_tmp_rhs.reinit(n_dofs);
68  preconditioner_band = 100;
69  preconditioner_sparsity_pattern.reinit (n_dofs,n_dofs,preconditioner_band);
70  is_preconditioner_initialized = false;
71 }
72 
73 template <int dim>
75 {
76 
77  // In the solver section, we set
78  // all SolverControl
79  // parameters. The object will then
80  // be fed to the GMRES solver in
81  // the solve_system() function.
82 
83  prm.enter_subsection("Solver");
85  prm.leave_subsection();
86 
87  prm.declare_entry("Solution method", "Direct",
88  Patterns::Selection("Direct|FMA"));
89 }
90 
91 template <int dim>
93 {
94 
95  prm.enter_subsection("Solver");
96  solver_control.parse_parameters(prm);
97  prm.leave_subsection();
98 
99  solution_method = prm.get("Solution method");
100 
101 
102 }
103 
104 
105 
106 template <int dim>
108 {
109  std::cout<<"(Directly) Assembling system matrices"<<std::endl;
110 
111  neumann_matrix = 0;
112  dirichlet_matrix = 0;
113 
114 
115 
116  std::vector<QGaussOneOverR<2> > sing_quadratures_3d;
117  for (unsigned int i=0; i<comp_dom.fe.dofs_per_cell; ++i)
118  sing_quadratures_3d.push_back
119  (QGaussOneOverR<2>(comp_dom.singular_quadrature_order,
120  comp_dom.fe.get_unit_support_points()[i], true));
121 
122 
123  // Next, we initialize an FEValues
124  // object with the quadrature
125  // formula for the integration of
126  // the kernel in non singular
127  // cells. This quadrature is
128  // selected with the parameter
129  // file, and needs to be quite
130  // precise, since the functions we
131  // are integrating are not
132  // polynomial functions.
133  FEValues<dim-1,dim> fe_v(*comp_dom.mapping,comp_dom.fe, *comp_dom.quadrature,
134  update_values |
135  update_cell_normal_vectors |
136  update_quadrature_points |
137  update_JxW_values);
138 
139  const unsigned int n_q_points = fe_v.n_quadrature_points;
140 
141  std::vector<unsigned int> local_dof_indices(comp_dom.fe.dofs_per_cell);
142 
143  // Unlike in finite element
144  // methods, if we use a collocation
145  // boundary element method, then in
146  // each assembly loop we only
147  // assemble the information that
148  // refers to the coupling between
149  // one degree of freedom (the
150  // degree associated with support
151  // point $i$) and the current
152  // cell. This is done using a
153  // vector of fe.dofs_per_cell
154  // elements, which will then be
155  // distributed to the matrix in the
156  // global row $i$. The following
157  // object will hold this
158  // information:
159  Vector<double> local_neumann_matrix_row_i(comp_dom.fe.dofs_per_cell);
160  Vector<double> local_dirichlet_matrix_row_i(comp_dom.fe.dofs_per_cell);
161 
162  // Now that we have checked that
163  // the number of vertices is equal
164  // to the number of degrees of
165  // freedom, we construct a vector
166  // of support points which will be
167  // used in the local integrations:
168  std::vector<Point<dim> > support_points(comp_dom.dh.n_dofs());
169  DoFTools::map_dofs_to_support_points<dim-1, dim>( *comp_dom.mapping, comp_dom.dh, support_points);
170 
171 
172  // After doing so, we can start the
173  // integration loop over all cells,
174  // where we first initialize the
175  // FEValues object and get the
176  // values of $\mathbf{\tilde v}$ at
177  // the quadrature points (this
178  // vector field should be constant,
179  // but it doesn't hurt to be more
180  // general):
181 
182 
183  cell_it
184  cell = comp_dom.dh.begin_active(),
185  endc = comp_dom.dh.end();
186 
187  Point<dim> D;
188  double s;
189 
190  for (cell = comp_dom.dh.begin_active(); cell != endc; ++cell)
191  {
192  fe_v.reinit(cell);
193  cell->get_dof_indices(local_dof_indices);
194 
195  const std::vector<Point<dim> > &q_points = fe_v.get_quadrature_points();
196  const std::vector<Point<dim> > &normals = fe_v.get_normal_vectors();
197 
198  // We then form the integral over
199  // the current cell for all
200  // degrees of freedom (note that
201  // this includes degrees of
202  // freedom not located on the
203  // current cell, a deviation from
204  // the usual finite element
205  // integrals). The integral that
206  // we need to perform is singular
207  // if one of the local degrees of
208  // freedom is the same as the
209  // support point $i$. A the
210  // beginning of the loop we
211  // therefore check wether this is
212  // the case, and we store which
213  // one is the singular index:
214  for (unsigned int i=0; i<comp_dom.dh.n_dofs() ; ++i)
215  {
216 
217  local_neumann_matrix_row_i = 0;
218  local_dirichlet_matrix_row_i = 0;
219 
220  bool is_singular = false;
221  unsigned int singular_index = numbers::invalid_unsigned_int;
222 
223  for (unsigned int j=0; j<comp_dom.fe.dofs_per_cell; ++j)
224  //if(local_dof_indices[j] == i)
225  if (comp_dom.double_nodes_set[i].count(local_dof_indices[j]) > 0)
226  {
227  singular_index = j;
228  is_singular = true;
229  break;
230  }
231 
232  // We then perform the
233  // integral. If the index $i$
234  // is not one of the local
235  // degrees of freedom, we
236  // simply have to add the
237  // single layer terms to the
238  // right hand side, and the
239  // double layer terms to the
240  // matrix:
241  if (is_singular == false)
242  {
243  for (unsigned int q=0; q<n_q_points; ++q)
244  {
245  const Point<dim> R = q_points[q] + -1.0*support_points[i];
246  LaplaceKernel::kernels(R, D, s);
247 
248  for (unsigned int j=0; j<comp_dom.fe.dofs_per_cell; ++j)
249  {
250  local_neumann_matrix_row_i(j) += ( ( D *
251  normals[q] ) *
252  fe_v.shape_value(j,q) *
253  fe_v.JxW(q) );
254  local_dirichlet_matrix_row_i(j) += ( s *
255  fe_v.shape_value(j,q) *
256  fe_v.JxW(q) );
257 
258  }
259  }
260  }
261  else
262  {
263  // Now we treat the more
264  // delicate case. If we
265  // are here, this means
266  // that the cell that
267  // runs on the $j$ index
268  // contains
269  // support_point[i]. In
270  // this case both the
271  // single and the double
272  // layer potential are
273  // singular, and they
274  // require special
275  // treatment.
276  //
277  // Whenever the
278  // integration is
279  // performed with the
280  // singularity inside the
281  // given cell, then a
282  // special quadrature
283  // formula is used that
284  // allows one to
285  // integrate arbitrary
286  // functions against a
287  // singular weight on the
288  // reference cell.
289  // Notice that singular
290  // integration requires a
291  // careful selection of
292  // the quadrature
293  // rules. In particular
294  // the deal.II library
295  // provides quadrature
296  // rules which are
297  // taylored for
298  // logarithmic
299  // singularities
300  // (QGaussLog,
301  // QGaussLogR), as well
302  // as for 1/R
303  // singularities
304  // (QGaussOneOverR).
305  //
306  // Singular integration
307  // is typically obtained
308  // by constructing
309  // weighted quadrature
310  // formulas with singular
311  // weights, so that it is
312  // possible to write
313  //
314  // \f[
315  // \int_K f(x) s(x) dx = \sum_{i=1}^N w_i f(q_i)
316  // \f]
317  //
318  // where $s(x)$ is a given
319  // singularity, and the weights
320  // and quadrature points
321  // $w_i,q_i$ are carefully
322  // selected to make the formula
323  // above an equality for a
324  // certain class of functions
325  // $f(x)$.
326  //
327  // In all the finite
328  // element examples we
329  // have seen so far, the
330  // weight of the
331  // quadrature itself
332  // (namely, the function
333  // $s(x)$), was always
334  // constantly equal to 1.
335  // For singular
336  // integration, we have
337  // two choices: we can
338  // use the definition
339  // above, factoring out
340  // the singularity from
341  // the integrand (i.e.,
342  // integrating $f(x)$
343  // with the special
344  // quadrature rule), or
345  // we can ask the
346  // quadrature rule to
347  // "normalize" the
348  // weights $w_i$ with
349  // $s(q_i)$:
350  //
351  // \f[
352  // \int_K f(x) s(x) dx =
353  // \int_K g(x) dx = \sum_{i=1}^N \frac{w_i}{s(q_i)} g(q_i)
354  // \f]
355  //
356  // We use this second
357  // option, through the @p
358  // factor_out_singularity
359  // parameter of both
360  // QGaussLogR and
361  // QGaussOneOverR.
362  //
363  // These integrals are
364  // somewhat delicate,
365  // especially in two
366  // dimensions, due to the
367  // transformation from
368  // the real to the
369  // reference cell, where
370  // the variable of
371  // integration is scaled
372  // with the determinant
373  // of the transformation.
374  //
375  // In two dimensions this
376  // process does not
377  // result only in a
378  // factor appearing as a
379  // constant factor on the
380  // entire integral, but
381  // also on an additional
382  // integral alltogether
383  // that needs to be
384  // evaluated:
385  //
386  // \f[
387  // \int_0^1 f(x)\ln(x/\alpha) dx =
388  // \int_0^1 f(x)\ln(x) dx - \int_0^1 f(x) \ln(\alpha) dx.
389  // \f]
390  //
391  // This process is taken care of by
392  // the constructor of the QGaussLogR
393  // class, which adds additional
394  // quadrature points and weights to
395  // take into consideration also the
396  // second part of the integral.
397  //
398  // A similar reasoning
399  // should be done in the
400  // three dimensional
401  // case, since the
402  // singular quadrature is
403  // taylored on the
404  // inverse of the radius
405  // $r$ in the reference
406  // cell, while our
407  // singular function
408  // lives in real space,
409  // however in the three
410  // dimensional case
411  // everything is simpler
412  // because the
413  // singularity scales
414  // linearly with the
415  // determinant of the
416  // transformation. This
417  // allows us to build the
418  // singular two
419  // dimensional quadrature
420  // rules once and for all
421  // outside the loop over
422  // all cells, using only
423  // a pointer where needed.
424  //
425  // Notice that in one
426  // dimensional
427  // integration this is
428  // not possible, since we
429  // need to know the
430  // scaling parameter for
431  // the quadrature, which
432  // is not known a
433  // priori. Here, the
434  // quadrature rule itself
435  // depends also on the
436  // size of the current
437  // cell. For this reason,
438  // it is necessary to
439  // create a new
440  // quadrature for each
441  // singular
442  // integration. Since we
443  // create it using the
444  // new operator of C++,
445  // we also need to
446  // destroy it using the
447  // dual of new:
448  // delete. This is done
449  // at the end, and only
450  // if dim == 2.
451  //
452  // Putting all this into a
453  // dimension independent
454  // framework requires a little
455  // trick. The problem is that,
456  // depending on dimension, we'd
457  // like to either assign a
458  // QGaussLogR<1> or a
459  // QGaussOneOverR<2> to a
460  // Quadrature<dim-1>. C++
461  // doesn't allow this right
462  // away, and neither is a
463  // static_cast
464  // possible. However, we can
465  // attempt a dynamic_cast: the
466  // implementation will then
467  // look up at run time whether
468  // the conversion is possible
469  // (which we <em>know</em> it
470  // is) and if that isn't the
471  // case simply return a null
472  // pointer. To be sure we can
473  // then add a safety check at
474  // the end:
475  Assert(singular_index != numbers::invalid_unsigned_int,
476  ExcInternalError());
477 
478  const Quadrature<dim-1> *
479  singular_quadrature
480  = (dim == 2
481  ?
482  dynamic_cast<Quadrature<dim-1>*>(
483  new QGaussLogR<1>(comp_dom.singular_quadrature_order,
484  Point<1>((double)singular_index),
485  1./cell->measure(), true))
486  :
487  (dim == 3
488  ?
489  dynamic_cast<Quadrature<dim-1>*>(
490  &sing_quadratures_3d[singular_index])
491  :
492  0));
493  Assert(singular_quadrature, ExcInternalError());
494 
495  FEValues<dim-1,dim> fe_v_singular (*comp_dom.mapping, comp_dom.fe, *singular_quadrature,
496  update_jacobians |
497  update_values |
498  update_cell_normal_vectors |
499  update_quadrature_points );
500 
501  fe_v_singular.reinit(cell);
502 
503  const std::vector<Point<dim> > &singular_normals = fe_v_singular.get_normal_vectors();
504  const std::vector<Point<dim> > &singular_q_points = fe_v_singular.get_quadrature_points();
505 
506  for (unsigned int q=0; q<singular_quadrature->size(); ++q)
507  {
508  const Point<dim> R = singular_q_points[q] + -1.0*support_points[i];
509  LaplaceKernel::kernels(R, D, s);
510 
511  for (unsigned int j=0; j<comp_dom.fe.dofs_per_cell; ++j)
512  {
513  local_neumann_matrix_row_i(j) += (( D *
514  singular_normals[q]) *
515  fe_v_singular.shape_value(j,q) *
516  fe_v_singular.JxW(q) );
517 
518  local_dirichlet_matrix_row_i(j) += ( s *
519  fe_v_singular.shape_value(j,q) *
520  fe_v_singular.JxW(q) );
521  }
522  }
523  if (dim==2)
524  delete singular_quadrature;
525  }
526 
527  // Finally, we need to add
528  // the contributions of the
529  // current cell to the
530  // global matrix.
531  for (unsigned int j=0; j<comp_dom.fe.dofs_per_cell; ++j)
532  {
533  neumann_matrix(i,local_dof_indices[j])
534  += local_neumann_matrix_row_i(j);
535  dirichlet_matrix(i,local_dof_indices[j])
536  += local_dirichlet_matrix_row_i(j);
537  }
538  }
539  }
540 
541  // The second part of the integral
542  // operator is the term
543  // $\alpha(\mathbf{x}_i)
544  // \phi_j(\mathbf{x}_i)$. Since we
545  // use a collocation scheme,
546  // $\phi_j(\mathbf{x}_i)=\delta_{ij}$
547  // and the corresponding matrix is
548  // a diagonal one with entries
549  // equal to $\alpha(\mathbf{x}_i)$.
550 
551  // One quick way to compute this
552  // diagonal matrix of the solid
553  // angles, is to use the Neumann
554  // matrix itself. It is enough to
555  // multiply the matrix with a
556  // vector of elements all equal to
557  // -1, to get the diagonal matrix
558  // of the alpha angles, or solid
559  // angles (see the formula in the
560  // introduction for this). The
561  // result is then added back onto
562  // the system matrix object to
563  // yield the final form of the
564  // matrix:
565 
566  /*
567  std::cout<<"Neumann"<<std::endl;
568  for (unsigned int i = 0; i < comp_dom.dh.n_dofs(); i++)
569  {
570  for (unsigned int j = 0; j < comp_dom.dh.n_dofs(); j++)
571  {
572  std::cout<<neumann_matrix(i,j)<<" ";
573  }
574  std::cout<<std::endl;
575  }
576 
577 
578 
579  std::cout<<"Dirichlet"<<std::endl;
580  for (unsigned int i = 0; i < comp_dom.dh.n_dofs(); i++)
581  {
582  for (unsigned int j = 0; j < comp_dom.dh.n_dofs(); j++)
583  {
584  std::cout<<dirichlet_matrix(i,j)<<" ";
585  }
586  std::cout<<std::endl;
587  }
588  //*/
589  std::cout<<"done assembling system matrices"<<std::endl;
590 }
591 
592 
593 
594 template <int dim>
596 {
597  static Vector<double> ones, zeros, dummy;
598  if (ones.size() != comp_dom.dh.n_dofs())
599  {
600  ones.reinit(comp_dom.dh.n_dofs());
601  ones.add(-1.);
602  zeros.reinit(comp_dom.dh.n_dofs());
603  dummy.reinit(comp_dom.dh.n_dofs());
604  }
605 
606 
607  if (solution_method == "Direct")
608  {
609  neumann_matrix.vmult(alpha, ones);
610  }
611  else
612  {
613  fma.generate_multipole_expansions(ones,zeros);
614  fma.multipole_matr_vect_products(ones,zeros,alpha,dummy);
615  }
616 
617  //alpha.print(std::cout);
618 }
619 
620 template <int dim>
622 {
623  static Vector<double> phi(src.size());
624  static Vector<double> dphi_dn(src.size());
625 
626  Vector <double> matrVectProdN;
627  Vector <double> matrVectProdD;
628 
629  matrVectProdN.reinit(src.size());
630  matrVectProdD.reinit(src.size());
631 
632  dst = 0;
633 
634  if (phi.size() != src.size())
635  {
636  phi.reinit(src.size());
637  dphi_dn.reinit(src.size());
638  }
639  phi = src;
640  dphi_dn = src;
641  //phi.scale(comp_dom.surface_nodes);
642  //dphi_dn.scale(comp_dom.other_nodes);
643  phi.scale(comp_dom.other_nodes);
644  dphi_dn.scale(comp_dom.surface_nodes);
645 
646  if (solution_method == "Direct")
647  {
648  dirichlet_matrix.vmult(dst, dphi_dn);
649  dst *= -1;
650  neumann_matrix.vmult_add(dst, phi);
651  phi.scale(alpha);
652  dst += phi;
653 
654  }
655  else
656  {
657  fma.generate_multipole_expansions(phi,dphi_dn);
658  fma.multipole_matr_vect_products(phi,dphi_dn,matrVectProdN,matrVectProdD);
659  phi.scale(alpha);
660  dst += matrVectProdD;
661  dst *= -1;
662  dst += matrVectProdN;
663  dst += phi;
664  }
665  // in fully neumann bc case, we have to rescale the vector to have a zero mean
666  // one
667  if (comp_dom.surface_nodes.linfty_norm() < 1e-10)
668  dst.add(-dst.l2_norm());
669 
670 }
671 
672 
673 template <int dim>
675 {
676  static Vector<double> phi(src.size());
677  static Vector<double> dphi_dn(src.size());
678  static Vector<double> rhs(src.size());
679 
680  static Vector <double> matrVectProdN;
681  static Vector <double> matrVectProdD;
682 
683  if (phi.size() != src.size())
684  {
685  phi.reinit(src.size());
686  dphi_dn.reinit(src.size());
687  matrVectProdN.reinit(src.size());
688  matrVectProdD.reinit(src.size());
689  }
690 
691  phi = src;
692  dphi_dn = src;
693 
694  phi.scale(comp_dom.surface_nodes);
695  dphi_dn.scale(comp_dom.other_nodes);
696 
697  //for (unsigned int ppp = 0; ppp < src.size(); ++ppp)
698  //std::cout<<ppp<<" phi(ppp)="<<phi(ppp)<<" ||| dphi_dn(ppp)="<<dphi_dn(ppp)<<std::endl;
699 
700  if (solution_method == "Direct")
701  {
702  neumann_matrix.vmult(dst, phi);
703  phi.scale(alpha);
704  dst += phi;
705  dst *= -1;
706  dirichlet_matrix.vmult_add(dst, dphi_dn);
707  }
708  else
709  {
710  fma.generate_multipole_expansions(phi,dphi_dn);
711  fma.multipole_matr_vect_products(phi,dphi_dn,matrVectProdN,matrVectProdD);
712  phi.scale(alpha);
713  dst += matrVectProdN;
714  dst += phi;
715  dst *= -1;
716  dst += matrVectProdD;
717  }
718 
719  /*
720  // here we correct the right hand side so as to account for the presence of double and
721  // triple dofs
722 
723  // we start looping on the dofs
724  for (unsigned int i=0; i <src.size(); i++)
725  {
726  // in the next line we compute the "first" among the set of double nodes: this node
727  // is the first dirichlet node in the set, and if no dirichlet node is there, we get the
728  // first neumann node
729 
730  std::set<unsigned int> doubles = comp_dom.double_nodes_set[i];
731  unsigned int firstOfDoubles = *doubles.begin();
732  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
733  {
734  if (comp_dom.surface_nodes(*it) == 1)
735  {
736  firstOfDoubles = *it;
737  break;
738  }
739  }
740 
741  // for each set of double nodes, we will perform the correction only once, and precisely
742  // when the current node is the first of the set
743  if (i == firstOfDoubles)
744  {
745  // the vector entry corresponding to the first node of the set does not need modification,
746  // thus we erase ti form the set
747  doubles.erase(i);
748 
749  // if the current (first) node is a dirichlet node, for all its neumann doubles we will
750  // impose that the potential is equal to that of the first node: this means that in the
751  // rhs we will put the potential value of the first node (which is prescribed by bc)
752  if (comp_dom.surface_nodes(i) == 1)
753  {
754  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
755  {
756  if (comp_dom.surface_nodes(*it) == 1)
757  {
758 
759  }
760  else
761  {
762  dst(*it) = phi(i)/alpha(i);
763  }
764  }
765  }
766 
767  // if the current (first) node is a neumann node, for all its doubles we will impose that
768  // the potential is equal to that of the first node: this means that in the rhs we will
769  // put 0
770  if (comp_dom.surface_nodes(i) == 0)
771  {
772  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
773  {
774  dst(*it) = 0;
775  }
776  }
777 
778 
779  }
780  }
781 
782 
783  //*/
784  /* for (unsigned int i=0; i <src.size(); i++)
785  {
786  if (comp_dom.double_nodes_set[i].size() > 1 && comp_dom.surface_nodes(i) == 0)
787  {
788  std::set<unsigned int> doubles = comp_dom.double_nodes_set[i];
789  doubles.erase(i);
790  unsigned int doubleSurIndex = 0;
791  double a = 0;
792  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
793  {
794  if (comp_dom.surface_nodes(*it) == 1)
795  {
796  doubleSurIndex = *it;
797  a += comp_dom.surface_nodes(*it);
798  }
799  }
800  if (a > 0)
801  dst(i) = phi(doubleSurIndex)/alpha(doubleSurIndex);
802  else
803  {
804  std::set<unsigned int>::iterator it = doubles.begin();
805  if ( i > *it)
806  dst(i) = 0;
807  }
808  }
809  }//*/
810 
811 }
812 
813 
814 
815 
816 
817 // @sect4{BEMProblem::solve_system}
818 
819 // The next function simply solves
820 // the linear system.
821 template <int dim>
823  const Vector<double> &tmp_rhs)
824 {
825 
826  SolverGMRES<Vector<double> > solver (solver_control,
827  SolverGMRES<Vector<double> >::AdditionalData(100));
828 
829  system_rhs = 0;
830  sol = 0;
831  alpha = 0;
832 
833  compute_alpha();
834 
835  //std::cout<<"alpha "<<std::endl;
836  //for (unsigned int i = 0; i < alpha.size(); i++)
837  //std::cout<<i<<" "<<alpha(i)<<std::endl;
838 
839  compute_rhs(system_rhs, tmp_rhs);
840 
841  compute_constraints(constraints, tmp_rhs);
843  cc(*this, constraints);
844 
845  cc.distribute_rhs(system_rhs);
846 
847  if (solution_method == "Direct")
848  {
849  //SparseDirectUMFPACK &inv = fma.FMA_preconditioner(alpha);
850  //solver.solve (*this, sol, system_rhs, inv);
851  assemble_preconditioner();
852  //solver.solve (cc, sol, system_rhs, PreconditionIdentity());
853  solver.solve (cc, sol, system_rhs, preconditioner);
854  }
855  else
856  {
857  SparseDirectUMFPACK &inv = fma.FMA_preconditioner(alpha);
858  solver.solve (*this, sol, system_rhs, inv);
859  // solver.solve (*this, sol, system_rhs, PreconditionIdentity());
860  }
861 
862  //std::cout<<"sol = [";
863  //for (unsigned int i = 0; i < comp_dom.dh.n_dofs(); i++)
864  // std::cout<<sol(i)<<"; ";
865  //std::cout<<"];"<<std::endl;
866 
867 
868 
869  for (unsigned int i=0; i <comp_dom.surface_nodes.size(); i++)
870  {
871  if (comp_dom.surface_nodes(i) == 0)
872  {
873  phi(i) = sol(i);
874  }
875  else
876  {
877  dphi_dn(i) = sol(i);
878  }
879  }
880 
881  //for (unsigned int i=0;i<comp_dom.dh.n_dofs();++i)
882  // cout<<i<<" "<<tmp_rhs(i)<<" "<<dphi_dn(i)<<" "<<phi(i)<<" "<<comp_dom.surface_nodes(i)<<endl;
883 
884  //std::cout<<"sol "<<std::endl;
885  //for (unsigned int i = 0; i < sol.size(); i++)
886  // {
887  //std::cout<<i<<" "<<sol(i)<<" ";
888  //std::set<unsigned int> doubles = comp_dom.double_nodes_set[i];
889  // for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
890  // std::cout<<*it<<"("<<comp_dom.surface_nodes(*it)<<") ";
891  //std::cout<<"phi "<<phi(i)<<" dphi_dn "<<dphi_dn(i);
892  //std::cout<<std::endl;
893  // }
894 
895 }
896 
897 
898 
899 // @sect4{BEMProblem::solve_system}
900 
901 // The next function simply solves
902 // the linear system.
903 template <int dim>
905  const Vector<double> &dphi_dn)
906 {
907 
908  alpha = 0;
909  res = 0;
910  sol = 0;
911  serv_phi = phi;
912  serv_dphi_dn = dphi_dn;
913  serv_tmp_rhs = 0;
914 
915  compute_alpha();
916 
917  serv_dphi_dn.scale(comp_dom.other_nodes);
918  serv_phi.scale(comp_dom.surface_nodes);
919  serv_tmp_rhs.add(serv_dphi_dn);
920  serv_tmp_rhs.add(serv_phi);
921 
922  //for (unsigned int i=0;i<comp_dom.dh.n_dofs();++i)
923  // cout<<i<<" "<<serv_tmp_rhs(i)<<" "<<serv_dphi_dn(i)<<" "<<serv_phi(i)<<" "<<comp_dom.surface_nodes(i)<<endl;
924 
925  Vector<double> rrhs(comp_dom.dh.n_dofs());
926  compute_rhs(rrhs, serv_tmp_rhs);
927  //for (unsigned int i=0;i<comp_dom.dh.n_dofs();++i)
928  // cout<<i<<" "<<rrhs(i)<<endl;
929  compute_constraints(constraints, serv_tmp_rhs);
931  cc(*this, constraints);
932  cc.distribute_rhs(rrhs);
933 
934 
935 //for (unsigned int i=0;i<comp_dom.dh.n_dofs();++i)
936 // cout<<i<<"---> "<<rrhs(i)<<" vs "<<system_rhs(i)<<" diff: "<<rrhs(i)-system_rhs(i)<<endl;
937 
938  rrhs *= -1;
939 
940  sol = 0;
941 
942  serv_phi = phi;
943  serv_dphi_dn = dphi_dn;
944  serv_dphi_dn.scale(comp_dom.surface_nodes);
945  serv_phi.scale(comp_dom.other_nodes);
946  sol.add(serv_dphi_dn);
947  sol.add(serv_phi);
948 
949  if (solution_method == "Direct")
950  {
951  cc.vmult(res,sol);
952  res += rrhs;
953  }
954  else
955  {
956  //SparseDirectUMFPACK &inv = fma.FMA_preconditioner(alpha);
957  //solver.solve (*this, sol, system_rhs, inv);
958  // solver.solve (*this, sol, system_rhs, PreconditionIdentity());
959  }
960  //cout<<"Residual test: "<<res.l2_norm()<<" £££££ "<<rrhs.l2_norm()<<endl;
961 }
962 
963 
964 
965 // @sect4{BEMProblem::compute_errors}
966 
967 // This method performs a Bem resolution,
968 // either in a direct or multipole method
969 template <int dim>
971  const Vector<double> &tmp_rhs)
972 {
973 
974  if (solution_method == "Direct")
975  {
976  assemble_system();
977  }
978  else
979  {
980  comp_dom.generate_octree_blocking();
981  fma.direct_integrals();
982  fma.multipole_integrals();
983  }
984 
985  solve_system(phi,dphi_dn,tmp_rhs);
986 
987 }
988 
989 
990 template <int dim>
992 
993 {
994 
995  // we will need the normals and the surface gradients at the nodes
996  comp_dom.compute_normals();
997  compute_surface_gradients(tmp_rhs);
998 
999  // we start clearing the constraint matrix
1000  c.clear();
1001 
1002  // here we prepare the constraint matrix so as to account for the presence hanging
1003  // nodes
1004 
1006 
1007 
1008  // here we prepare the constraint matrix so as to account for the presence of double and
1009  // triple dofs
1010 
1011  // we start looping on the dofs
1012  for (unsigned int i=0; i <tmp_rhs.size(); i++)
1013  {
1014  // in the next line we compute the "first" among the set of double nodes: this node
1015  // is the first dirichlet node in the set, and if no dirichlet node is there, we get the
1016  // first neumann node
1017 
1018  std::set<unsigned int> doubles = comp_dom.double_nodes_set[i];
1019  unsigned int firstOfDoubles = *doubles.begin();
1020  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
1021  {
1022  if (comp_dom.surface_nodes(*it) == 1)
1023  {
1024  firstOfDoubles = *it;
1025  break;
1026  }
1027  }
1028 
1029  // for each set of double nodes, we will perform the correction only once, and precisely
1030  // when the current node is the first of the set
1031  if (i == firstOfDoubles)
1032  {
1033  // the vector entry corresponding to the first node of the set does not need modification,
1034  // thus we erase ti form the set
1035  doubles.erase(i);
1036 
1037  // if the current (first) node is a dirichlet node, for all its neumann doubles we will
1038  // impose that the potential is equal to that of the first node: this means that in the
1039  // matrix vector product we will put the potential value of the double node
1040  if (comp_dom.surface_nodes(i) == 1)
1041  {
1042  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
1043  {
1044  if (comp_dom.surface_nodes(*it) == 1)
1045  {
1046  // this is the dirichlet-dirichlet case on flat edges: here we impose that
1047  // dphi_dn on the two (or more) sides is equal.
1048  if ( comp_dom.nodes_normals[*it].distance(comp_dom.nodes_normals[i]) < 1e-4 )
1049  {
1050  c.add_line(*it);
1051  c.add_entry(*it,i,1);
1052  }
1053  // this is the dirichlet-dirichlet case on sharp edges: both normal gradients
1054  // can be computed from surface gradients of phi and assingned as BC
1055  else
1056  {
1057  c.add_line(*it);
1058  double this_normal_gradient = (1.0/(1.0-pow(comp_dom.nodes_normals[i]*comp_dom.nodes_normals[*it],2))) *
1059  (node_surface_gradients[*it]*comp_dom.nodes_normals[i]+
1060  (node_surface_gradients[i]*comp_dom.nodes_normals[*it])*(comp_dom.nodes_normals[i]*comp_dom.nodes_normals[*it]));
1061  double other_normal_gradient = (1.0/(1.0-pow(comp_dom.nodes_normals[*it]*comp_dom.nodes_normals[i],2))) *
1062  (node_surface_gradients[i]*comp_dom.nodes_normals[*it]+
1063  (node_surface_gradients[*it]*comp_dom.nodes_normals[i])*(comp_dom.nodes_normals[*it]*comp_dom.nodes_normals[i]));
1064  //std::cout<<"i="<<i<<" j="<<*it<<std::endl;
1065  //std::cout<<"ni=("<<comp_dom.nodes_normals[i]<<") nj=("<<comp_dom.nodes_normals[*it]<<")"<<std::endl;
1066  //std::cout<<"grad_s_phi_i=("<<node_surface_gradients[i]<<") grad_s_phi_j=("<<node_surface_gradients[*it]<<")"<<std::endl;
1067  //std::cout<<"dphi_dn_i="<<this_normal_gradient<<" dphi_dn_j="<<other_normal_gradient<<std::endl;
1068  //Point<3> this_full_gradient = comp_dom.nodes_normals[i]*this_normal_gradient + node_surface_gradients[i];
1069  //Point<3> other_full_gradient = comp_dom.nodes_normals[*it]*other_normal_gradient + node_surface_gradients[*it];
1070  //std::cout<<"grad_phi_i=("<<this_full_gradient<<") grad_phi_j=("<<other_full_gradient<<")"<<std::endl;
1071  c.add_line(i);
1072  c.set_inhomogeneity(i,this_normal_gradient);
1073  c.add_line(*it);
1074  c.set_inhomogeneity(*it,other_normal_gradient);
1075  }
1076  }
1077  else
1078  {
1079  c.add_line(*it);
1080  c.set_inhomogeneity(*it,tmp_rhs(i));
1081  //dst(*it) = phi(*it)/alpha(*it);
1082  }
1083  }
1084  }
1085 
1086  // if the current (first) node is a neumann node, for all its doubles we will impose that
1087  // the potential is equal to that of the first node: this means that in the matrix vector
1088  // product we will put the difference between the potential at the fist node in the doubles
1089  // set, and the current double node
1090  if (comp_dom.surface_nodes(i) == 0)
1091  {
1092  for (std::set<unsigned int>::iterator it = doubles.begin() ; it != doubles.end(); it++ )
1093  {
1094  c.add_line(*it);
1095  c.add_entry(*it,i,1);
1096  //dst(*it) = phi(*it)/alpha(*it)-phi(i)/alpha(i);
1097  }
1098  }
1099 
1100 
1101  }
1102  }
1103 
1104  c.close();
1105 }
1106 
1107 template<int dim>
1109 {
1110 
1111 
1112  static SparseMatrix<double> band_system;
1113 
1114  if (is_preconditioner_initialized == false)
1115  {
1116  for (int i=0; (unsigned int) i<comp_dom.dh.n_dofs(); ++i)
1117  for (int j=std::max(i-preconditioner_band/2,0); j<std::min(i+preconditioner_band/2,(int)comp_dom.dh.n_dofs()); ++j)
1118  preconditioner_sparsity_pattern.add((unsigned int) j,(unsigned int) i);
1119  preconditioner_sparsity_pattern.compress();
1120  band_system.reinit(preconditioner_sparsity_pattern);
1121  is_preconditioner_initialized = true;
1122  }
1123  else
1124  band_system = 0;
1125 
1126  for (int i=0; (unsigned int) i<comp_dom.dh.n_dofs(); ++i)
1127  {
1128  if (constraints.is_constrained(i))
1129  band_system.set((unsigned int)i,(unsigned int) i, 1);
1130  for (int j=std::max(i-preconditioner_band/2,0); j<std::min(i+preconditioner_band/2,(int)comp_dom.dh.n_dofs()); ++j)
1131  {
1132  if (constraints.is_constrained(j) == false)
1133  {
1134  if (comp_dom.surface_nodes(i) == 0)
1135  {
1136  // Nodo di Dirichlet
1137  band_system.set(j,i,neumann_matrix(j,i));
1138  if (i == j)
1139  band_system.add((unsigned int) j,(unsigned int) i, alpha(i));
1140  }
1141  else
1142  band_system.set(j,i,-dirichlet_matrix(j,i));
1143  }
1144  }
1145  }
1146 
1147  preconditioner.initialize(band_system);
1148 
1149 }
1150 
1151 
1152 
1153 template <int dim>
1155 {
1156  Vector<double> phi(tmp_rhs.size());
1157  phi = tmp_rhs;
1158  phi.scale(comp_dom.surface_nodes);
1159 
1160 
1161  typedef typename DoFHandler<dim-1,dim>::active_cell_iterator cell_it;
1162 
1163  SparsityPattern gradients_sparsity_pattern;
1164  gradients_sparsity_pattern.reinit(comp_dom.vector_dh.n_dofs(),
1165  comp_dom.vector_dh.n_dofs(),
1166  comp_dom.vector_dh.max_couplings_between_dofs());
1167  ConstraintMatrix vector_constraints;
1168  vector_constraints.clear();
1169  DoFTools::make_hanging_node_constraints (comp_dom.vector_dh,vector_constraints);
1170  vector_constraints.close();
1171  DoFTools::make_sparsity_pattern (comp_dom.vector_dh, gradients_sparsity_pattern, vector_constraints);
1172  gradients_sparsity_pattern.compress();
1173 
1174 
1175  SparseMatrix<double> vector_gradients_matrix;
1176  Vector<double> vector_gradients_rhs;
1177 
1178  vector_gradients_matrix.reinit (gradients_sparsity_pattern);
1179  vector_gradients_rhs.reinit(comp_dom.vector_dh.n_dofs());
1180  Vector<double> vector_gradients_solution(comp_dom.vector_dh.n_dofs());
1181 
1182 
1183  FEValues<dim-1,dim> vector_fe_v(*comp_dom.mapping, comp_dom.vector_fe, *comp_dom.quadrature,
1184  update_values | update_gradients |
1185  update_cell_normal_vectors |
1186  update_quadrature_points |
1187  update_JxW_values);
1188 
1189  FEValues<dim-1,dim> fe_v(*comp_dom.mapping, comp_dom.fe, *comp_dom.quadrature,
1190  update_values | update_gradients |
1191  update_cell_normal_vectors |
1192  update_quadrature_points |
1193  update_JxW_values);
1194 
1195  const unsigned int vector_n_q_points = vector_fe_v.n_quadrature_points;
1196  const unsigned int vector_dofs_per_cell = comp_dom.vector_fe.dofs_per_cell;
1197  std::vector<unsigned int> vector_local_dof_indices (vector_dofs_per_cell);
1198 
1199  std::vector< Tensor<1,dim> > phi_surf_grads(vector_n_q_points);
1200  std::vector<double> phi_norm_grads(vector_n_q_points);
1201  std::vector<Vector<double> > q_vector_normals_solution(vector_n_q_points,
1202  Vector<double>(dim));
1203 
1204  FullMatrix<double> local_gradients_matrix (vector_dofs_per_cell, vector_dofs_per_cell);
1205  Vector<double> local_gradients_rhs (vector_dofs_per_cell);
1206 
1207 
1208  cell_it
1209  vector_cell = comp_dom.vector_dh.begin_active(),
1210  vector_endc = comp_dom.vector_dh.end();
1211 
1212  cell_it
1213  cell = comp_dom.dh.begin_active(),
1214  endc = comp_dom.dh.end();
1215 
1216  std::vector<Point<dim> > support_points(comp_dom.dh.n_dofs());
1217  DoFTools::map_dofs_to_support_points<dim-1, dim>( *comp_dom.mapping, comp_dom.dh, support_points);
1218  std::vector<unsigned int> face_dofs(comp_dom.fe.dofs_per_face);
1219 
1220  Quadrature <dim-1> dummy_quadrature(comp_dom.fe.get_unit_support_points());
1221  FEValues<dim-1,dim> dummy_fe_v(*comp_dom.mapping, comp_dom.fe, dummy_quadrature,
1222  update_values | update_gradients |
1223  update_cell_normal_vectors |
1224  update_quadrature_points);
1225 
1226  const unsigned int dofs_per_cell = comp_dom.fe.dofs_per_cell;
1227  std::vector<unsigned int> local_dof_indices (dofs_per_cell);
1228  const unsigned int n_q_points = dummy_fe_v.n_quadrature_points;
1229  std::vector< Tensor<1,dim> > dummy_phi_surf_grads(n_q_points);
1230 
1231 
1232  for (; cell!=endc,vector_cell!=vector_endc; ++cell,++vector_cell)
1233  {
1234  Assert(cell->index() == vector_cell->index(), ExcInternalError());
1235 
1236  fe_v.reinit (cell);
1237  vector_fe_v.reinit (vector_cell);
1238  local_gradients_matrix = 0;
1239  local_gradients_rhs = 0;
1240  const std::vector<Point<dim> > &vector_node_normals = vector_fe_v.get_normal_vectors();
1241  fe_v.get_function_gradients(phi, phi_surf_grads);
1242  unsigned int comp_i, comp_j;
1243 
1244 
1245 
1246 
1247  for (unsigned int q=0; q<vector_n_q_points; ++q)
1248  {
1249  Point<dim> gradient(phi_surf_grads[q][0],phi_surf_grads[q][1],phi_surf_grads[q][2]);
1250  for (unsigned int i=0; i<vector_dofs_per_cell; ++i)
1251  {
1252  comp_i = comp_dom.vector_fe.system_to_component_index(i).first;
1253  for (unsigned int j=0; j<vector_dofs_per_cell; ++j)
1254  {
1255  comp_j = comp_dom.vector_fe.system_to_component_index(j).first;
1256  if (comp_i == comp_j)
1257  {
1258  local_gradients_matrix(i,j) += vector_fe_v.shape_value(i,q)*
1259  vector_fe_v.shape_value(j,q)*
1260  vector_fe_v.JxW(q);
1261  }
1262  }
1263  local_gradients_rhs(i) += (vector_fe_v.shape_value(i, q)) *
1264  gradient(comp_i) * vector_fe_v.JxW(q);
1265  }
1266  }
1267  vector_cell->get_dof_indices (vector_local_dof_indices);
1268 
1269  vector_constraints.distribute_local_to_global
1270  (local_gradients_matrix,
1271  local_gradients_rhs,
1272  vector_local_dof_indices,
1273  vector_gradients_matrix,
1274  vector_gradients_rhs);
1275  }
1276 
1277  SparseDirectUMFPACK gradients_inverse;
1278  gradients_inverse.initialize(vector_gradients_matrix);
1279  gradients_inverse.vmult(vector_gradients_solution, vector_gradients_rhs);
1280  vector_constraints.distribute(vector_gradients_solution);
1281 
1282 
1283  node_surface_gradients.resize(comp_dom.dh.n_dofs());
1284 
1285  for (unsigned int i=0; i<comp_dom.vector_dh.n_dofs()/dim; ++i)
1286  {
1287  for (unsigned int d=0; d<dim; d++)
1288  node_surface_gradients[i](d) = vector_gradients_solution(3*i+d);
1289  //cout<<i<<" Gradient: "<<node_gradients[i]<<endl;
1290  }
1291 
1292 
1293 }
1294 
1295 
1296 
1297 template class BEMProblem<3>;
static const unsigned int invalid_unsigned_int
real_type l2_norm() const
void kernels(const Point< dim > &R, Point< dim > &D, double &d)
void map_dofs_to_support_points(const Mapping< dim, spacedim > &mapping, const DoFHandler< dim, spacedim > &dof_handler, std::vector< Point< spacedim > > &support_points)
void set(const size_type i, const size_type j, const number value)
static void declare_parameters(ParameterHandler &param)
void distribute(VectorType &vec) const
void residual(Vector< double > &res, const Vector< double > &phi, const Vector< double > &dphi_dn)
Definition: bem_problem.cc:904
virtual void reinit(const SparsityPattern &sparsity)
std::string get(const std::string &entry_string) const
DoFHandler< dim-1, dim >::active_cell_iterator cell_it
Definition: bem_problem.h:92
void enter_subsection(const std::string &subsection)
void add_line(const size_type line)
void add(const std::vector< size_type > &indices, const std::vector< OtherNumber > &values)
void make_hanging_node_constraints(const DoFHandlerType &dof_handler, ConstraintMatrix &constraints)
void distribute_local_to_global(const InVector &local_vector, const std::vector< size_type > &local_dof_indices, OutVector &global_vector) const
void distribute_rhs(VEC &rhs) const
void scale(const Vector< double > &scaling_factors)
void add_entry(const size_type line, const size_type column, const double value)
#define Assert(cond, exc)
void compute_constraints(ConstraintMatrix &constraints, const Vector< double > &tmp_rhs)
Definition: bem_problem.cc:991
void assemble_system()
Definition: bem_problem.cc:107
void assemble_preconditioner()
void solve(const MatrixType &A, VectorType &x, const VectorType &b, const PreconditionerType &precondition)
std::size_t size() const
void compute_alpha()
Definition: bem_problem.cc:595
void compute_rhs(Vector< double > &dst, const Vector< double > &src) const
Definition: bem_problem.cc:674
void vmult(Vector< double > &dst, const Vector< double > &src) const
Definition: bem_problem.cc:621
void vmult(VEC &dst, const VEC &src) const
void parse_parameters(ParameterHandler &prm)
Definition: bem_problem.cc:92
void add(const size_type i, const size_type j, const number value)
void reinit()
Definition: bem_problem.cc:56
void solve_system(Vector< double > &phi, Vector< double > &dphi_dn, const Vector< double > &tmp_rhs)
Definition: bem_problem.cc:822
void leave_subsection()
void reinit(const size_type m, const size_type n, const unsigned int max_per_row)
BEMProblem(ComputationalDomain< dim > &comp_dom, BEMFMA< dim > &fma)
Definition: bem_problem.cc:49
void make_sparsity_pattern(const DoFHandlerType &dof_handler, SparsityPatternType &sparsity_pattern, const ConstraintMatrix &constraints=ConstraintMatrix(), const bool keep_constrained_dofs=true, const types::subdomain_id subdomain_id=numbers::invalid_subdomain_id)
void initialize(const SparsityPattern &sparsity_pattern)
void declare_parameters(ParameterHandler &prm)
Definition: bem_problem.cc:74
void solve(Vector< double > &phi, Vector< double > &dphi_dn, const Vector< double > &tmp_rhs)
Definition: bem_problem.cc:970
virtual void reinit(const size_type N, const bool omit_zeroing_entries=false)
void declare_entry(const std::string &entry, const std::string &default_value, const Patterns::PatternBase &pattern=Patterns::Anything(), const std::string &documentation=std::string())
void compute_surface_gradients(const Vector< double > &tmp_rhs)
void set_inhomogeneity(const size_type line, const double value)
Definition: bem_fma.h:45
void vmult(Vector< double > &dst, const Vector< double > &src) const
static::ExceptionBase & ExcInternalError()