LCOV - code coverage report
Current view: top level - src/executioners - Eigenvalue.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 121 129 93.8 %
Date: 2025-07-17 01:28:37 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : // MOOSE includes
      11             : #include "Eigenvalue.h"
      12             : #include "EigenProblem.h"
      13             : #include "Factory.h"
      14             : #include "MooseApp.h"
      15             : #include "NonlinearEigenSystem.h"
      16             : #include "PetscSupport.h"
      17             : #include "SlepcSupport.h"
      18             : #include "UserObject.h"
      19             : 
      20             : #include "libmesh/petsc_solver_exception.h"
      21             : 
      22             : // Needed for LIBMESH_CHECK_ERR
      23             : using libMesh::PetscSolverException;
      24             : 
      25             : registerMooseObject("MooseApp", Eigenvalue);
      26             : 
      27             : InputParameters
      28       15385 : Eigenvalue::validParams()
      29             : {
      30       15385 :   InputParameters params = Executioner::validParams();
      31             : 
      32       15385 :   params.addClassDescription(
      33             :       "Eigenvalue solves a standard/generalized linear or nonlinear eigenvalue problem");
      34             : 
      35       15385 :   params += FEProblemSolve::validParams();
      36       15385 :   params.addParam<Real>("time", 0.0, "System time");
      37             : 
      38             :   // matrix_free will be an invalid for griffin once the integration is done.
      39             :   // In this PR, we can not change it. It will still be a valid option when users
      40             :   // use non-Newton algorithms
      41       46155 :   params.addParam<bool>(
      42             :       "matrix_free",
      43       30770 :       false,
      44             :       "Whether or not to use a matrix free fashion to form operators. "
      45             :       "If true, shell matrices will be used and meanwhile a preconditioning matrix"
      46             :       "may be formed as well.");
      47             : 
      48       46155 :   params.addParam<bool>(
      49             :       "precond_matrix_free",
      50       30770 :       false,
      51             :       "Whether or not to use a matrix free fashion for forming the preconditioning matrix. "
      52             :       "If true, a shell matrix will be used for preconditioner.");
      53             : 
      54       46155 :   params.addParam<bool>("constant_matrices",
      55       30770 :                         false,
      56             :                         "Whether or not to use constant matrices so that we can use them to form "
      57             :                         "residuals on both linear and "
      58             :                         "nonlinear iterations");
      59             : 
      60       46155 :   params.addParam<bool>("precond_matrix_includes_eigen",
      61       30770 :                         false,
      62             :                         "Whether or not to include eigen kernels in the preconditioning matrix. "
      63             :                         "If true, the preconditioning matrix will include eigen kernels.");
      64             : 
      65       15385 :   params.addPrivateParam<bool>("_use_eigen_value", true);
      66             : 
      67       15385 :   params.addParam<Real>("initial_eigenvalue", 1, "Initial eigenvalue");
      68       15385 :   params.addParam<PostprocessorName>(
      69             :       "normalization", "Postprocessor evaluating norm of eigenvector for normalization");
      70       15385 :   params.addParam<Real>("normal_factor",
      71             :                         "Normalize eigenvector to make a defined norm equal to this factor");
      72             : 
      73       46155 :   params.addParam<bool>("auto_initialization",
      74       30770 :                         true,
      75             :                         "If true, we will set an initial eigen vector in moose, otherwise EPS "
      76             :                         "solver will initial eigen vector");
      77             : 
      78       15385 :   params.addParamNamesToGroup("matrix_free precond_matrix_free constant_matrices "
      79             :                               "precond_matrix_includes_eigen",
      80             :                               "Matrix and Matrix-Free");
      81       15385 :   params.addParamNamesToGroup("initial_eigenvalue auto_initialization",
      82             :                               "Eigenvector and eigenvalue initialization");
      83       15385 :   params.addParamNamesToGroup("normalization normal_factor", "Solution normalization");
      84             : 
      85             :   // If Newton and Inverse Power is combined in SLEPc side
      86       15385 :   params.addPrivateParam<bool>("_newton_inverse_power", false);
      87             : 
      88             : // Add slepc options and eigen problems
      89             : #ifdef LIBMESH_HAVE_SLEPC
      90       15385 :   Moose::SlepcSupport::getSlepcValidParams(params);
      91             : 
      92       15385 :   params += Moose::SlepcSupport::getSlepcEigenProblemValidParams();
      93             : #endif
      94       15385 :   return params;
      95           0 : }
      96             : 
      97         562 : Eigenvalue::Eigenvalue(const InputParameters & parameters)
      98             :   : Executioner(parameters),
      99         562 :     _eigen_problem(*getCheckedPointerParam<EigenProblem *>(
     100             :         "_eigen_problem", "This might happen if you don't have a mesh")),
     101         562 :     _feproblem_solve(*this),
     102         562 :     _normalization(isParamValid("normalization") ? &getPostprocessorValue("normalization")
     103             :                                                  : nullptr),
     104         562 :     _system_time(getParam<Real>("time")),
     105         562 :     _time_step(_eigen_problem.timeStep()),
     106         562 :     _time(_eigen_problem.time()),
     107        1124 :     _final_timer(registerTimedSection("final", 1))
     108             : {
     109             : // Extract and store SLEPc options
     110             : #ifdef LIBMESH_HAVE_SLEPC
     111             :   mooseAssert(_fe_problem.numSolverSystems() == 1,
     112             :               "The Eigenvalue executioner only currently supports a single solver system.");
     113             : 
     114         562 :   Moose::SlepcSupport::storeSolveType(_eigen_problem, parameters);
     115             : 
     116         562 :   Moose::SlepcSupport::setEigenProblemSolverParams(_eigen_problem, parameters);
     117        1116 :   _eigen_problem.setEigenproblemType(
     118         558 :       _eigen_problem.solverParams(/*solver_sys_num=*/0)._eigen_problem_type);
     119             : 
     120             :   // pass two control parameters to eigen problem
     121        1116 :   _eigen_problem.solverParams(/*solver_sys_num=*/0)._free_power_iterations =
     122         558 :       getParam<unsigned int>("free_power_iterations");
     123        1116 :   _eigen_problem.solverParams(/*solver_sys_num=*/0)._extra_power_iterations =
     124         558 :       getParam<unsigned int>("extra_power_iterations");
     125             : 
     126         558 :   if (!isParamValid("normalization") && isParamValid("normal_factor"))
     127           0 :     paramError("normal_factor",
     128             :                "Cannot set scaling factor without defining normalization postprocessor.");
     129             : 
     130         558 :   _fixed_point_solve->setInnerSolve(_feproblem_solve);
     131         558 :   _time = _system_time;
     132             : 
     133         558 :   if (isParamValid("normalization"))
     134             :   {
     135          10 :     const auto & normpp = getParam<PostprocessorName>("normalization");
     136          10 :     if (isParamValid("normal_factor"))
     137          10 :       _eigen_problem.setNormalization(normpp, getParam<Real>("normal_factor"));
     138             :     else
     139           0 :       _eigen_problem.setNormalization(normpp);
     140             :   }
     141             : 
     142         558 :   _eigen_problem.setInitialEigenvalue(getParam<Real>("initial_eigenvalue"));
     143             : 
     144             :   // Set a flag to nonlinear eigen system
     145         558 :   _eigen_problem.getNonlinearEigenSystem(/*nl_sys_num=*/0)
     146         558 :       .precondMatrixIncludesEigenKernels(getParam<bool>("precond_matrix_includes_eigen"));
     147             : #else
     148             :   mooseError("SLEPc is required to use Eigenvalue executioner, please use '--download-slepc in "
     149             :              "PETSc configuration'");
     150             : #endif
     151             :   // SLEPc older than 3.13.0 can not take initial guess from moose
     152             :   // It may generate converge issues
     153             : #if PETSC_RELEASE_LESS_THAN(3, 13, 0)
     154             :   mooseDeprecated(
     155             :       "Please use SLEPc-3.13.0 or higher. Old versions of SLEPc likely produce bad convergence");
     156             : #endif
     157             : 
     158             :   // To avoid petsc unused option warnings, ensure we do not set irrelevant options.
     159         558 :   Moose::PetscSupport::dontAddLinearConvergedReason(_fe_problem);
     160         558 :   Moose::PetscSupport::dontAddNonlinearConvergedReason(_fe_problem);
     161         558 :   Moose::PetscSupport::dontAddPetscFlag("-mat_mffd_type", _fe_problem.getPetscOptions());
     162         558 :   Moose::PetscSupport::dontAddPetscFlag("-st_ksp_atol", _fe_problem.getPetscOptions());
     163         558 :   Moose::PetscSupport::dontAddPetscFlag("-st_ksp_max_it", _fe_problem.getPetscOptions());
     164         558 :   Moose::PetscSupport::dontAddPetscFlag("-st_ksp_rtol", _fe_problem.getPetscOptions());
     165         558 :   if (!_eigen_problem.solverParams()._eigen_matrix_free)
     166          84 :     Moose::PetscSupport::dontAddPetscFlag("-snes_mf_operator", _fe_problem.getPetscOptions());
     167         558 : }
     168             : 
     169             : #ifdef LIBMESH_HAVE_SLEPC
     170             : void
     171         542 : Eigenvalue::init()
     172             : {
     173         542 :   if (isParamValid("normalization"))
     174             :   {
     175          10 :     const auto & normpp = getParam<PostprocessorName>("normalization");
     176          10 :     const auto & exec = _eigen_problem.getUserObject<UserObject>(normpp).getExecuteOnEnum();
     177          10 :     if (!exec.isValueSet(EXEC_LINEAR))
     178           0 :       mooseError("Normalization postprocessor ", normpp, " requires execute_on = 'linear'");
     179             :   }
     180             : 
     181             :   // Does not allow time kernels
     182         542 :   checkIntegrity();
     183             : 
     184             :   // Provide vector of ones to solver
     185             :   // "auto_initialization" is on by default and we init the vector values associated
     186             :   // with eigen-variables as ones. If "auto_initialization" is turned off by users,
     187             :   // it is up to users to provide an initial guess. If "auto_initialization" is off
     188             :   // and users does not provide an initial guess, slepc will automatically generate
     189             :   // a random vector as the initial guess. The motivation to offer this option is
     190             :   // that we have to initialize ONLY eigen variables in multiphysics simulation.
     191             :   // auto_initialization can be overriden by initial conditions.
     192         542 :   if (getParam<bool>("auto_initialization") && !_app.isRestarting())
     193         532 :     _eigen_problem.initEigenvector(1.0);
     194             : 
     195             :   // Some setup
     196         542 :   _eigen_problem.execute(EXEC_PRE_MULTIAPP_SETUP);
     197         542 :   _eigen_problem.initialSetup();
     198             : 
     199             :   // Make sure all PETSc options are setup correctly
     200         542 :   prepareSolverOptions();
     201         542 : }
     202             : 
     203             : void
     204         542 : Eigenvalue::prepareSolverOptions()
     205             : {
     206             : #if PETSC_RELEASE_LESS_THAN(3, 12, 0)
     207             :   // Make sure the SLEPc options are setup for this app
     208             :   Moose::SlepcSupport::slepcSetOptions(
     209             :       _eigen_problem, _eigen_problem.solverParams(/*eigen_sys_num=*/0), _pars);
     210             : #else
     211             :   // Options need to be setup once only
     212         542 :   if (!_eigen_problem.petscOptionsInserted())
     213             :   {
     214             :     // Parent application has the default data base
     215         532 :     if (!_app.isUltimateMaster())
     216          24 :       LibmeshPetscCall(PetscOptionsPush(_eigen_problem.petscOptionsDatabase()));
     217         532 :     Moose::SlepcSupport::slepcSetOptions(
     218         532 :         _eigen_problem, _eigen_problem.solverParams(/*eigen_sys_num=*/0), _pars);
     219         532 :     if (!_app.isUltimateMaster())
     220          24 :       LibmeshPetscCall(PetscOptionsPop());
     221         532 :     _eigen_problem.petscOptionsInserted() = true;
     222             :   }
     223             : #endif
     224         542 : }
     225             : 
     226             : void
     227         542 : Eigenvalue::checkIntegrity()
     228             : {
     229             :   // check to make sure that we don't have any time kernels in eigenvalue simulation
     230         542 :   if (_eigen_problem.getNonlinearSystemBase(/*nl_sys=*/0).containsTimeKernel())
     231           0 :     mooseError("You have specified time kernels in your eigenvalue simulation");
     232         542 : }
     233             : 
     234             : #endif
     235             : 
     236             : void
     237         650 : Eigenvalue::execute()
     238             : {
     239             : #ifdef LIBMESH_HAVE_SLEPC
     240             :   // Recovering makes sense for only transient simulations since the solution from
     241             :   // the previous time steps is required.
     242         650 :   if (_app.isRecovering())
     243             :   {
     244          24 :     _console << "\nCannot recover eigenvalue solves!\nExiting...\n" << std::endl;
     245          24 :     _last_solve_converged = true;
     246          24 :     return;
     247             :   }
     248             : 
     249             :   // Outputs initial conditions set by users
     250             :   // It is consistent with Steady
     251         626 :   _time_step = 0;
     252         626 :   _time = _time_step;
     253         626 :   _eigen_problem.outputStep(EXEC_INITIAL);
     254         626 :   _time = _system_time;
     255             : 
     256         626 :   preExecute();
     257             : 
     258             :   // The following code of this function is copied from "Steady"
     259             :   // "Eigenvalue" implementation can be considered a one-time-step simulation to
     260             :   // have the code compatible with the rest moose world.
     261         626 :   _eigen_problem.advanceState();
     262             : 
     263             :   // First step in any eigenvalue state solve is always 1 (preserving backwards compatibility)
     264         626 :   _time_step = 1;
     265             : 
     266             : #ifdef LIBMESH_ENABLE_AMR
     267             : 
     268             :   // Define the refinement loop
     269         626 :   auto steps = _eigen_problem.adaptivity().getSteps();
     270        1242 :   for (const auto r_step : make_range(steps + 1))
     271             :   {
     272             : #endif // LIBMESH_ENABLE_AMR
     273         626 :     _eigen_problem.timestepSetup();
     274             : 
     275         626 :     _last_solve_converged = _fixed_point_solve->solve();
     276         626 :     if (!lastSolveConverged())
     277             :     {
     278          10 :       _console << "Aborting as solve did not converge" << std::endl;
     279          10 :       break;
     280             :     }
     281             : 
     282             :     // Compute markers and indicators only when we do have at least one adaptivity step
     283         616 :     if (steps)
     284             :     {
     285           0 :       _eigen_problem.computeIndicators();
     286           0 :       _eigen_problem.computeMarkers();
     287             :     }
     288             :     // need to keep _time in sync with _time_step to get correct output
     289         616 :     _time = _time_step;
     290         616 :     _eigen_problem.outputStep(EXEC_TIMESTEP_END);
     291         616 :     _time = _system_time;
     292             : 
     293             : #ifdef LIBMESH_ENABLE_AMR
     294         616 :     if (r_step < steps)
     295             :     {
     296           0 :       _eigen_problem.adaptMesh();
     297             :     }
     298             : 
     299         616 :     _time_step++;
     300             :   }
     301             : #endif
     302             : 
     303             :   {
     304         626 :     TIME_SECTION(_final_timer)
     305         626 :     _eigen_problem.execMultiApps(EXEC_FINAL);
     306         626 :     _eigen_problem.finalizeMultiApps();
     307         626 :     _eigen_problem.postExecute();
     308         626 :     _eigen_problem.execute(EXEC_FINAL);
     309         626 :     _time = _time_step;
     310         626 :     _eigen_problem.outputStep(EXEC_FINAL);
     311         626 :     _time = _system_time;
     312         626 :   }
     313             : 
     314         626 :   postExecute();
     315             : 
     316             : #else
     317             :   mooseError("SLEPc is required for eigenvalue executioner, please use --download-slepc when "
     318             :              "configuring PETSc ");
     319             : #endif
     320             : }

Generated by: LCOV version 1.14