libMesh
miscellaneous_ex14.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 // <h1>Miscellaneous Example 14 - Hydrogen Atom Using Infinite Elements With Imaginary Frequency</h1>
20 // \author Hubert Weissmann
21 // \date 2017
22 //
23 // This example uses second order infinite elements to solve the Schroedinger equation to obtain
24 // the wave function of the Hydrogen atom.
25 // To speed up the calculation, the SlepcSolver is configured to use the shift-and-invert-transform for solving
26 // the resulting generalised eigensystem.
27 //
28 // The result is printed in exodus-format and along the x-axis in a free format.
29 
30 #include <iostream>
31 #include <fstream>
32 // libMesh include files.
33 #include "libmesh/getpot.h" // for input-argument parsing
34 #include "libmesh/libmesh.h"
35 #include "libmesh/mesh.h"
36 #include "libmesh/mesh_tetgen_interface.h"
37 #include "libmesh/elem.h"
38 #include "libmesh/mesh_generation.h"
39 #include "libmesh/exodusII_io.h"
40 #include "libmesh/eigen_system.h"
41 #include "libmesh/equation_systems.h"
42 #include "libmesh/fe.h"
43 #include "libmesh/quadrature_gauss.h"
44 #include "libmesh/dense_matrix.h"
45 #include "libmesh/sparse_matrix.h"
46 #include "libmesh/numeric_vector.h"
47 #include "libmesh/dof_map.h"
48 #include "libmesh/fe_interface.h"
49 #include "libmesh/explicit_system.h"
50 // for infinite elements:
51 #include "libmesh/inf_fe.h"
52 #include "libmesh/inf_elem_builder.h"
53 #include "libmesh/fe_interface.h"
54 #include "libmesh/fe_compute_data.h"
55 // for finding element for point
56 #include "libmesh/point_locator_tree.h"
57 
58 // for the SlepcSolverConfiguration
59 #include "libmesh/petsc_solver_exception.h"
60 #include "libmesh/solver_configuration.h"
61 #include "libmesh/slepc_eigen_solver.h"
62 #include "libmesh/enum_eigen_solver_type.h"
63 
64 #ifdef LIBMESH_HAVE_SLEPC
65 EXTERN_C_FOR_SLEPC_BEGIN
66 # include <slepceps.h>
67 EXTERN_C_FOR_SLEPC_END
68 #endif // LIBMESH_HAVE_SLEPC
69 
70 // Bring in everything from the libMesh namespace
71 using namespace libMesh;
72 
73 #ifdef LIBMESH_HAVE_SLEPC
74 
81 
83 };
84 
91 {
92 public:
93 
98  _slepc_solver(slepc_eigen_solver),
99  _st(INVALID_ST)
100  {}
101 
106 
107  virtual void configure_solver() override;
108 
113  { _st=st;}
114 
115 private:
116 
122 
123 };
124 #endif // LIBMESH_HAVE_SLEPC
125 
126 //prototypes of functions needed to set-up the system:
127 // The assemble-function is similar to miscellaneous_ex1
128 void assemble_SchroedingerEquation(EquationSystems & , const std::string &);
129 
130 // print the solution along the x-axis.
131 void line_print(EquationSystems& es, std::string output, std::string SysName);
132 
133 
134 int main (int argc, char** argv)
135 {
136  // Initialize libMesh and the dependent libraries.
137  LibMeshInit init (argc, argv);
138 
139  // Check for proper usage first.
140  libmesh_error_msg_if(argc < 2, "\nUsage: " << argv[0] << " <input-filename>");
141 
142  // Tell the user what we are doing.
143  std::cout << "Running " << argv[0];
144 
145  for (int i=1; i<argc; i++)
146  std::cout << " " << argv[i];
147  std::cout << std::endl << std::endl;
148 
149  // Skip SLEPc examples on a non-SLEPc libMesh build
150 #ifndef LIBMESH_HAVE_SLEPC
151  libmesh_example_requires(false, "--enable-slepc");
152 #else
153 #ifndef LIBMESH_ENABLE_INFINITE_ELEMENTS
154  libmesh_example_requires(false, "--enable-ifem");
155 #else
156 
157 #ifndef LIBMESH_USE_COMPLEX_NUMBERS
158  libmesh_example_requires(false, "--enable-complex");
159 #endif
160 
161 #ifdef LIBMESH_DEFAULT_SINGLE_PRECISION
162  // SLEPc currently gives us a nasty crash with Real==float
163  libmesh_example_requires(false, "--disable-singleprecision");
164 #endif
165 
166  // parse the input file to getpot and obtain an object holding all options
167  GetPot cl(argv[1]);
168 
169  // Skip this example if libMesh was compiled with <3 dimensions.
170  // INFINITE ELEMENTS ARE IMPLEMENTED ONLY FOR 3 DIMENSIONS AT THE MOMENT.
171  libmesh_example_requires(3 <= LIBMESH_DIM, "3D support");
172 
173  // read the options from the input-file.
174  const unsigned int nev = cl("nev",1);
175  Real E = cl("Energy", -.5);
176  Real r=cl("radius", 4.);
177  unsigned int maxiter=cl("maxiter", 700);
178 
179  int dim = 3;
180 
181  // creation of an empty mesh-object
182  Mesh mesh(init.comm(), dim);
183 
184  // fill the meshes with a spherical grid of type HEX27 with radius r
186 
187  // The infinite elements are attached to all elements that build the outer surface of the FEM-region.
188  InfElemBuilder builder(mesh);
189  builder.build_inf_elem(true);
190 
191  // Reassign subdomain_id() of all infinite elements.
192  // and their neighbours. This makes finding the surface
193  // between these elemests much easier.
194  for (auto & elem : mesh.element_ptr_range())
195  if (elem->infinite())
196  {
197  elem->subdomain_id() = 1;
198  // the base elements are always the 0-th neighbor.
199  elem->neighbor_ptr(0)->subdomain_id()=2;
200  }
201 
202  // find the neighbours; for correct linking the two areas
204 
205  // Create an equation systems object
206  EquationSystems eq_sys (mesh);
207 
208  // This is the only system added here.
209  EigenSystem & eig_sys = eq_sys.add_system<EigenSystem> ("EigenSE");
210 
211  //set the complete type of the variable
213 
214  // Name the variable of interest 'phi' and approximate it as \p fe_type.
215  eig_sys.add_variable("phi", fe_type);
216 
217  // assign the ground state energy. For infinite elements, the quality of this guess is crucial
218  // otherwise the long-range limit will be wrong. If the 'current frequency' is too much off,
219  // the solution will have no physical meaning at all.
220  eq_sys.parameters.set<Number>("gsE")=E;
221 
222  //save the mesh-size in a parameter to scale the region for printing the solution later accordingly.
223  eq_sys.parameters.set<Real>("radius") = r;
224 
225  //set number of eigen values ( \p nev) and number of
226  // basis vectors \p ncv for the solution.
227  //Note that ncv >= nev must hold and ncv >= 2*nev is recommended.
228  eq_sys.parameters.set<unsigned int>("eigenpairs") = nev;
229 
230  eq_sys.parameters.set<unsigned int>("basis vectors") = nev*3+4;
231 
232  // Set the solver tolerance and the maximum number of iterations.
233  eq_sys.parameters.set<Real> ("linear solver tolerance") = pow(TOLERANCE, 5./3.);
234  eq_sys.parameters.set<unsigned int>("linear solver maximum iterations") = maxiter;
235 
236  // set numerical parameters for SLEPC on how to solve the system.
237 #if SLEPC_VERSION_LESS_THAN(2,3,2)
238  eig_sys.get_eigen_solver().set_eigensolver_type(ARNOLDI);
239 #else
240  eig_sys.get_eigen_solver().set_eigensolver_type(KRYLOVSCHUR);
241 #endif
242 
243  eig_sys.get_eigen_solver().set_position_of_spectrum(E);
244 
245  //fetch the solver-object used internally to be able to manipulate it using the self-written class
246  // to set the transformation
247  SlepcEigenSolver<Number> * solver =
248  cast_ptr<SlepcEigenSolver<Number>* >( &eig_sys.get_eigen_solver() );
249 
250  // setup of our class @SlepcSolverConfiguration
251  SlepcSolverConfiguration ConfigSolver(*solver);
252 
253  // set the spectral transformation: the default (SHIFT) will not converge
254  // so we apply some more elaborate scheme.
255  ConfigSolver.SetST(SINVERT);
256  solver ->set_solver_configuration(ConfigSolver);
257 
258  // attach the name of the function that assembles the matrix equation:
260 
261  // important to set the system to be generalised nonhermitian eigen problem.
262  // By default it is HEP and so _matrix_B is not available.
263  // In the infinite element scheme, the Hamiltonian is not hermitian because left and right
264  // basis are not the same!
265  eig_sys.set_eigenproblem_type(GNHEP);
266 
267  // Initialize the data structures for the equation system.
268  eq_sys.init();
269 
270  // Solve system. This function calls the assemble-functions.
271  eig_sys.solve();
272 
273  // get number of actually converged eigenpairs. It can be larger or smaller than \p nev.
274  unsigned int nconv = eig_sys.get_n_converged();
275 
276  out << "Number of converged eigenpairs: " << nconv << "\n";
277 
278  // Write the eigen vector to file and the eigenvalues to libMesh::out.
279  for(unsigned int i=0; i<nconv; i++)
280  {
281  std::pair<Real,Real> eigpair = eig_sys.get_eigenpair(i);
282 
283  // get the complete eigenvalue:
284  Complex eigenvalue(eigpair.first, eigpair.second);
285  out<<" "<<eigenvalue<<std::endl;
286 
287  // Here, one needs to formally distinguish between bound states (negative energy)
288  // and free states (positive energy) because bound states have an imaginary momentum.
289  // It is important to ensure that the imaginary part is positive; otherwise we get an exp.
290  // rise instead of exponential decay when computing the shape functions in the infinite element region.
291  if (eigpair.first > 0.)
292  // positive eigen energy
293  eq_sys.parameters.set<Complex>("momentum")=sqrt(eigenvalue*2.);
294  else
295  {
296  // for negative energies: ensure that imaginary contribution is positive!
297  Complex k= sqrt(2.*eigenvalue);
298  if (std::imag(k)>0)
299  eq_sys.parameters.set<Complex>("momentum")=k;
300  else
301  eq_sys.parameters.set<Complex>("momentum")=std::conj(k);
302  }
303 
304 #ifdef LIBMESH_HAVE_EXODUS_API
305  // set the name of the Exodus-file
306  std::ostringstream eigenvector_output_name;
307  eigenvector_output_name << "U" << "-" << i << "_inf.e";
308  ExodusII_IO (mesh).write_equation_systems(eigenvector_output_name.str(), eq_sys);
309 #endif
310 
311  // set the base-name for the free-format file.
312  // This file can be viewed e.g. with gnuplot using
313  // p 're_infini_0.txt' w l
314  std::ostringstream file;
315  file << "infini_" << i << ".txt";
316  // print the solution along the x-coordinate
317  line_print(eq_sys, file.str(), "EigenSE");
318  }
319 
320  // All done.
321 #endif // LIBMESH_ENABLE_INFINITE_ELEMENTS
322 #endif // LIBMESH_HAVE_SLEPC
323  return 0;
324 }
325 
332 void assemble_SchroedingerEquation(EquationSystems &es, const std::string &system_name)
333 {
334 #if defined(LIBMESH_ENABLE_INFINITE_ELEMENTS) && defined(LIBMESH_HAVE_SLEPC)
335  // It is a good idea to make sure we are assembling
336  // the proper system.
337  libmesh_assert_equal_to (system_name, "EigenSE");
338 
339  // Get a constant reference to the mesh object.
340  const MeshBase& mesh = es.get_mesh();
341 
342  // The dimension that we are running.
343  const unsigned int dim = mesh.mesh_dimension();
344 
345  // Get a reference to our system.
346  EigenSystem & eigen_system = es.get_system<EigenSystem> (system_name);
347 
348  // Get a constant reference to the Finite Element type
349  // for the first (and only) variable in the system.
350  FEType fe_type = eigen_system.get_dof_map().variable_type(0);
351 
352  // A reference to the system matrices
353  SparseMatrix<Number>& matrix_A = eigen_system.get_matrix_A();
354  SparseMatrix<Number>& matrix_B = eigen_system.get_matrix_B();
355 
356  // A Gauss quadrature rule for numerical integration.
357  // Use the default quadrature order.
358  QGauss qrule (dim, fe_type.default_quadrature_order());
359 
360  // Build a Finite Element object of the specified type. Since the
361  // \p FEBase::build() member dynamically creates memory we will
362  // store the object as an \p std::unique_ptr<FEBase>. This can be thought
363  // of as a pointer that will clean up after itself.
364  std::unique_ptr<FEBase> fe (FEBase::build(dim, fe_type));
365  std::unique_ptr<FEBase> inf_fe (FEBase::build_InfFE(dim, fe_type));
366 
367  // Tell the finite element object to use our quadrature rule.
368  fe->attach_quadrature_rule (&qrule);
369  inf_fe->attach_quadrature_rule (&qrule);
370 
371  Number E=es.parameters.get<Number>("gsE");
372 
373  /*
374  * this is used as a parameter in the matrix assembly which is used to resemble the exponential
375  * term in the infinite region.
376  * For bound states, i*k is real (and negative), for free states i*k is imaginary.
377  * The latter case is what is usually assumed for infinite elements.
378  */
379  Number ik=-sqrt(-2.*E);
380 
381  // set parameters used for the infinite elements:
382  es.parameters.set<Real>("speed")=137.0359991;
383  es.parameters.set<Number>("current frequency")=es.parameters.get<Real>("speed")*sqrt(2.*E)/(2*pi);
384 
385  // A reference to the \p DofMap object for this system. The \p DofMap
386  // object handles the index translation from node and element numbers
387  // to degree of freedom numbers.
388  const DofMap& dof_map = eigen_system.get_dof_map();
389 
390  // The element mass matrix M and Hamiltonian H.
393 
394  // This vector will hold the degree of freedom indices for
395  // the element. These define where in the global system
396  // the element degrees of freedom get mapped.
397  std::vector<dof_id_type> dof_indices;
398 
399  // Now we will loop over all the elements in the mesh that
400  // live on the local processor. We will compute the element
401  // matrix and right-hand-side contribution. In case users
402  // later modify this program to include refinement, we will
403  // be safe and will only consider the active elements;
404  // hence we use a variant of the \p active_elem_iterator.
405  for (const auto & elem : mesh.active_local_element_ptr_range())
406  {
407  // Get the degree of freedom indices for the
408  // current element. These define where in the global
409  // matrix and right-hand-side this element will
410  // contribute to.
411  dof_map.dof_indices (elem, dof_indices);
412 
413  // unifyging finite and infinite elements
414  FEBase * cfe = nullptr;
415 
416  if (elem->infinite())
417  {
418  // We have an infinite element. Let \p cfe point
419  // to our \p InfFE object. This is handled through
420  // an std::unique_ptr. Through the \p std::unique_ptr::get() we "borrow"
421  // the pointer, while the \p std::unique_ptr \p inf_fe is
422  // still in charge of memory management.
423  cfe = inf_fe.get();
424  }
425  else
426  {
427  cfe = fe.get();
428  }
429 
430  // The element Jacobian * quadrature weight at each integration point.
431  const std::vector<Real> & JxW = cfe->get_JxWxdecay_sq();
432 
433  // The element shape functions evaluated at the quadrature points.
434  const std::vector<std::vector<Real>> & phi = cfe->get_phi_over_decayxR();
435  const std::vector<std::vector<RealGradient>> & dphi = cfe->get_dphi_over_decayxR();
436  const std::vector<Point>& q_point = cfe->get_xyz();
437  // get extra data needed for infinite elements
438  const std::vector<RealGradient>& dphase = cfe->get_dphase();
439  const std::vector<Real> & weight = cfe->get_Sobolev_weightxR_sq();
440  const std::vector<RealGradient> & dweight = cfe->get_Sobolev_dweightxR_sq();
441 
442  // Compute the element-specific data for the current
443  // element. This involves computing the location of the
444  // quadrature points (q_point) and the shape functions
445  // (phi, dphi) for the current element.
446  cfe->reinit (elem);
447 
448  // Zero the element matrices and rhs before
449  // summing them. We use the resize member here because
450  // the number of degrees of freedom might have changed from
451  // the last element. Note that this will be the case if the
452  // element type is different (i.e. the last element was a
453  // triangle, now we are on a quadrilateral).
454  M.resize (dof_indices.size(), dof_indices.size());
455  H.resize (dof_indices.size(), dof_indices.size());
456 
457  // Now loop over the quadrature points. This handles
458  // the numeric integration.
459  //For infinite elements, the number of quadrature points is asked and than looped over; works for finite elements as well.
460  unsigned int max_qp = cfe->n_quadrature_points();
461  for (unsigned int qp=0; qp<max_qp; qp++)
462  {
463 
464  // compute the Coulomb potential
465  Number potval=-1./(q_point[qp]).norm();
466 
467  // Now, get number of shape functions that are nonzero at this point::
468  const unsigned int n_sf =
469  FEInterface::n_dofs(cfe->get_fe_type(), elem);
470  // loop over them:
471  for (unsigned int i=0; i<n_sf; i++)
472  {
473  for (unsigned int j=0; j<n_sf; j++)
474  {
475 
476  // This is just the of derivatives of shape functions i and j.
477  // For finite elements, dweight==0 and dphase==0, thus
478  // temp=dphi[i][qp]*dphi[j][qp].
479  Number temp = (dweight[qp]*phi[i][qp]+weight[qp]*(dphi[i][qp]-ik*dphase[qp]*phi[i][qp]))
480  *(dphi[j][qp]+ik*dphase[qp]*phi[j][qp]);
481 
482  // assemble the Hamiltonian: H=1/2 Nabla^2 + V
483  H(i,j) += JxW[qp]*(0.5*temp + potval*weight[qp]*phi[i][qp]*phi[j][qp]);
484 
485  // assemble the mass matrix:
486  M(i,j) += JxW[qp]*weight[qp]*phi[i][qp]*phi[j][qp];
487  }
488  }
489  }
490  // On an unrefined mesh, constrain_element_matrix does
491  // nothing. If this assembly function is ever repurposed to
492  // run on a refined mesh, getting the hanging node constraints
493  // right will be important. Note that, even with
494  // asymmetric_constraint_rows = false, the constrained dof
495  // diagonals still exist in the matrix, with diagonal entries
496  // that are there to ensure non-singular matrices for linear
497  // solves but which would generate positive non-physical
498  // eigenvalues for eigensolves.
499  dof_map.constrain_element_matrix(M, dof_indices, false);
500  dof_map.constrain_element_matrix(H, dof_indices, false);
501 
502  // Finally, simply add the element contribution to the
503  // overall matrices.
504  matrix_A.add_matrix (H, dof_indices);
505  matrix_B.add_matrix (M, dof_indices);
506 
507  } // end of element loop
508 
509  // After the setup of the main part, we add the contributions from the surface,
510  // arising due to the partial integration.
511  // Keeping these terms follows the suggestion of Bettess (P. Bettess "Infinite Elements", Penshaw Pres 1992)
513  // For easier syntax, we loop over infinite elements and their neighbours separately.
514  // loop over INFINITE ELEMENTS
515  sd=1;{
516  QGauss qrule2 (dim-1, fe_type.default_quadrature_order());
517  auto face_fe = FEBase::build_InfFE(dim, fe_type);
518  face_fe->attach_quadrature_rule (&qrule2);
519 
520  for (const auto & elem : mesh.active_local_subdomain_elements_ptr_range(sd))
521  {
522  //dof_map.dof_indices (elem, dof_indices_lm,lm_num);
523  dof_map.dof_indices (elem, dof_indices);
524 
525  /*
526  * Here we use an inconsistent setup: the Jacobian is weighted while all other quantities are
527  * not weighted.
528  * But on the base face all weights are one; thus, it is only a formal problem.
529  */
530 
531  // Having the correct face, we can start initializing all quantities:
532  const std::vector<Real>& JxW = face_fe->get_JxWxdecay_sq();
533  const std::vector<Point>& q_point = face_fe->get_xyz();
534  const std::vector<std::vector<RealGradient> >& dphi = face_fe->get_dphi();
535  const std::vector<std::vector<Real> >& phi = face_fe->get_phi();
536  const std::vector<Point>& normal = face_fe->get_normals();
537  const std::vector<RealGradient>& dphase = face_fe->get_dphase();
538  const std::vector<Real>& weight = face_fe->get_Sobolev_weight(); // in publication called D
539 
540  // the base side always has number 0
541  const unsigned int side=0;
542  face_fe->reinit (elem, side);
543  const unsigned int n_sf =
544  FEInterface::n_dofs(face_fe->get_fe_type(), elem);
545 
546  H.resize (dof_indices.size(), dof_indices.size());
547 
548  for (unsigned int pts=0; pts<q_point.size(); pts++){
549  for (unsigned int i=0; i<n_sf; i++){
550  // A(i,j)=A(column, row) =A(bra,ket)
551  for (unsigned int j=0; j<n_sf; j++){
552  //WARNING: The normal() of infinite elements points inwards! Thus, the sign is flipped.
553  libmesh_assert(normal[pts]*q_point[pts]/q_point[pts].norm() > 0);
554  H(i,j)-=.5*JxW[pts]*weight[pts]*phi[i][pts]*normal[pts]*(dphi[j][pts]+ik*dphase[pts]*phi[j][pts]);
555  }
556  }
557  }
558  matrix_A.add_matrix (H, dof_indices);
559  } // end of element loop
560  }
561  // loop over NEIGHBOURS OF INFINITE ELEMENTS
562  sd=2; {
563  QGauss qrule2 (dim-1, fe_type.default_quadrature_order());
564  auto face_fe = FEBase::build(dim, fe_type);
565  face_fe->attach_quadrature_rule (&qrule2);
566 
567  for (const auto & elem : mesh.active_local_subdomain_elements_ptr_range(sd))
568  {
569  dof_map.dof_indices (elem, dof_indices);
570 
571 #ifdef DEBUG
572  unsigned int num_neighbors=0;
573 
574  for (unsigned int i=0; i<elem->n_neighbors(); i++){
575  if (elem->neighbor_ptr(i)->infinite()){
576  num_neighbors++;
577  }
578  }
579  libmesh_assert_greater(num_neighbors,0);
580  libmesh_assert_greater(elem->n_neighbors(), num_neighbors);
581 #endif
582 
583  // In contrast to the infinite elements, here it is not as easy to find the correct side.
584  // Also, a single finite element can have multiple infinite neighbours.
585  for(unsigned int prev_neighbor=0; prev_neighbor<elem->n_neighbors(); prev_neighbor++){
586 
587  // Having the correct face, we can start initializing all quantities:
588  const std::vector<Real>& JxW =face_fe->get_JxW();
589  const std::vector<Point>& q_point = face_fe->get_xyz();
590  const std::vector<std::vector<RealGradient> >& dphi = face_fe->get_dphi();
591  const std::vector<std::vector<Real> >& phi = face_fe->get_phi();
592  const std::vector<Point>& normal = face_fe->get_normals();
593 
594  const Elem* relevant_neighbor=elem->neighbor_ptr(prev_neighbor);
595 
596  // Get the correct face to the finite element; for this, continue looping
597  // until a neighbor is an infinite element.
598  // If none is found, we leave this loop
599  for (; prev_neighbor<elem->n_neighbors(); prev_neighbor++){
600  if (elem->neighbor_ptr(prev_neighbor)->infinite()){
601  relevant_neighbor=elem->neighbor_ptr(prev_neighbor);
602  break;
603  }
604  }
605  if ( prev_neighbor == elem->n_neighbors()){
606  break;
607  }
608 
609  // now, we need to find the side of this element which faces the infinite element neighbor.
610  // This is used as a consistency-check in debug-mode.
611  unsigned int side=0;
612  unsigned int found_s=0;
613  // Trying to get a reasonable bound; I would like to avoid that it breaks.
614  Real tol=0.01*elem->hmin();
615  while (found_s==0){
616  for(unsigned int n=0; n< elem->n_sides(); n++){
617  if (relevant_neighbor->close_to_point(elem->side_ptr(n)->vertex_average(), tol)){
618  found_s++;
619  side=n;
620 #ifndef DEBUG
621  // We should never find more than 1 element...
622  break;
623 #endif
624  }
625  }
626  tol*=2.;
627 
628 #ifdef DEBUG
629  // In case of 1/3 elem->hmin(), we can expect the point to be near all neighboring elements.
630  if (tol > 0.2*elem->hmin()){
631  err<<"Warning: tolerance reached "<<tol<<","<<std::endl;
632  err<<" but element-height is "<<elem->hmin()<<std::endl;
633  err<<" (element id: "<<elem->id()<<")"<<std::endl;
634  }
635 #endif
636  }
637 
638 #ifdef DEBUG
639  libmesh_assert_equal_to(found_s, 1);
640 #endif
641  //out<<"neighbor id: "<<relevant_neighbor->id();
642  //out<<" this id: "<<elem->id()<<std::endl;
643  face_fe->reinit (elem, side);
644  const unsigned int n_sf =
645  FEInterface::n_dofs(face_fe->get_fe_type(), elem);
646 
647  H.resize (dof_indices.size(), dof_indices.size());
648 
649  for (unsigned int pts=0; pts<q_point.size(); pts++){
650  // i=bra, j=ket
651  for (unsigned int j=0; j<n_sf; j++){
652  for (unsigned int i=0; i<n_sf; i++){
653  libmesh_assert(normal[pts]*q_point[pts]/q_point[pts].norm() > 0);
654  H(i,j)+=.5*JxW[pts]*normal[pts]*phi[i][pts]*dphi[j][pts];
655  }
656  }
657  }
658  matrix_A.add_matrix (H, dof_indices);
659 
660  } // end infinite neighbor-loop
661  } // end of element loop
662  }
663 
668 #else
669  // Avoid unused variable warnings when compiling without infinite
670  // elements and/or SLEPc enabled.
671  libmesh_ignore(es, system_name);
672 #endif // LIBMESH_ENABLE_INFINITE_ELEMENTS && LIBMESH_HAVE_SLEPC
673 }
674 
675 #ifdef LIBMESH_HAVE_SLEPC
676 //define the functions needed for the @ SlepcSolverConfiguration.
678 {
679  // if a spectral transformation was requested
680  if (_st!=INVALID_ST)
681  {
682  // initialise the st with the default values and change the spectral transformation value.
683  ST st;
684  PetscErrorCode ierr = EPSGetST(_slepc_solver.eps(), &st);
685  if (ierr)
686  libmesh_error();
687 
688  // Set it to the desired type of spectral transformation.
689  // The value of the respective shift is chosen to be the target
690  // specified via \p set_position_of_spectrum().
691  switch (_st)
692  {
693  case SHIFT:
694  ierr = STSetType(st, STSHIFT);
695  break;
696  case SINVERT:
697  // this method has been renamed in 3.1
698 #if SLEPC_VERSION_LESS_THAN(3,1,0)
699  ierr = STSetType(st, STSINV);
700 #else
701  ierr = STSetType(st, STSINVERT);
702 #endif
703  break;
704  case CAYLEY:
705  ierr = STSetType(st, STCAYLEY);
706  break;
707  default:
708  // print a warning but do nothing more.
709  break;
710  }
711  if (ierr)
712  libmesh_error();
713 
714  // since st is a reference to the particular object used by \p _slepc_solver,
715  // we don't need to hand back the manipulated object. It will be applied before
716  // solving the system automatically.
717  }
718 }
719 #endif // LIBMESH_HAVE_SLEPC
720 
721 // To see the solution, even in the infinite-element region.
722 // The analytic solution is c*exp(-abs(|r|)) where r is the distance from the origin
723 // and C is some normalization constant (here ~0.08).
724 // The gradient is given just to show that it works.
725 void line_print(EquationSystems& es, std::string output, std::string SysName)
726 {
727 
728  // this function does not work without infinite elements properly.
729 #ifndef LIBMESH_ENABLE_INFINITE_ELEMENTS
730  libmesh_ignore(es, output, SysName);
731 #else
732 
733  //Since we don't need any functionality that is special for EigenSystem,
734  // we can use the base class here, thus keeping this function more general.
735  System & system = es.get_system<System> (SysName);
736 
737  Real r = 2*es.parameters.get<Real>("radius");
738 
739  // get the variable number to specify, at which quantity we want to look later:
740  unsigned short int phi_var=system.variable_number("phi");
741 
742 
743  // set the full name of output-files:
744  std::ostringstream re_output;
745  re_output<<"re_"<<output;
746  std::ostringstream im_output;
747  im_output<<"im_"<<output;
748  std::ostringstream abs_output;
749  abs_output<<"abs_"<<output;
750 
751  // create output-objects, writing in respective files:
752  std::ofstream im_out(re_output.str());
753  std::ofstream re_out(im_output.str());
754  std::ofstream abs_out(abs_output.str());
755 
756  Real N = 100.;
757  Point q_point;
758  const Real start=-r;
759  Number soln;
760  Gradient grad;
761 
762  for (int pts=1;pts<=N;pts++)
763  {
764 
765  //specify the point to look at; going from -r to +r.
766  q_point = Point(start+ 2*pts*r/N, 0., 0.);
767 
768  soln=system.point_value(phi_var, q_point);
769  grad=system.point_gradient(phi_var, q_point);
770 
771  // and print them to the output-file:
772  re_out<<" "<<std::setw(12)<<q_point(0);
773  im_out<<" "<<std::setw(12)<<q_point(0);
774  abs_out<<" "<<std::setw(12)<<q_point(0);
775  re_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::real(soln);
776  im_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::imag(soln);
777  abs_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::abs(soln);
778  // print the probability-current as well:
779  re_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::imag(grad(0)*soln)<<std::endl;
780  im_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::imag(grad(0)*soln)<<std::endl;
781  abs_out<<" "<<std::setw(12)<<std::scientific<<std::setprecision(6)<<std::abs(std::imag(grad(0)*soln))<<std::endl;
782 
783  // check that the solution is close to the analytic answer.
784  // Comparing abs(soln) because there is a degree of freedom in the global phase.
785  libmesh_assert_less(std::abs(std::abs(soln)-0.08*exp(-q_point.norm())), .002);
786 
787  }
788 #endif //LIBMESH_ENABLE_INFINITE_ELEMENTS
789 }
SlepcSolverConfiguration(libMesh::SlepcEigenSolver< libMesh::Number > &slepc_eigen_solver)
Constructor: get a reference to the SlepcEigenSolver variable to be able to manipulate it...
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition: fe_type.h:196
OStreamProxy err
virtual std::pair< Real, Real > get_eigenpair(dof_id_type i)
Definition: eigen_system.C:308
This is the EquationSystems class.
void build_sphere(UnstructuredMesh &mesh, const Real rad=1, const unsigned int nr=2, const ElemType type=INVALID_ELEM, const unsigned int n_smooth=2, const bool flat=true)
Meshes a spherical or mapped-spherical domain.
static unsigned int n_dofs(const unsigned int dim, const FEType &fe_t, const ElemType t)
Definition: fe_interface.C:355
virtual const std::vector< Real > & get_Sobolev_weightxR_sq() const
Definition: fe_base.h:470
Gradient point_gradient(unsigned int var, const Point &p, const bool insist_on_success=true, const NumericVector< Number > *sol=nullptr) const
Definition: system.C:2550
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
A class that interfaces the SolverConfiguration to add the SLEPC option SetST.
const SparseMatrix< Number > & get_matrix_A() const
Definition: eigen_system.C:333
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition: dof_map.C:2164
static constexpr Real TOLERANCE
unsigned int dim
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition: exodusII_io.h:52
const SparseMatrix< Number > & get_matrix_B() const
Definition: eigen_system.C:351
const FEType & variable_type(const unsigned int c) const
Definition: dof_map.h:2220
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
Number point_value(unsigned int var, const Point &p, const bool insist_on_success=true, const NumericVector< Number > *sol=nullptr) const
Definition: system.C:2421
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition: libmesh.h:90
The libMesh namespace provides an interface to certain functionality in the library.
const EigenSolver< Number > & get_eigen_solver() const
Definition: eigen_system.C:482
void assemble_SchroedingerEquation(EquationSystems &, const std::string &)
In this function, we assemble the actual system matrices.
const T_sys & get_system(std::string_view name) const
void line_print(EquationSystems &es, std::string output, std::string SysName)
This is the MeshBase class.
Definition: mesh_base.h:75
const std::vector< OutputGradient > & get_dphase() const
Definition: fe_base.h:437
unsigned int variable_number(std::string_view var) const
Definition: system.C:1609
int main(int argc, char **argv)
libMesh::SlepcEigenSolver< libMesh::Number > & _slepc_solver
The linear solver object that we are configuring.
FEType get_fe_type() const
Definition: fe_abstract.h:520
This class handles the numbering of degrees of freedom on a mesh.
Definition: dof_map.h:179
void libmesh_ignore(const Args &...)
virtual void add_matrix(const DenseMatrix< T > &dm, const std::vector< numeric_index_type > &rows, const std::vector< numeric_index_type > &cols)=0
Add the full matrix dm to the SparseMatrix.
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true)=0
Locate element face (edge in 2D) neighbors.
T pow(const T &x)
Definition: utility.h:328
const T & get(std::string_view) const
Definition: parameters.h:426
void SetST(SpectralTransform st)
This is the main functionality of this little class.
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition: mesh_tools.C:437
virtual void write_equation_systems(const std::string &fname, const EquationSystems &es, const std::set< std::string > *system_names=nullptr) override
Writes out the solution for no specific time or timestep.
Definition: exodusII_io.C:2033
virtual const std::vector< Real > & get_JxWxdecay_sq() const
This function is the variant of get_JxW() for InfFE.
Definition: fe_abstract.h:292
virtual unsigned int n_quadrature_points() const
Definition: fe_abstract.C:1279
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition: system.h:96
virtual const std::vector< std::vector< OutputShape > > & get_phi_over_decayxR() const
Definition: fe_base.h:493
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
libmesh_assert(ctx)
SpectralTransform
Defines an enum for spectral tronsformations applied before solving the (generalised) eigenproblem...
This class stores solver configuration data, e.g.
virtual void reinit(const Elem *elem, const std::vector< Point > *const pts=nullptr, const std::vector< Real > *const weights=nullptr)=0
This is at the core of this class.
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition: system.C:1357
virtual_for_inffe const std::vector< Point > & get_xyz() const
Definition: fe_abstract.h:280
void attach_assemble_function(void fptr(EquationSystems &es, const std::string &name))
Register a user function to use in assembling the system matrix and RHS.
Definition: system.C:2161
auto norm(const T &a) -> decltype(std::abs(a))
Definition: tensor_tools.h:74
virtual void solve() override
Assembles & solves the eigen system.
Definition: eigen_system.C:287
This class is used to build infinite elements on top of an existing mesh.
std::complex< Real > Complex
const Elem * neighbor_ptr(unsigned int i) const
Definition: elem.h:2598
virtual bool close_to_point(const Point &p, Real tol) const
Definition: elem.C:2776
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual SimpleRange< element_iterator > active_local_subdomain_elements_ptr_range(subdomain_id_type sid)=0
const Point build_inf_elem(const bool be_verbose=false)
Build infinite elements atop a volume-based mesh, determine origin automatically. ...
~SlepcSolverConfiguration()
empty destructor
T & set(const std::string &)
Definition: parameters.h:469
OStreamProxy out
const MeshBase & get_mesh() const
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
Definition: dense_matrix.h:895
This class implements specific orders of Gauss quadrature.
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
Parameters parameters
Data structure holding arbitrary parameters.
void set_eigenproblem_type(EigenProblemType ept)
Sets the type of the current eigen problem.
Definition: eigen_system.C:85
static std::unique_ptr< FEGenericBase > build_InfFE(const unsigned int dim, const FEType &type)
Builds a specific infinite element type.
virtual const std::vector< RealGradient > & get_Sobolev_dweightxR_sq() const
Definition: fe_base.h:480
boost::multiprecision::float128 imag(const boost::multiprecision::float128)
unsigned int get_n_converged() const
Definition: eigen_system.h:130
virtual void init()
Initialize all the systems.
void set_solver_configuration(SolverConfiguration &solver_configuration)
Set the solver configuration object.
Definition: eigen_solver.C:79
virtual System & add_system(std::string_view system_type, std::string_view name)
Add the system of type system_type named name to the systems array.
Manages consistently variables, degrees of freedom, and coefficient vectors for eigenvalue problems...
Definition: eigen_system.h:55
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition: mesh.h:50
virtual void configure_solver() override
Apply solver options to a particular solver.
const DofMap & get_dof_map() const
Definition: system.h:2374
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
This class forms the foundation from which generic finite elements may be derived.
virtual const std::vector< std::vector< OutputGradient > > & get_dphi_over_decayxR() const
Definition: fe_base.h:501
const Real pi
.
Definition: libmesh.h:299
void constrain_element_matrix(DenseMatrix< Number > &matrix, std::vector< dof_id_type > &elem_dofs, bool asymmetric_constraint_rows=true) const
Constrains the element matrix.
Definition: dof_map.h:2317