LCOV - code coverage report
Current view: top level - src/solvers - eigen_time_solver.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 0 79 0.0 %
Date: 2025-08-19 19:27:09 Functions: 0 9 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      20             : #include "libmesh/libmesh_config.h"
      21             : #ifdef LIBMESH_HAVE_SLEPC
      22             : 
      23             : #include "libmesh/diff_system.h"
      24             : #include "libmesh/eigen_time_solver.h"
      25             : #include "libmesh/eigen_solver.h"
      26             : #include "libmesh/sparse_matrix.h"
      27             : #include "libmesh/enum_eigen_solver_type.h"
      28             : 
      29             : namespace libMesh
      30             : {
      31             : 
      32             : // Constructor
      33           0 : EigenTimeSolver::EigenTimeSolver (sys_type & s)
      34             :   : Parent(s),
      35           0 :     eigen_solver     (EigenSolver<Number>::build(s.comm())),
      36           0 :     tol(pow(TOLERANCE, 5./3.)),
      37           0 :     maxits(1000),
      38           0 :     n_eigenpairs_to_compute(5),
      39           0 :     n_basis_vectors_to_use(3*n_eigenpairs_to_compute),
      40           0 :     n_converged_eigenpairs(0),
      41           0 :     n_iterations_reqd(0)
      42             : {
      43             :   libmesh_experimental();
      44           0 :   eigen_solver->set_eigenproblem_type(GHEP);//or GNHEP
      45           0 :   eigen_solver->set_position_of_spectrum(LARGEST_MAGNITUDE);
      46           0 : }
      47             : 
      48           0 : EigenTimeSolver::~EigenTimeSolver () = default;
      49             : 
      50           0 : void EigenTimeSolver::reinit ()
      51             : {
      52             :   // empty...
      53           0 : }
      54             : 
      55           0 : void EigenTimeSolver::init ()
      56             : {
      57             :   // Add matrix "B" to _system if not already there.
      58             :   // The user may have already added a matrix "B" before
      59             :   // calling the System initialization.  This would be
      60             :   // necessary if e.g. the System originally started life
      61             :   // with a different type of TimeSolver and only later
      62             :   // had its TimeSolver changed to an EigenTimeSolver.
      63           0 :   if (!_system.have_matrix("B"))
      64           0 :     _system.add_matrix("B");
      65           0 : }
      66             : 
      67           0 : void EigenTimeSolver::solve ()
      68             : {
      69             :   // The standard implementation is basically to call:
      70             :   // _diff_solver->solve();
      71             :   // which internally assembles (when necessary) matrices and vectors
      72             :   // and calls linear solver software while also doing Newton steps (see newton_solver.C)
      73             :   //
      74             :   // The element_residual and side_residual functions below control
      75             :   // what happens in the interior of the element assembly loops.
      76             :   // We have a system reference, so it's possible to call _system.assembly()
      77             :   // ourselves if we want to...
      78             :   //
      79             :   // Interestingly, for the EigenSolver we don't need residuals...just Jacobians.
      80             :   // The Jacobian should therefore always be requested, and always return
      81             :   // jacobian_computed as being true.
      82             : 
      83             :   // The basic plan of attack is:
      84             :   // .) Construct the Jacobian using _system.assembly(true,true) as we
      85             :   //    would for a steady system.  Use a flag in this class to
      86             :   //    control behavior in element_residual and side_residual
      87             :   // .) Swap _system.matrix to matrix "B" (be sure to add this extra matrix during init)
      88             :   // .) Call _system.assembly(true,true) again, using the flag in element_residual
      89             :   //    and side_residual to only get the mass matrix terms.
      90             :   // .) Send A and B to Steffen's EigenSolver interface.
      91             : 
      92             :   // Assemble the spatial part (matrix A) of the operator
      93           0 :   if (!this->quiet)
      94           0 :     libMesh::out << "Assembling matrix A." << std::endl;
      95           0 :   _system.matrix =   &( _system.get_system_matrix() );
      96           0 :   this->now_assembling = Matrix_A;
      97           0 :   _system.assembly(true, true);
      98             :   //_system.matrix->print_matlab("matrix_A.m");
      99             : 
     100             :   // Point the system's matrix at B, call assembly again.
     101           0 :   if (!this->quiet)
     102           0 :     libMesh::out << "Assembling matrix B." << std::endl;
     103           0 :   _system.matrix =   &( _system.get_matrix ("B") );
     104           0 :   this->now_assembling = Matrix_B;
     105           0 :   _system.assembly(true, true);
     106             :   //_system.matrix->print_matlab("matrix_B.m");
     107             : 
     108             :   // Send matrices A, B to Steffen's SlepcEigenSolver interface
     109             :   //libmesh_here();
     110           0 :   if (!this->quiet)
     111           0 :     libMesh::out << "Calling the EigenSolver." << std::endl;
     112           0 :   std::tie(this->n_converged_eigenpairs, this->n_iterations_reqd) =
     113           0 :     eigen_solver->solve_generalized (_system.get_system_matrix(),
     114           0 :                                      _system.get_matrix ("B"),
     115           0 :                                      n_eigenpairs_to_compute,
     116           0 :                                      n_basis_vectors_to_use,
     117             :                                      tol,
     118           0 :                                      maxits);
     119           0 : }
     120             : 
     121             : 
     122             : 
     123           0 : bool EigenTimeSolver::element_residual(bool request_jacobian,
     124             :                                        DiffContext & context)
     125             : {
     126             :   // The EigenTimeSolver always computes jacobians!
     127           0 :   libmesh_assert (request_jacobian);
     128             : 
     129           0 :   context.elem_solution_rate_derivative = 1;
     130           0 :   context.elem_solution_derivative = 1;
     131             : 
     132             :   // Assemble the operator for the spatial part.
     133           0 :   if (now_assembling == Matrix_A)
     134             :     {
     135             :       bool jacobian_computed =
     136           0 :         _system.get_physics()->element_time_derivative(request_jacobian, context);
     137             : 
     138             :       // The user shouldn't compute a jacobian unless requested
     139           0 :       libmesh_assert(request_jacobian || !jacobian_computed);
     140             : 
     141             :       bool jacobian_computed2 =
     142           0 :         _system.get_physics()->element_constraint(jacobian_computed, context);
     143             : 
     144             :       // The user shouldn't compute a jacobian unless requested
     145           0 :       libmesh_assert (jacobian_computed || !jacobian_computed2);
     146             : 
     147           0 :       return jacobian_computed && jacobian_computed2;
     148             : 
     149             :     }
     150             : 
     151             :   // Assemble the mass matrix operator
     152           0 :   else if (now_assembling == Matrix_B)
     153             :     {
     154             :       bool mass_jacobian_computed =
     155           0 :         _system.get_physics()->mass_residual(request_jacobian, context);
     156             : 
     157             :       // Scale Jacobian by -1 to get positive matrix from new negative
     158             :       // mass_residual convention
     159           0 :       context.get_elem_jacobian() *= -1.0;
     160             : 
     161           0 :       return mass_jacobian_computed;
     162             :     }
     163             : 
     164             :   else
     165           0 :     libmesh_error_msg("Unrecognized value now_assembling = " << now_assembling);
     166             : }
     167             : 
     168             : 
     169             : 
     170           0 : bool EigenTimeSolver::side_residual(bool request_jacobian,
     171             :                                     DiffContext & context)
     172             : {
     173             :   // The EigenTimeSolver always requests jacobians?
     174             :   //libmesh_assert (request_jacobian);
     175             : 
     176           0 :   context.elem_solution_rate_derivative = 1;
     177           0 :   context.elem_solution_derivative = 1;
     178             : 
     179             :   // Assemble the operator for the spatial part.
     180           0 :   if (now_assembling == Matrix_A)
     181             :     {
     182             :       bool jacobian_computed =
     183           0 :         _system.get_physics()->side_time_derivative(request_jacobian, context);
     184             : 
     185             :       // The user shouldn't compute a jacobian unless requested
     186           0 :       libmesh_assert (request_jacobian || !jacobian_computed);
     187             : 
     188             :       bool jacobian_computed2 =
     189           0 :         _system.get_physics()->side_constraint(jacobian_computed, context);
     190             : 
     191             :       // The user shouldn't compute a jacobian unless requested
     192           0 :       libmesh_assert (jacobian_computed || !jacobian_computed2);
     193             : 
     194           0 :       return jacobian_computed && jacobian_computed2;
     195             : 
     196             :     }
     197             : 
     198             :   // There is now a "side" equivalent for the mass matrix
     199           0 :   else if (now_assembling == Matrix_B)
     200             :     {
     201             :       bool mass_jacobian_computed =
     202           0 :         _system.get_physics()->side_mass_residual(request_jacobian, context);
     203             : 
     204             :       // Scale Jacobian by -1 to get positive matrix from new negative
     205             :       // mass_residual convention
     206           0 :       context.get_elem_jacobian() *= -1.0;
     207             : 
     208           0 :       return mass_jacobian_computed;
     209             :     }
     210             : 
     211             :   else
     212           0 :     libmesh_error_msg("Unrecognized value now_assembling = " << now_assembling);
     213             : }
     214             : 
     215             : 
     216             : 
     217           0 : bool EigenTimeSolver::nonlocal_residual(bool request_jacobian,
     218             :                                         DiffContext & context)
     219             : {
     220             :   // The EigenTimeSolver always requests jacobians?
     221             :   //libmesh_assert (request_jacobian);
     222             : 
     223             :   // Assemble the operator for the spatial part.
     224           0 :   if (now_assembling == Matrix_A)
     225             :     {
     226             :       bool jacobian_computed =
     227           0 :         _system.get_physics()->nonlocal_time_derivative(request_jacobian, context);
     228             : 
     229             :       // The user shouldn't compute a jacobian unless requested
     230           0 :       libmesh_assert (request_jacobian || !jacobian_computed);
     231             : 
     232             :       bool jacobian_computed2 =
     233           0 :         _system.get_physics()->nonlocal_constraint(jacobian_computed, context);
     234             : 
     235             :       // The user shouldn't compute a jacobian unless requested
     236           0 :       libmesh_assert (jacobian_computed || !jacobian_computed2);
     237             : 
     238           0 :       return jacobian_computed && jacobian_computed2;
     239             : 
     240             :     }
     241             : 
     242             :   // There is now a "side" equivalent for the mass matrix
     243           0 :   else if (now_assembling == Matrix_B)
     244             :     {
     245             :       bool mass_jacobian_computed =
     246           0 :         _system.get_physics()->nonlocal_mass_residual(request_jacobian, context);
     247             : 
     248             :       // Scale Jacobian by -1 to get positive matrix from new negative
     249             :       // mass_residual convention
     250           0 :       context.get_elem_jacobian() *= -1.0;
     251             : 
     252           0 :       return mass_jacobian_computed;
     253             :     }
     254             : 
     255             :   else
     256           0 :     libmesh_error_msg("Unrecognized value now_assembling = " << now_assembling);
     257             : }
     258             : 
     259             : } // namespace libMesh
     260             : 
     261             : #endif // LIBMESH_HAVE_SLEPC

Generated by: LCOV version 1.14