libMesh
Loading...
Searching...
No Matches
Classes | Enumerations | Functions
miscellaneous_ex14.C File Reference

Go to the source code of this file.

Classes

class  SlepcSolverConfiguration
 A class that interfaces the SolverConfiguration to add the SLEPC option SetST. More...
 

Enumerations

enum  SpectralTransform { SHIFT =0 , SINVERT , CAYLEY , INVALID_ST }
 Defines an enum for spectral tronsformations applied before solving the (generalised) eigenproblem. More...
 

Functions

void assemble_SchroedingerEquation (EquationSystems &es, const std::string &system_name)
 In this function, we assemble the actual system matrices.
 
void line_print (EquationSystems &es, std::string output, std::string SysName)
 
int main (int argc, char **argv)
 

Enumeration Type Documentation

◆ SpectralTransform

Defines an enum for spectral tronsformations applied before solving the (generalised) eigenproblem.

Enumerator
SHIFT 
SINVERT 
CAYLEY 
INVALID_ST 

Definition at line 78 of file miscellaneous_ex14.C.

78 {SHIFT=0,
79 SINVERT,
80 CAYLEY,
81
83};
@ SINVERT
@ INVALID_ST

Function Documentation

◆ assemble_SchroedingerEquation()

void assemble_SchroedingerEquation ( EquationSystems es,
const std::string &  system_name 
)

In this function, we assemble the actual system matrices.

The initial equation is the eigen system \( H \Psi = \epsilon \Psi \) where H is the Hamilton operator and Psi the eigen vector with corresponding eigen value \(\epsilon\). In the FEM-scheme, this becomes the generalised eigen problem \( H \Psi = \epsilon M \Psi \) where M is the element mass matrix.

All done!

Definition at line 332 of file miscellaneous_ex14.C.

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}
unsigned int dim
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
Manages consistently variables, degrees of freedom, and coefficient vectors for eigenvalue problems.
const SparseMatrix< Number > & get_matrix_B() const
const SparseMatrix< Number > & get_matrix_A() 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
const MeshBase & get_mesh() const
Parameters parameters
Data structure holding arbitrary parameters.
const T_sys & get_system(std::string_view name) const
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 is the MeshBase class.
Definition mesh_base.h:81
unsigned int mesh_dimension() const
Definition mesh_base.C:430
T & set(const std::string &)
Definition parameters.h:494
const T & get(std::string_view) const
Definition parameters.h:451
This class implements specific orders of Gauss quadrature.
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.
const DofMap & get_dof_map() const
Definition system.h:2417
MeshBase & mesh
dof_id_type weight(const MeshBase &mesh, const processor_id_type pid)
Definition mesh_tools.C:444
auto norm(const T &a)
OStreamProxy err
void libmesh_ignore(const Args &...)
libmesh_assert(ctx)
const Real pi
.
Definition libmesh.h:292
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

References libMesh::MeshBase::active_local_subdomain_elements_ptr_range(), libMesh::SparseMatrix< T >::add_matrix(), libMesh::FEGenericBase< OutputType >::build(), libMesh::FEGenericBase< OutputType >::build_InfFE(), libMesh::Elem::close_to_point(), libMesh::DofMap::constrain_element_matrix(), dim, libMesh::DofMap::dof_indices(), libMesh::err, libMesh::Parameters::get(), libMesh::System::get_dof_map(), libMesh::FEGenericBase< OutputType >::get_dphase(), libMesh::FEGenericBase< OutputType >::get_dphi_over_decayxR(), libMesh::FEAbstract::get_fe_type(), libMesh::FEAbstract::get_JxWxdecay_sq(), libMesh::EigenSystem::get_matrix_A(), libMesh::EigenSystem::get_matrix_B(), libMesh::EquationSystems::get_mesh(), libMesh::FEGenericBase< OutputType >::get_phi_over_decayxR(), libMesh::FEGenericBase< OutputType >::get_Sobolev_dweightxR_sq(), libMesh::FEGenericBase< OutputType >::get_Sobolev_weightxR_sq(), libMesh::EquationSystems::get_system(), libMesh::FEAbstract::get_xyz(), libMesh::Elem::hmin(), libMesh::libmesh_assert(), libMesh::libmesh_ignore(), mesh, libMesh::MeshBase::mesh_dimension(), libMesh::FEInterface::n_dofs(), libMesh::FEAbstract::n_quadrature_points(), libMesh::Elem::neighbor_ptr(), libMesh::EquationSystems::parameters, libMesh::pi, libMesh::Real, libMesh::FEAbstract::reinit(), libMesh::DenseMatrix< T >::resize(), libMesh::Parameters::set(), and libMesh::DofMap::variable_type().

Referenced by main().

◆ line_print()

void line_print ( EquationSystems es,
std::string  output,
std::string  SysName 
)

Definition at line 725 of file miscellaneous_ex14.C.

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}
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
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
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
auto norm() const
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
boost::multiprecision::float128 real(const boost::multiprecision::float128 in)
boost::multiprecision::float128 imag(const boost::multiprecision::float128)

References libMesh::Parameters::get(), libMesh::EquationSystems::get_system(), std::imag(), libMesh::libmesh_ignore(), libMesh::EquationSystems::parameters, libMesh::System::point_gradient(), libMesh::System::point_value(), std::real(), libMesh::Real, and libMesh::System::variable_number().

Referenced by main().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 134 of file miscellaneous_ex14.C.

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}
A class that interfaces the SolverConfiguration to add the SLEPC option SetST.
void set_solver_configuration(SolverConfiguration &solver_configuration)
Set the solver configuration object.
void set_eigenproblem_type(EigenProblemType ept)
Sets the type of the current eigen problem.
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 EigenSolver< Number > & get_eigen_solver() const
This is the EquationSystems class.
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.
This class is used to build infinite elements on top of an existing mesh.
The LibMeshInit class, when constructed, initializes the dependent libraries (e.g.
Definition libmesh.h:92
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
This class provides an interface to the SLEPc eigenvalue solver library from http://slepc....
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
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.
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.
void init(triangulateio &t)
Initializes the fields of t to nullptr/0 as necessary.
T pow(const T &x)
Definition utility.h:296
std::complex< Real > Complex
OStreamProxy out
static constexpr Real TOLERANCE

References libMesh::EquationSystems::add_system(), libMesh::System::add_variable(), libMesh::ARNOLDI, assemble_SchroedingerEquation(), libMesh::System::attach_assemble_function(), libMesh::InfElemBuilder::build_inf_elem(), libMesh::MeshTools::Generation::build_sphere(), libMesh::CARTESIAN, dim, libMesh::MeshBase::find_neighbors(), libMesh::FOURTH, libMesh::EigenSystem::get_eigen_solver(), libMesh::EigenSystem::get_eigenpair(), libMesh::EigenSystem::get_n_converged(), libMesh::GNHEP, libMesh::HEX27, std::imag(), libMesh::EquationSystems::init(), libMesh::JACOBI_20_00, libMesh::KRYLOVSCHUR, libMesh::LAGRANGE, line_print(), main(), mesh, libMesh::out, libMesh::EquationSystems::parameters, libMesh::Real, libMesh::SECOND, libMesh::Parameters::set(), libMesh::EigenSystem::set_eigenproblem_type(), libMesh::EigenSolver< T >::set_solver_configuration(), SINVERT, libMesh::EigenSystem::solve(), libMesh::TOLERANCE, and libMesh::ExodusII_IO::write_equation_systems().