LCOV - code coverage report
Current view: top level - src/systems - condensed_eigen_system.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 109 158 69.0 %
Date: 2025-08-19 19:27:09 Functions: 15 20 75.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             : #include "libmesh/libmesh_config.h"
      19             : 
      20             : // Currently, the EigenSystem should only be available
      21             : // if SLEPc support is enabled.
      22             : #if defined(LIBMESH_HAVE_SLEPC)
      23             : 
      24             : #include "libmesh/condensed_eigen_system.h"
      25             : 
      26             : #include "libmesh/dof_map.h"
      27             : #include "libmesh/equation_systems.h"
      28             : #include "libmesh/int_range.h"
      29             : #include "libmesh/libmesh_logging.h"
      30             : #include "libmesh/numeric_vector.h"
      31             : #include "libmesh/parallel.h"
      32             : 
      33             : namespace libMesh
      34             : {
      35             : 
      36         208 : CondensedEigenSystem::CondensedEigenSystem (EquationSystems & es,
      37             :                                             const std::string & name_in,
      38         208 :                                             const unsigned int number_in)
      39             :   : Parent(es, name_in, number_in),
      40         200 :     _condensed_dofs_initialized(false),
      41         200 :     _have_condensed_dofs(false),
      42         208 :     _create_submatrices_in_solve(true)
      43             : {
      44         208 : }
      45             : 
      46         598 : CondensedEigenSystem::~CondensedEigenSystem() = default;
      47             : 
      48             : void
      49         207 : CondensedEigenSystem::initialize_condensed_dofs(const std::set<dof_id_type> & global_dirichlet_dofs_set)
      50             : {
      51           4 :   const DofMap & dof_map = this->get_dof_map();
      52         207 :   if (global_dirichlet_dofs_set.empty() && !dof_map.n_constrained_dofs())
      53             :     {
      54           0 :       _have_condensed_dofs = false;
      55           0 :       _condensed_dofs_initialized = true;
      56           0 :       return;
      57             :     }
      58             : 
      59             :   // First, put all unconstrained local dofs into non_dirichlet_dofs_set
      60           4 :   std::set<dof_id_type> local_non_condensed_dofs_set;
      61       36328 :   for (auto i : make_range(dof_map.first_dof(), dof_map.end_dof()))
      62             : #if LIBMESH_ENABLE_CONSTRAINTS
      63        5550 :     if (!dof_map.is_constrained_dof(i))
      64             : #endif
      65       30235 :       local_non_condensed_dofs_set.insert(i);
      66             : 
      67             :   // Now erase the condensed dofs
      68         233 :   for (const auto dof : global_dirichlet_dofs_set)
      69          26 :     if ((dof_map.first_dof() <= dof) && (dof < dof_map.end_dof()))
      70           0 :       local_non_condensed_dofs_set.erase(dof);
      71             : 
      72             :   // Finally, move local_non_condensed_dofs_set over to a vector for convenience in solve()
      73         207 :   this->local_non_condensed_dofs_vector.clear();
      74             : 
      75       32426 :   for (const auto dof : local_non_condensed_dofs_set)
      76       32219 :     this->local_non_condensed_dofs_vector.push_back(dof);
      77             : 
      78         207 :   _have_condensed_dofs = true;
      79         207 :   _condensed_dofs_initialized = true;
      80             : }
      81             : 
      82             : void
      83           0 : CondensedEigenSystem::reinit()
      84             : {
      85           0 :   Parent::reinit();
      86           0 :   _condensed_dofs_initialized = false;
      87           0 : }
      88             : 
      89             : void
      90          66 : CondensedEigenSystem::initialize_condensed_matrices()
      91             : {
      92          66 :   if (!_condensed_dofs_initialized)
      93         132 :     this->initialize_condensed_dofs();
      94             : 
      95          66 :   if (!_have_condensed_dofs)
      96           0 :     return;
      97             : 
      98           0 :   const auto m = cast_int<numeric_index_type>(this->local_non_condensed_dofs_vector.size());
      99          66 :   auto M = m;
     100          66 :   _communicator.sum(M);
     101          66 :   if (this->has_condensed_matrix_A())
     102             :   {
     103           0 :     this->get_condensed_matrix_A().init(M, M, m, m);
     104             :     // A bit ludicrously MatCopy requires the matrix being copied to to be assembled
     105           0 :     this->get_condensed_matrix_A().close();
     106             :   }
     107          66 :   if (this->has_condensed_matrix_B())
     108             :   {
     109           0 :     this->get_condensed_matrix_B().init(M, M, m, m);
     110           0 :     this->get_condensed_matrix_B().close();
     111             :   }
     112          66 :   if (this->has_condensed_precond_matrix())
     113             :   {
     114          66 :     this->get_condensed_precond_matrix().init(M, M, m, m);
     115          66 :     this->get_condensed_precond_matrix().close();
     116             :   }
     117             : }
     118             : 
     119           0 : dof_id_type CondensedEigenSystem::n_global_non_condensed_dofs() const
     120             : {
     121           0 :   if (!_have_condensed_dofs)
     122           0 :     return this->n_dofs();
     123             :   else
     124             :     {
     125             :       dof_id_type n_global_non_condensed_dofs =
     126           0 :         cast_int<dof_id_type>(local_non_condensed_dofs_vector.size());
     127           0 :       this->comm().sum(n_global_non_condensed_dofs);
     128             : 
     129           0 :       return n_global_non_condensed_dofs;
     130             :     }
     131             : }
     132             : 
     133             : #ifdef LIBMESH_ENABLE_DEPRECATED
     134             : void
     135         208 : CondensedEigenSystem::set_raw_pointers()
     136             : {
     137         208 :   condensed_matrix_A = _condensed_matrix_A.get();
     138         208 :   condensed_matrix_B = _condensed_matrix_B.get();
     139         208 : }
     140             : #endif
     141             : 
     142             : void
     143           0 : CondensedEigenSystem::clear()
     144             : {
     145           0 :   Parent::clear();
     146           0 :   _condensed_matrix_A = nullptr;
     147           0 :   _condensed_matrix_B = nullptr;
     148           0 :   _condensed_precond_matrix = nullptr;
     149             : #ifdef LIBMESH_ENABLE_DEPRECATED
     150           0 :   set_raw_pointers();
     151             : #endif
     152           0 : }
     153             : 
     154             : void
     155         208 : CondensedEigenSystem::add_matrices()
     156             : {
     157         208 :   Parent::add_matrices();
     158             : 
     159         208 :   if (!this->use_shell_matrices())
     160             :   {
     161         142 :     if (!_condensed_matrix_A)
     162         280 :       _condensed_matrix_A = SparseMatrix<Number>::build(this->comm());
     163         142 :     if (!_condensed_matrix_B)
     164         280 :       _condensed_matrix_B = SparseMatrix<Number>::build(this->comm());
     165             : 
     166             :     // When not using shell matrices we use A for P as well so we don't need to add P
     167             :   }
     168             :   // we *are* using shell matrices but we might not be using a shell matrix for P
     169          66 :   else if (!this->use_shell_precond_matrix())
     170             :   {
     171          66 :     if (!_condensed_precond_matrix)
     172         132 :       _condensed_precond_matrix = SparseMatrix<Number>::build(this->comm());
     173             :   }
     174             : 
     175             : #ifdef LIBMESH_ENABLE_DEPRECATED
     176         208 :   set_raw_pointers();
     177             : #endif
     178         208 : }
     179             : 
     180         219 : void CondensedEigenSystem::solve()
     181             : {
     182           4 :   LOG_SCOPE("solve()", "CondensedEigenSystem");
     183             : 
     184             :   // If we haven't initialized any condensed dofs,
     185             :   // just use the default eigen_system
     186         219 :   if (!_have_condensed_dofs)
     187             :     {
     188           0 :       Parent::solve();
     189           0 :       return;
     190             :     }
     191             : 
     192             :   // check that necessary parameters have been set
     193           4 :   libmesh_assert(
     194             :       this->get_equation_systems().parameters.have_parameter<unsigned int>("eigenpairs"));
     195           4 :   libmesh_assert(
     196             :       this->get_equation_systems().parameters.have_parameter<unsigned int>("basis vectors"));
     197             : 
     198         219 :   if (this->assemble_before_solve)
     199             :     {
     200             :       // Assemble the linear system
     201         140 :       this->assemble();
     202             : 
     203             :       // And close the assembled matrices; using a non-closed matrix
     204             :       // with create_submatrix() is deprecated.
     205         140 :       if (matrix_A)
     206         140 :         matrix_A->close();
     207         140 :       if (generalized() && matrix_B)
     208         140 :         matrix_B->close();
     209         140 :       if (precond_matrix)
     210           0 :         precond_matrix->close();
     211             :     }
     212             : 
     213             :   // If we reach here, then there should be some non-condensed dofs
     214           4 :   libmesh_assert(!local_non_condensed_dofs_vector.empty());
     215             : 
     216         219 :   if (_create_submatrices_in_solve)
     217             :     {
     218         153 :       if (matrix_A)
     219         153 :         matrix_A->create_submatrix(
     220         153 :             *_condensed_matrix_A, local_non_condensed_dofs_vector, local_non_condensed_dofs_vector);
     221         153 :       if (generalized() && matrix_B)
     222         153 :         matrix_B->create_submatrix(
     223         153 :             *_condensed_matrix_B, local_non_condensed_dofs_vector, local_non_condensed_dofs_vector);
     224         153 :       if (precond_matrix)
     225           0 :         precond_matrix->create_submatrix(*_condensed_precond_matrix,
     226           0 :                                          local_non_condensed_dofs_vector,
     227           0 :                                          local_non_condensed_dofs_vector);
     228             :     }
     229             : 
     230         223 :   solve_helper(
     231             :       _condensed_matrix_A.get(), _condensed_matrix_B.get(), _condensed_precond_matrix.get());
     232             : }
     233             : 
     234         779 : std::pair<Real, Real> CondensedEigenSystem::get_eigenpair(dof_id_type i)
     235             : {
     236          40 :   LOG_SCOPE("get_eigenpair()", "CondensedEigenSystem");
     237             : 
     238             :   // If we haven't initialized any condensed dofs,
     239             :   // just use the default eigen_system
     240         779 :   if (!_have_condensed_dofs)
     241           0 :     return Parent::get_eigenpair(i);
     242             : 
     243             :   // If we reach here, then there should be some non-condensed dofs
     244          20 :   libmesh_assert(!local_non_condensed_dofs_vector.empty());
     245             : 
     246             :   // This function assumes that condensed_solve has just been called.
     247             :   // If this is not the case, then we will trip an asset in get_eigenpair
     248         779 :   std::unique_ptr<NumericVector<Number>> temp = NumericVector<Number>::build(this->comm());
     249             :   const dof_id_type n_local =
     250          40 :     cast_int<dof_id_type>(local_non_condensed_dofs_vector.size());
     251         779 :   dof_id_type n       = n_local;
     252         779 :   this->comm().sum(n);
     253             : 
     254         779 :   temp->init (n, n_local, false, PARALLEL);
     255             : 
     256         799 :   std::pair<Real, Real> eval = eigen_solver->get_eigenpair (i, *temp);
     257             : 
     258             :   // Now map temp to solution. Loop over local entries of local_non_condensed_dofs_vector
     259          20 :   libmesh_assert(this->comm().verify(this->solution->closed()));
     260         779 :   if (!this->solution->closed())
     261           0 :     this->solution->close();
     262         779 :   this->solution->zero();
     263      129238 :   for (auto j : make_range(n_local))
     264             :     {
     265      138509 :       const dof_id_type index = local_non_condensed_dofs_vector[j];
     266      138509 :       solution->set(index,(*temp)(temp->first_local_index()+j));
     267             :     }
     268             : 
     269             :   // Enforcing constraints requires creating a ghosted version of the solution, which requires the
     270             :   // solution be assembled
     271         779 :   solution->close();
     272         779 :   get_dof_map().enforce_constraints_exactly(*this);
     273         779 :   this->update();
     274             : 
     275         779 :   return eval;
     276         739 : }
     277             : 
     278             : 
     279             : 
     280           0 : SparseMatrix<Number> & CondensedEigenSystem::get_condensed_matrix_A()
     281             : {
     282           0 :   libmesh_assert(_condensed_matrix_A);
     283           0 :   return *_condensed_matrix_A;
     284             : }
     285             : 
     286           0 : SparseMatrix<Number> & CondensedEigenSystem::get_condensed_matrix_B()
     287             : {
     288           0 :   libmesh_assert(_condensed_matrix_B);
     289           0 :   return *_condensed_matrix_B;
     290             : }
     291             : 
     292         132 : SparseMatrix<Number> & CondensedEigenSystem::get_condensed_precond_matrix()
     293             : {
     294           0 :   libmesh_assert(_condensed_precond_matrix);
     295         132 :   return *_condensed_precond_matrix;
     296             : }
     297             : 
     298             : void
     299        3762 : CondensedEigenSystem::copy_sub_to_super(const NumericVector<Number> & sub,
     300             :                                         NumericVector<Number> & super)
     301             : {
     302           0 :   libmesh_assert_equal_to(sub.local_size(), local_non_condensed_dofs_vector.size());
     303           0 :   libmesh_assert_equal_to(sub.local_size() + this->get_dof_map().n_local_constrained_dofs(),
     304             :                           super.local_size());
     305        3762 :   auto super_sub_view = super.get_subvector(local_non_condensed_dofs_vector);
     306        3762 :   *super_sub_view = sub;
     307        3762 :   super.restore_subvector(std::move(super_sub_view), local_non_condensed_dofs_vector);
     308        3762 : }
     309             : 
     310             : void
     311        3498 : CondensedEigenSystem::copy_super_to_sub(NumericVector<Number> & super,
     312             :                                         NumericVector<Number> & sub)
     313             : {
     314           0 :   libmesh_assert_equal_to(sub.local_size(), local_non_condensed_dofs_vector.size());
     315           0 :   libmesh_assert_equal_to(sub.local_size() + this->get_dof_map().n_local_constrained_dofs(),
     316             :                           super.local_size());
     317        3498 :   auto super_sub_view = super.get_subvector(local_non_condensed_dofs_vector);
     318        3498 :   sub = *super_sub_view;
     319        3498 :   super.restore_subvector(std::move(super_sub_view), local_non_condensed_dofs_vector);
     320        3498 : }
     321             : 
     322             : void
     323         264 : CondensedEigenSystem::copy_super_to_sub(const SparseMatrix<Number> & super,
     324             :                                         SparseMatrix<Number> & sub)
     325             : {
     326           0 :   parallel_object_only();
     327             : 
     328           0 :   libmesh_assert_equal_to(sub.local_m(), local_non_condensed_dofs_vector.size());
     329           0 :   libmesh_assert_equal_to(sub.local_m() + this->get_dof_map().n_local_constrained_dofs(),
     330             :                           super.local_m());
     331         264 :   auto super_sub_view = SparseMatrix<Number>::build(super.comm());
     332         264 :   super.create_submatrix(
     333         264 :       *super_sub_view, local_non_condensed_dofs_vector, local_non_condensed_dofs_vector);
     334         264 :   sub = *super_sub_view;
     335         264 : }
     336             : 
     337             : } // namespace libMesh
     338             : 
     339             : 
     340             : #endif // LIBMESH_HAVE_SLEPC

Generated by: LCOV version 1.14