LCOV - code coverage report
Current view: top level - src/reduced_basis - rb_scm_construction.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4232 (290bfc) with base 82cc40 Lines: 152 222 68.5 %
Date: 2025-08-27 15:46:53 Functions: 17 19 89.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // rbOOmit: An implementation of the Certified Reduced Basis method.
       2             : // Copyright (C) 2009, 2010 David J. Knezevic
       3             : 
       4             : // This file is part of rbOOmit.
       5             : 
       6             : // rbOOmit is free software; you can redistribute it and/or
       7             : // modify it under the terms of the GNU Lesser General Public
       8             : // License as published by the Free Software Foundation; either
       9             : // version 2.1 of the License, or (at your option) any later version.
      10             : 
      11             : // rbOOmit is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             : // Lesser General Public License for more details.
      15             : 
      16             : // You should have received a copy of the GNU Lesser General Public
      17             : // License along with this library; if not, write to the Free Software
      18             : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
      19             : 
      20             : // Configuration data
      21             : #include "libmesh/libmesh_config.h"
      22             : 
      23             : // Currently, the RBSCMConstruction should only be available
      24             : // if SLEPc support is enabled.
      25             : #if defined(LIBMESH_HAVE_SLEPC) && (LIBMESH_HAVE_GLPK)
      26             : 
      27             : #include "libmesh/rb_scm_construction.h"
      28             : #include "libmesh/rb_construction.h"
      29             : #include "libmesh/rb_scm_evaluation.h"
      30             : 
      31             : #include "libmesh/libmesh_logging.h"
      32             : #include "libmesh/numeric_vector.h"
      33             : #include "libmesh/sparse_matrix.h"
      34             : #include "libmesh/equation_systems.h"
      35             : #include "libmesh/getpot.h"
      36             : #include "libmesh/dof_map.h"
      37             : #include "libmesh/enum_eigen_solver_type.h"
      38             : 
      39             : // For creating a directory
      40             : #include <sys/types.h>
      41             : #include <sys/stat.h>
      42             : #include <errno.h>
      43             : 
      44             : namespace libMesh
      45             : {
      46             : 
      47           2 : RBSCMConstruction::RBSCMConstruction (EquationSystems & es,
      48             :                                       const std::string & name_in,
      49           2 :                                       const unsigned int number_in)
      50             :   : Parent(es, name_in, number_in),
      51           2 :     SCM_training_tolerance(0.5),
      52           4 :     RB_system_name(""),
      53           2 :     rb_scm_eval(nullptr)
      54             : {
      55             : 
      56             :   // set assemble_before_solve flag to false
      57             :   // so that we control matrix assembly.
      58           2 :   assemble_before_solve = false;
      59             : 
      60             :   // We symmetrize all operators hence use symmetric solvers
      61           2 :   set_eigenproblem_type(GHEP);
      62             : 
      63           2 : }
      64             : 
      65           4 : RBSCMConstruction::~RBSCMConstruction () = default;
      66             : 
      67           0 : void RBSCMConstruction::clear()
      68             : {
      69           0 :   Parent::clear();
      70           0 : }
      71             : 
      72           2 : void RBSCMConstruction::set_rb_scm_evaluation(RBSCMEvaluation & rb_scm_eval_in)
      73             : {
      74           2 :   rb_scm_eval = &rb_scm_eval_in;
      75           2 : }
      76             : 
      77         113 : RBSCMEvaluation & RBSCMConstruction::get_rb_scm_evaluation()
      78             : {
      79         113 :   libmesh_error_msg_if(!rb_scm_eval, "Error: RBSCMEvaluation object hasn't been initialized yet");
      80             : 
      81         113 :   return *rb_scm_eval;
      82             : }
      83             : 
      84         112 : RBThetaExpansion & RBSCMConstruction::get_rb_theta_expansion()
      85             : {
      86         112 :   return get_rb_scm_evaluation().get_rb_theta_expansion();
      87             : }
      88             : 
      89           1 : void RBSCMConstruction::process_parameters_file(const std::string & parameters_filename)
      90             : {
      91             :   // First read in data from parameters_filename
      92           2 :   GetPot infile(parameters_filename);
      93           1 :   const unsigned int n_training_samples = infile("n_training_samples",1);
      94           1 :   const bool deterministic_training     = infile("deterministic_training",false);
      95             : 
      96             :   // Read in training_parameters_random_seed value.  This is used to
      97             :   // seed the RNG when picking the training parameters.  By default the
      98             :   // value is -1, which means use std::time to seed the RNG.
      99           1 :   unsigned int training_parameters_random_seed_in = static_cast<unsigned int>(-1);
     100           1 :   training_parameters_random_seed_in = infile("training_parameters_random_seed",
     101             :                                               training_parameters_random_seed_in);
     102           1 :   set_training_random_seed(static_cast<int>(training_parameters_random_seed_in));
     103             : 
     104             :   // SCM Greedy termination tolerance
     105           1 :   const Real SCM_training_tolerance_in = infile("SCM_training_tolerance", SCM_training_tolerance);
     106           0 :   set_SCM_training_tolerance(SCM_training_tolerance_in);
     107             : 
     108             :   // Initialize the parameter ranges and the parameters themselves
     109           1 :   unsigned int n_continuous_parameters = infile.vector_variable_size("parameter_names");
     110           1 :   RBParameters mu_min_in;
     111           1 :   RBParameters mu_max_in;
     112           4 :   for (unsigned int i=0; i<n_continuous_parameters; i++)
     113             :     {
     114             :       // Read in the parameter names
     115           3 :       std::string param_name = infile("parameter_names", "NONE", i);
     116             : 
     117             :       {
     118           3 :         Real min_val = infile(param_name, 0., 0);
     119           3 :         mu_min_in.set_value(param_name, min_val);
     120             :       }
     121             : 
     122             :       {
     123           3 :         Real max_val = infile(param_name, 0., 1);
     124           3 :         mu_max_in.set_value(param_name, max_val);
     125             :       }
     126             :     }
     127             : 
     128           0 :   std::map<std::string, std::vector<Real>> discrete_parameter_values_in;
     129             : 
     130           1 :   unsigned int n_discrete_parameters = infile.vector_variable_size("discrete_parameter_names");
     131           1 :   for (unsigned int i=0; i<n_discrete_parameters; i++)
     132             :     {
     133           0 :       std::string param_name = infile("discrete_parameter_names", "NONE", i);
     134             : 
     135           0 :       unsigned int n_vals_for_param = infile.vector_variable_size(param_name);
     136           0 :       std::vector<Real> vals_for_param(n_vals_for_param);
     137           0 :       for (unsigned int j=0; j != n_vals_for_param; j++)
     138           0 :         vals_for_param[j] = infile(param_name, 0., j);
     139             : 
     140           0 :       discrete_parameter_values_in[param_name] = vals_for_param;
     141             :     }
     142             : 
     143           1 :   initialize_parameters(mu_min_in, mu_max_in, discrete_parameter_values_in);
     144             : 
     145           0 :   std::map<std::string,bool> log_scaling;
     146           1 :   const RBParameters & mu = get_parameters();
     147           0 :   unsigned int i=0;
     148           4 :   for (const auto & pr : mu)
     149             :     {
     150           3 :       const std::string & param_name = pr.first;
     151           3 :       log_scaling[param_name] = static_cast<bool>(infile("log_scaling", 0, i++));
     152             :     }
     153             : 
     154           1 :   initialize_training_parameters(this->get_parameters_min(),
     155             :                                  this->get_parameters_max(),
     156             :                                  n_training_samples,
     157             :                                  log_scaling,
     158           0 :                                  deterministic_training);   // use deterministic parameters
     159           1 : }
     160             : 
     161           1 : void RBSCMConstruction::print_info()
     162             : {
     163             :   // Print out info that describes the current setup
     164           0 :   libMesh::out << std::endl << "RBSCMConstruction parameters:" << std::endl;
     165           0 :   libMesh::out << "system name: " << this->name() << std::endl;
     166           0 :   libMesh::out << "SCM Greedy tolerance: " << get_SCM_training_tolerance() << std::endl;
     167           1 :   if (rb_scm_eval)
     168             :     {
     169           1 :       libMesh::out << "A_q operators attached: " << get_rb_theta_expansion().get_n_A_terms() << std::endl;
     170             :     }
     171             :   else
     172             :     {
     173           0 :       libMesh::out << "RBThetaExpansion member is not set yet" << std::endl;
     174             :     }
     175           1 :   libMesh::out << "Number of parameters: " << get_n_params() << std::endl;
     176           4 :   for (const auto & pr : get_parameters())
     177             :     {
     178           3 :       const std::string & param_name = pr.first;
     179           0 :       libMesh::out <<   "Parameter " << param_name
     180           3 :                    << ": Min = " << get_parameter_min(param_name)
     181           3 :                    << ", Max = " << get_parameter_max(param_name) << std::endl;
     182             :     }
     183           1 :   print_discrete_parameter_values();
     184           1 :   libMesh::out << "n_training_samples: " << get_n_training_samples() << std::endl;
     185           0 :   libMesh::out << std::endl;
     186           1 : }
     187             : 
     188           0 : void RBSCMConstruction::resize_SCM_vectors()
     189             : {
     190             :   // Clear SCM data vectors
     191           0 :   rb_scm_eval->B_min.clear();
     192           0 :   rb_scm_eval->B_max.clear();
     193           0 :   rb_scm_eval->C_J.clear();
     194           0 :   rb_scm_eval->C_J_stability_vector.clear();
     195           0 :   for (auto & vec : rb_scm_eval->SCM_UB_vectors)
     196           0 :     vec.clear();
     197           0 :   rb_scm_eval->SCM_UB_vectors.clear();
     198             : 
     199             :   // Resize the bounding box vectors
     200           0 :   rb_scm_eval->B_min.resize(get_rb_theta_expansion().get_n_A_terms());
     201           0 :   rb_scm_eval->B_max.resize(get_rb_theta_expansion().get_n_A_terms());
     202           0 : }
     203             : 
     204          45 : void RBSCMConstruction::add_scaled_symm_Aq(unsigned int q_a, Number scalar)
     205             : {
     206           0 :   LOG_SCOPE("add_scaled_symm_Aq()", "RBSCMConstruction");
     207             :   // Load the operators from the RBConstruction
     208           0 :   EquationSystems & es = this->get_equation_systems();
     209          45 :   RBConstruction & rb_system = es.get_system<RBConstruction>(RB_system_name);
     210          45 :   rb_system.add_scaled_Aq(scalar, q_a, &get_matrix_A(), true);
     211          45 : }
     212             : 
     213           1 : void RBSCMConstruction::load_matrix_B()
     214             : {
     215             :   // Load the operators from the RBConstruction
     216           0 :   EquationSystems & es = this->get_equation_systems();
     217           1 :   RBConstruction & rb_system = es.get_system<RBConstruction>(RB_system_name);
     218             : 
     219           1 :   matrix_B->zero();
     220           1 :   matrix_B->close();
     221           1 :   matrix_B->add(1.,*rb_system.get_inner_product_matrix());
     222           1 : }
     223             : 
     224           1 : void RBSCMConstruction::perform_SCM_greedy()
     225             : {
     226           0 :   LOG_SCOPE("perform_SCM_greedy()", "RBSCMConstruction");
     227             : 
     228             :   // initialize rb_scm_eval's parameters
     229           1 :   rb_scm_eval->initialize_parameters(*this);
     230             : 
     231             : #ifdef LIBMESH_ENABLE_CONSTRAINTS
     232             :   // Get a list of constrained dofs from rb_system
     233           0 :   std::set<dof_id_type> constrained_dofs_set;
     234           0 :   EquationSystems & es = this->get_equation_systems();
     235           1 :   RBConstruction & rb_system = es.get_system<RBConstruction>(RB_system_name);
     236             : 
     237         677 :   for (dof_id_type i=0; i<rb_system.n_dofs(); i++)
     238             :     {
     239         650 :       if (rb_system.get_dof_map().is_constrained_dof(i))
     240             :         {
     241          26 :           constrained_dofs_set.insert(i);
     242             :         }
     243             :     }
     244             : 
     245             :   // Use these constrained dofs to identify which dofs we want to "get rid of"
     246             :   // (i.e. condense) in our eigenproblems.
     247           1 :   this->initialize_condensed_dofs(constrained_dofs_set);
     248             : #endif // LIBMESH_ENABLE_CONSTRAINTS
     249             : 
     250             :   // Copy the inner product matrix over from rb_system to be used as matrix_B
     251           1 :   load_matrix_B();
     252             : 
     253           1 :   attach_deflation_space();
     254             : 
     255           1 :   compute_SCM_bounding_box();
     256             :   // This loads the new parameter into current_parameters
     257           1 :   enrich_C_J(0);
     258             : 
     259           0 :   unsigned int SCM_iter=0;
     260             :   while (true)
     261             :     {
     262             :       // matrix_A is reinitialized for the current parameters
     263             :       // on each call to evaluate_stability_constant
     264           7 :       evaluate_stability_constant();
     265             : 
     266           7 :       std::pair<unsigned int,Real> SCM_error_pair = compute_SCM_bounds_on_training_set();
     267             : 
     268           0 :       libMesh::out << "SCM iteration " << SCM_iter
     269           0 :                    << ", max_SCM_error = " << SCM_error_pair.second << std::endl;
     270             : 
     271           7 :       if (SCM_error_pair.second < SCM_training_tolerance)
     272             :         {
     273           0 :           libMesh::out << std::endl << "SCM tolerance of " << SCM_training_tolerance << " reached."
     274           0 :                        << std::endl << std::endl;
     275           1 :           break;
     276             :         }
     277             : 
     278             :       // If we need another SCM iteration, then enrich C_J
     279           6 :       enrich_C_J(SCM_error_pair.first);
     280             : 
     281           0 :       libMesh::out << std::endl << "-----------------------------------" << std::endl << std::endl;
     282             : 
     283           6 :       SCM_iter++;
     284           6 :     }
     285           1 : }
     286             : 
     287           1 : void RBSCMConstruction::compute_SCM_bounding_box()
     288             : {
     289           0 :   LOG_SCOPE("compute_SCM_bounding_box()", "RBSCMConstruction");
     290             : 
     291             :   // Resize the bounding box vectors
     292           1 :   rb_scm_eval->B_min.resize(get_rb_theta_expansion().get_n_A_terms());
     293           1 :   rb_scm_eval->B_max.resize(get_rb_theta_expansion().get_n_A_terms());
     294             : 
     295           4 :   for (unsigned int q=0; q<get_rb_theta_expansion().get_n_A_terms(); q++)
     296             :     {
     297           3 :       matrix_A->zero();
     298           3 :       add_scaled_symm_Aq(q, 1.);
     299             : 
     300             :       // Compute B_min(q)
     301           0 :       eigen_solver->set_position_of_spectrum(SMALLEST_REAL);
     302           3 :       set_eigensolver_properties(q);
     303             : 
     304           3 :       solve();
     305             :       // TODO: Assert convergence for eigensolver
     306             : 
     307           0 :       unsigned int nconv = get_n_converged();
     308           3 :       if (nconv != 0)
     309             :         {
     310           3 :           std::pair<Real, Real> eval = get_eigenpair(0);
     311             : 
     312             :           // ensure that the eigenvalue is real
     313           0 :           libmesh_assert_less (eval.second, TOLERANCE);
     314             : 
     315           3 :           rb_scm_eval->set_B_min(q, eval.first);
     316           3 :           libMesh::out << std::endl << "B_min("<<q<<") = " << rb_scm_eval->get_B_min(q) << std::endl;
     317             :         }
     318             :       else
     319           0 :         libmesh_error_msg("Eigen solver for computing B_min did not converge");
     320             : 
     321             :       // Compute B_max(q)
     322           0 :       eigen_solver->set_position_of_spectrum(LARGEST_REAL);
     323           3 :       set_eigensolver_properties(q);
     324             : 
     325           3 :       solve();
     326             :       // TODO: Assert convergence for eigensolver
     327             : 
     328           0 :       nconv = get_n_converged();
     329           3 :       if (nconv != 0)
     330             :         {
     331           3 :           std::pair<Real, Real> eval = get_eigenpair(0);
     332             : 
     333             :           // ensure that the eigenvalue is real
     334           0 :           libmesh_assert_less (eval.second, TOLERANCE);
     335             : 
     336           3 :           rb_scm_eval->set_B_max(q,eval.first);
     337           3 :           libMesh::out << "B_max("<<q<<") = " << rb_scm_eval->get_B_max(q) << std::endl;
     338             :         }
     339             :       else
     340           0 :         libmesh_error_msg("Eigen solver for computing B_max did not converge");
     341             :     }
     342           1 : }
     343             : 
     344           7 : void RBSCMConstruction::evaluate_stability_constant()
     345             : {
     346           0 :   LOG_SCOPE("evaluate_stability_constant()", "RBSCMConstruction");
     347             : 
     348             :   // Get current index of C_J
     349           7 :   const unsigned int j = cast_int<unsigned int>(rb_scm_eval->C_J.size()-1);
     350             : 
     351           0 :   eigen_solver->set_position_of_spectrum(SMALLEST_REAL);
     352             : 
     353             :   // We assume B is set in system assembly
     354             :   // For coercive problems, B is set to the inner product matrix
     355             :   // For non-coercive time-dependent problems, B is set to the mass matrix
     356             : 
     357             :   // Set matrix A corresponding to mu_star
     358           7 :   matrix_A->zero();
     359          28 :   for (unsigned int q=0; q<get_rb_theta_expansion().get_n_A_terms(); q++)
     360             :     {
     361          21 :       add_scaled_symm_Aq(q, get_rb_theta_expansion().eval_A_theta(q,get_parameters()));
     362             :     }
     363             : 
     364           7 :   set_eigensolver_properties(-1);
     365           7 :   solve();
     366             :   // TODO: Assert convergence for eigensolver
     367             : 
     368           0 :   unsigned int nconv = get_n_converged();
     369           7 :   if (nconv != 0)
     370             :     {
     371           7 :       std::pair<Real, Real> eval = get_eigenpair(0);
     372             : 
     373             :       // ensure that the eigenvalue is real
     374           0 :       libmesh_assert_less (eval.second, TOLERANCE);
     375             : 
     376             :       // Store the coercivity constant corresponding to mu_star
     377           7 :       rb_scm_eval->set_C_J_stability_constraint(j,eval.first);
     378           0 :       libMesh::out << std::endl << "Stability constant for C_J("<<j<<") = "
     379           7 :                    << rb_scm_eval->get_C_J_stability_constraint(j) << std::endl << std::endl;
     380             : 
     381             :       // Compute and store the vector y = (y_1, \ldots, y_Q) for the
     382             :       // eigenvector currently stored in eigen_system.solution.
     383             :       // We use this later to compute the SCM upper bounds.
     384           7 :       Real norm_B2 = libmesh_real( B_inner_product(*solution, *solution) );
     385             : 
     386          28 :       for (unsigned int q=0; q<get_rb_theta_expansion().get_n_A_terms(); q++)
     387             :         {
     388          21 :           Real norm_Aq2 = libmesh_real( Aq_inner_product(q, *solution, *solution) );
     389             : 
     390          21 :           rb_scm_eval->set_SCM_UB_vector(j,q,norm_Aq2/norm_B2);
     391             :         }
     392             :     }
     393             :   else
     394           0 :     libmesh_error_msg("Error: Eigensolver did not converge in evaluate_stability_constant");
     395           7 : }
     396             : 
     397           7 : Number RBSCMConstruction::B_inner_product(const NumericVector<Number> & v,
     398             :                                           const NumericVector<Number> & w) const
     399             : {
     400           7 :   matrix_B->vector_mult(*inner_product_storage_vector, w);
     401             : 
     402           7 :   return v.dot(*inner_product_storage_vector);
     403             : }
     404             : 
     405          21 : Number RBSCMConstruction::Aq_inner_product(unsigned int q,
     406             :                                            const NumericVector<Number> & v,
     407             :                                            const NumericVector<Number> & w)
     408             : {
     409          21 :   libmesh_error_msg_if(q >= get_rb_theta_expansion().get_n_A_terms(),
     410             :                        "Error: We must have q < Q_a in Aq_inner_product.");
     411             : 
     412          21 :   matrix_A->zero();
     413          21 :   add_scaled_symm_Aq(q, 1.);
     414          21 :   matrix_A->vector_mult(*inner_product_storage_vector, w);
     415             : 
     416          21 :   return v.dot(*inner_product_storage_vector);
     417             : }
     418             : 
     419           7 : std::pair<unsigned int,Real> RBSCMConstruction::compute_SCM_bounds_on_training_set()
     420             : {
     421           0 :   LOG_SCOPE("compute_SCM_bounds_on_training_set()", "RBSCMConstruction");
     422             : 
     423             :   // Now compute the maximum bound error over training_parameters
     424           0 :   unsigned int new_C_J_index = 0;
     425           0 :   Real max_SCM_error = 0.;
     426             : 
     427           7 :   numeric_index_type first_index = get_first_local_training_index();
     428         707 :   for (unsigned int i=0; i<get_local_n_training_samples(); i++)
     429             :     {
     430         700 :       set_params_from_training_set(first_index+i);
     431         700 :       rb_scm_eval->set_parameters( get_parameters() );
     432         700 :       Real LB = rb_scm_eval->get_SCM_LB();
     433         700 :       Real UB = rb_scm_eval->get_SCM_UB();
     434             : 
     435         700 :       Real error_i = SCM_greedy_error_indicator(LB, UB);
     436             : 
     437         700 :       if (error_i > max_SCM_error)
     438             :         {
     439           0 :           max_SCM_error = error_i;
     440           0 :           new_C_J_index = i;
     441             :         }
     442             :     }
     443             : 
     444           7 :   numeric_index_type global_index = first_index + new_C_J_index;
     445           0 :   std::pair<numeric_index_type,Real> error_pair(global_index, max_SCM_error);
     446           7 :   get_global_max_error_pair(this->comm(),error_pair);
     447             : 
     448           7 :   return error_pair;
     449             : }
     450             : 
     451           7 : void RBSCMConstruction::enrich_C_J(unsigned int new_C_J_index)
     452             : {
     453           0 :   LOG_SCOPE("enrich_C_J()", "RBSCMConstruction");
     454             : 
     455           7 :   set_params_from_training_set_and_broadcast(new_C_J_index);
     456             : 
     457           7 :   rb_scm_eval->C_J.push_back(get_parameters());
     458             : 
     459           0 :   libMesh::out << std::endl << "SCM: Added mu = (";
     460             : 
     461           0 :   bool first = true;
     462          28 :   for (const auto & pr : get_parameters())
     463             :     {
     464          21 :       if (!first)
     465           0 :         libMesh::out << ",";
     466          21 :       const std::string & param_name = pr.first;
     467          21 :       RBParameters C_J_params = rb_scm_eval->C_J[rb_scm_eval->C_J.size()-1];
     468          21 :       libMesh::out << C_J_params.get_value(param_name);
     469           0 :       first = false;
     470          21 :     }
     471           0 :   libMesh::out << ")" << std::endl;
     472             : 
     473             :   // Finally, resize C_J_stability_vector and SCM_UB_vectors
     474           7 :   rb_scm_eval->C_J_stability_vector.push_back(0.);
     475             : 
     476           7 :   std::vector<Real> zero_vector(get_rb_theta_expansion().get_n_A_terms());
     477           7 :   rb_scm_eval->SCM_UB_vectors.push_back(zero_vector);
     478           7 : }
     479             : 
     480             : 
     481             : } // namespace libMesh
     482             : 
     483             : #endif // LIBMESH_HAVE_SLEPC && LIBMESH_HAVE_GLPK

Generated by: LCOV version 1.14