libMesh
Loading...
Searching...
No Matches
miscellaneous_ex14.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
65EXTERN_C_FOR_SLEPC_BEGIN
66# include <slepceps.h>
67EXTERN_C_FOR_SLEPC_END
68#endif // LIBMESH_HAVE_SLEPC
69
70// Bring in everything from the libMesh namespace
71using namespace libMesh;
72
73#ifdef LIBMESH_HAVE_SLEPC
81
83};
84
91{
92public:
93
98 _slepc_solver(slepc_eigen_solver),
100 {}
101
106
107 virtual void configure_solver() override;
108
113 { _st=st;}
114
115private:
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
128void assemble_SchroedingerEquation(EquationSystems & , const std::string &);
129
130// print the solution along the x-axis.
131void line_print(EquationSystems& es, std::string output, std::string SysName);
132
133
134int 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!
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
332void 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.
725void 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}
unsigned int dim
A class that interfaces the SolverConfiguration to add the SLEPC option SetST.
libMesh::SlepcEigenSolver< libMesh::Number > & _slepc_solver
The linear solver object that we are configuring.
SlepcSolverConfiguration(libMesh::SlepcEigenSolver< libMesh::Number > &slepc_eigen_solver)
Constructor: get a reference to the SlepcEigenSolver variable to be able to manipulate it.
void SetST(SpectralTransform st)
This is the main functionality of this little class.
virtual void configure_solver() override
Apply solver options to a particular solver.
~SlepcSolverConfiguration()
empty destructor
Defines a dense matrix for use in Finite Element-type computations.
void resize(const unsigned int new_m, const unsigned int new_n)
Resizes the matrix to the specified size and calls zero().
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
void dof_indices(const Elem *const elem, std::vector< dof_id_type > &di) const
Definition dof_map.C:2201
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:2485
const FEType & variable_type(const unsigned int i) const
Definition dof_map.h:2388
void set_solver_configuration(SolverConfiguration &solver_configuration)
Set the solver configuration object.
Manages consistently variables, degrees of freedom, and coefficient vectors for eigenvalue problems.
void set_eigenproblem_type(EigenProblemType ept)
Sets the type of the current eigen problem.
const SparseMatrix< Number > & get_matrix_B() const
virtual void solve() override
Assembles & solves the eigen system.
virtual std::pair< Real, Real > get_eigenpair(dof_id_type i)
unsigned int get_n_converged() const
const SparseMatrix< Number > & get_matrix_A() const
const EigenSolver< Number > & get_eigen_solver() const
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Real hmin() const
Definition elem.C:683
virtual bool close_to_point(const Point &p, Real tol) const
Definition elem.C:2809
const Elem * neighbor_ptr(unsigned int i) const
Definition elem.h:2615
This is the EquationSystems class.
const MeshBase & get_mesh() const
Parameters parameters
Data structure holding arbitrary parameters.
virtual void init()
Initialize all the systems.
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.
const T_sys & get_system(std::string_view name) const
The ExodusII_IO class implements reading meshes in the ExodusII file format from Sandia National Labs...
Definition exodusII_io.h:53
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.
virtual unsigned int n_quadrature_points() const
virtual_for_inffe const std::vector< Point > & get_xyz() const
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.
FEType get_fe_type() const
virtual const std::vector< Real > & get_JxWxdecay_sq() const
This function is the variant of get_JxW() for InfFE.
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
static std::unique_ptr< FEGenericBase > build(const unsigned int dim, const FEType &type)
Builds a specific finite element type.
static std::unique_ptr< FEGenericBase > build_InfFE(const unsigned int dim, const FEType &type)
Builds a specific infinite element type.
const std::vector< OutputGradient > & get_dphase() const
Definition fe_base.h:437
virtual const std::vector< Real > & get_Sobolev_weightxR_sq() const
Definition fe_base.h:470
virtual const std::vector< std::vector< OutputGradient > > & get_dphi_over_decayxR() const
Definition fe_base.h:501
virtual const std::vector< std::vector< OutputShape > > & get_phi_over_decayxR() const
Definition fe_base.h:493
virtual const std::vector< RealGradient > & get_Sobolev_dweightxR_sq() const
Definition fe_base.h:480
static unsigned int n_dofs(const unsigned int dim, const FEType &fe_t, const ElemType t)
class FEType hides (possibly multiple) FEFamily and approximation orders, thereby enabling specialize...
Definition fe_type.h:197
This class is used to build infinite elements on top of an existing mesh.
const Point build_inf_elem(const bool be_verbose=false)
Build infinite elements atop a volume-based mesh, determine origin automatically.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
This is the MeshBase class.
Definition mesh_base.h:81
unsigned int mesh_dimension() const
Definition mesh_base.C:430
virtual SimpleRange< element_iterator > active_local_subdomain_elements_ptr_range(subdomain_id_type sid)=0
virtual void find_neighbors(const bool reset_remote_elements=false, const bool reset_current_list=true, const bool assert_valid=true)=0
Locate element face (edge in 2D) neighbors.
The Mesh class is a thin wrapper, around the ReplicatedMesh class by default.
Definition mesh.h:51
T & set(const std::string &)
Definition parameters.h:494
const T & get(std::string_view) const
Definition parameters.h:451
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
This class implements specific orders of Gauss quadrature.
This class provides an interface to the SLEPc eigenvalue solver library from http://slepc....
This class stores solver configuration data, e.g.
Generic sparse matrix.
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.
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
Number point_value(unsigned int var, const Point &p, const bool insist_on_success=true, const NumericVector< Number > *sol=nullptr) const
Definition system.C:2219
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:1959
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:1344
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
Gradient point_gradient(unsigned int var, const Point &p, const bool insist_on_success=true, const NumericVector< Number > *sol=nullptr) const
Definition system.C:2348
const DofMap & get_dof_map() const
Definition system.h:2417
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
MeshBase & mesh
void line_print(EquationSystems &es, std::string output, std::string SysName)
void assemble_SchroedingerEquation(EquationSystems &, const std::string &)
In this function, we assemble the actual system matrices.
SpectralTransform
Defines an enum for spectral tronsformations applied before solving the (generalised) eigenproblem.
@ SINVERT
@ INVALID_ST
void build_sphere(UnstructuredMesh &mesh, const Real radius=1, const unsigned int n_refinements=2, const ElemType type=INVALID_ELEM, const unsigned int n_smooth=2, const bool flat=true)
Fills mesh with a mesh discretizing a ball (||x||<= radius) or sphere (||x|| = radius) domain.
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
std::complex< Real > Complex
void libmesh_ignore(const Args &...)
libmesh_assert(ctx)
OStreamProxy out
const Real pi
.
Definition libmesh.h:292
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
boost::multiprecision::float128 imag(const boost::multiprecision::float128)
int main()