LCOV - code coverage report
Current view: top level - src/timeintegrators - ExplicitMixedOrder.C (source / functions) Hit Total Coverage
Test: idaholab/moose solid_mechanics: f45d79 Lines: 135 137 98.5 %
Date: 2025-07-25 05:00:39 Functions: 10 10 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 "Assembly.h"
      12             : #include "ExplicitMixedOrder.h"
      13             : #include "ExplicitTimeIntegrator.h"
      14             : #include "Moose.h"
      15             : #include "MooseError.h"
      16             : #include "MooseTypes.h"
      17             : #include "MooseVariableFieldBase.h"
      18             : #include "NonlinearSystem.h"
      19             : #include "FEProblem.h"
      20             : #include "TimeStepper.h"
      21             : #include "TransientBase.h"
      22             : 
      23             : // libMesh includes
      24             : #include "TransientBase.h"
      25             : #include "libmesh/id_types.h"
      26             : #include "libmesh/nonlinear_solver.h"
      27             : #include "libmesh/sparse_matrix.h"
      28             : #include "DirichletBCBase.h"
      29             : #include "libmesh/vector_value.h"
      30             : #include <algorithm>
      31             : #include <iterator>
      32             : #include <utility>
      33             : 
      34             : registerMooseObject("SolidMechanicsApp", ExplicitMixedOrder);
      35             : registerMooseObjectRenamed("SolidMechanicsApp",
      36             :                            DirectCentralDifference,
      37             :                            "10/14/2025 00:00",
      38             :                            ExplicitMixedOrder);
      39             : 
      40             : InputParameters
      41         102 : ExplicitMixedOrder::validParams()
      42             : {
      43         102 :   InputParameters params = ExplicitTimeIntegrator::validParams();
      44             : 
      45         102 :   params.addClassDescription(
      46             :       "Implementation of explicit time integration without invoking any of the nonlinear solver.");
      47             : 
      48         204 :   params.addParam<bool>("use_constant_mass",
      49         204 :                         false,
      50             :                         "If set to true, will only compute the mass matrix in the first time step, "
      51             :                         "and keep using it throughout the simulation.");
      52         204 :   params.addParam<TagName>("mass_matrix_tag", "mass", "The tag for the mass matrix");
      53             : 
      54         204 :   params.addParam<std::vector<VariableName>>(
      55             :       "second_order_vars",
      56             :       {},
      57             :       "A subset of variables that require second-order integration (velocity and acceleration) to "
      58             :       "be applied by this time integrator.");
      59             : 
      60         204 :   params.addParam<std::vector<VariableName>>(
      61             :       "first_order_vars",
      62             :       {},
      63             :       "A subset of variables that require first-order integration (velocity only) to be applied by "
      64             :       "this time integrator.");
      65             : 
      66             :   // Prevent users from using variables option by accident.
      67         102 :   params.suppressParameter<std::vector<VariableName>>("variables");
      68             : 
      69         204 :   MooseEnum solve_type("consistent lumped lump_preconditioned", "lumped");
      70         102 :   params.setParameters("solve_type", solve_type);
      71         102 :   params.ignoreParameter<MooseEnum>("solve_type");
      72         102 :   return params;
      73         102 : }
      74             : 
      75          68 : ExplicitMixedOrder::ExplicitMixedOrder(const InputParameters & parameters)
      76             :   : ExplicitTimeIntegrator(parameters),
      77          68 :     _constant_mass(getParam<bool>("use_constant_mass")),
      78         136 :     _mass_matrix(getParam<TagName>("mass_matrix_tag")),
      79          68 :     _solution_older(_sys.solutionState(2)),
      80         136 :     _vars_first(declareRestartableData<std::unordered_set<unsigned int>>("first_order_vars")),
      81          68 :     _local_first_order_indices(
      82          68 :         declareRestartableData<std::vector<dof_id_type>>("first_local_indices")),
      83         136 :     _vars_second(declareRestartableData<std::unordered_set<unsigned int>>("second_order_vars")),
      84          68 :     _local_second_order_indices(
      85         136 :         declareRestartableData<std::vector<dof_id_type>>("second_local_indices"))
      86             : {
      87          68 :   _fe_problem.setUDotRequested(true);
      88          68 :   _fe_problem.setUDotOldRequested(true);
      89          68 :   _fe_problem.setUDotDotRequested(true);
      90          68 : }
      91             : 
      92             : void
      93        1950 : ExplicitMixedOrder::computeTimeDerivatives()
      94             : {
      95             :   /*
      96             :   Because this is called in NonLinearSystemBase
      97             :   this should not actually compute the time derivatives.
      98             :   Calculating time derivatives here will cause issues for the
      99             :   solution update.
     100             :   */
     101        1950 :   return;
     102             : }
     103             : 
     104             : TagID
     105         382 : ExplicitMixedOrder::massMatrixTagID() const
     106             : {
     107         382 :   return _sys.subproblem().getMatrixTagID(_mass_matrix);
     108             : }
     109             : 
     110             : void
     111         348 : ExplicitMixedOrder::solve()
     112             : {
     113             :   // Getting the tagID for the mass matrix
     114         348 :   auto mass_tag = massMatrixTagID();
     115             : 
     116             :   // Reset iteration counts
     117         348 :   _n_nonlinear_iterations = 0;
     118         348 :   _n_linear_iterations = 0;
     119             : 
     120         348 :   _current_time = _fe_problem.time();
     121             : 
     122         348 :   auto & mass_matrix = _nonlinear_implicit_system->get_system_matrix();
     123             : 
     124             :   // Compute the mass matrix
     125         348 :   if (!_constant_mass || _t_step == 1)
     126             :   {
     127             :     // We only want to compute "inverted" lumped mass matrix once.
     128         120 :     _fe_problem.computeJacobianTag(
     129         120 :         *_nonlinear_implicit_system->current_local_solution, mass_matrix, mass_tag);
     130             : 
     131             :     // Calculating the lumped mass matrix for use in residual calculation
     132         120 :     mass_matrix.vector_mult(*_mass_matrix_diag_inverted, *_ones);
     133             : 
     134             :     // "Invert" the diagonal mass matrix
     135         120 :     _mass_matrix_diag_inverted->reciprocal();
     136         120 :     _mass_matrix_diag_inverted->close();
     137             :   }
     138             : 
     139             :   // Set time to the time at which to evaluate the residual
     140         348 :   _fe_problem.time() = _fe_problem.timeOld();
     141         348 :   _nonlinear_implicit_system->update();
     142             : 
     143             :   // Compute the residual
     144         348 :   _explicit_residual->zero();
     145         348 :   _fe_problem.computeResidual(
     146         348 :       *_nonlinear_implicit_system->current_local_solution, *_explicit_residual, _nl->number());
     147             : 
     148             :   // Move the residual to the RHS
     149         348 :   *_explicit_residual *= -1.0;
     150             : 
     151             :   // Perform the linear solve
     152         348 :   bool converged = performExplicitSolve(mass_matrix);
     153         348 :   _nl->overwriteNodeFace(*_nonlinear_implicit_system->solution);
     154             : 
     155             :   // Update the solution
     156         348 :   *_nonlinear_implicit_system->solution = _nl->solutionOld();
     157         348 :   *_nonlinear_implicit_system->solution += *_solution_update;
     158             : 
     159         348 :   _nonlinear_implicit_system->update();
     160             : 
     161         348 :   _nl->setSolution(*_nonlinear_implicit_system->current_local_solution);
     162         348 :   _nonlinear_implicit_system->nonlinear_solver->converged = converged;
     163         348 : }
     164             : 
     165             : void
     166         348 : ExplicitMixedOrder::postResidual(NumericVector<Number> & residual)
     167             : {
     168         348 :   residual += *_Re_time;
     169         348 :   residual += *_Re_non_time;
     170         348 :   residual.close();
     171             : 
     172             :   // Reset time to the time at which to evaluate nodal BCs, which comes next
     173         348 :   _fe_problem.time() = _current_time;
     174         348 : }
     175             : 
     176             : bool
     177         348 : ExplicitMixedOrder::performExplicitSolve(SparseMatrix<Number> &)
     178             : {
     179             : 
     180             :   bool converged = false;
     181             : 
     182             :   // Grab all the vectors that we will need
     183         348 :   auto accel = _sys.solutionUDotDot();
     184         348 :   auto vel = _sys.solutionUDot();
     185             : 
     186             :   // Compute Forward Euler
     187             :   // Split diag mass and residual vectors into correct subvectors
     188             :   const std::unique_ptr<NumericVector<Number>> mass_first(
     189         348 :       NumericVector<Number>::build(_communicator));
     190             :   const std::unique_ptr<NumericVector<Real>> exp_res_first(
     191         348 :       NumericVector<Number>::build(_communicator));
     192         348 :   _mass_matrix_diag_inverted->create_subvector(*mass_first, _local_first_order_indices, false);
     193         348 :   _explicit_residual->create_subvector(*exp_res_first, _local_first_order_indices, false);
     194             : 
     195             :   // Need velocity vector split into subvectors
     196         348 :   auto vel_first = vel->get_subvector(_local_first_order_indices);
     197             : 
     198             :   // Velocity update for foward euler
     199         348 :   vel_first->pointwise_mult(*mass_first, *exp_res_first);
     200             : 
     201             :   // Restore the velocities
     202         348 :   vel->restore_subvector(std::move(vel_first), _local_first_order_indices);
     203             : 
     204             :   // Compute Central Difference
     205             :   // Split diag mass and residual vectors into correct subvectors
     206             :   const std::unique_ptr<NumericVector<Real>> mass_second(
     207         348 :       NumericVector<Number>::build(_communicator));
     208             :   const std::unique_ptr<NumericVector<Real>> exp_res_second(
     209         348 :       NumericVector<Number>::build(_communicator));
     210         348 :   _mass_matrix_diag_inverted->create_subvector(*mass_second, _local_second_order_indices, false);
     211         348 :   _explicit_residual->create_subvector(*exp_res_second, _local_second_order_indices, false);
     212             : 
     213             :   // Only need acceleration and old velocity vector for central difference
     214         348 :   auto accel_second = accel->get_subvector(_local_second_order_indices);
     215             : 
     216         348 :   auto vel_second = vel->get_subvector(_local_second_order_indices);
     217             : 
     218             :   // Compute acceleration for central difference
     219         348 :   accel_second->pointwise_mult(*mass_second, *exp_res_second);
     220             : 
     221             :   // Scaling the acceleration
     222         348 :   auto accel_scaled = accel_second->clone();
     223         348 :   accel_scaled->scale((_dt + _dt_old) / 2);
     224             : 
     225             :   // Velocity update for central difference
     226         348 :   *vel_second += *accel_scaled;
     227             : 
     228             :   // Restore acceleration
     229         348 :   accel->restore_subvector(std::move(accel_second), _local_second_order_indices);
     230             : 
     231         348 :   vel->restore_subvector(std::move(vel_second), _local_second_order_indices);
     232             : 
     233             :   // Same solution update for both methods
     234         348 :   *_solution_update = *vel;
     235         348 :   _solution_update->scale(_dt);
     236             : 
     237             :   // Check for convergence by seeing if there is a nan or inf
     238         348 :   auto sum = _solution_update->sum();
     239             :   converged = std::isfinite(sum);
     240             : 
     241             :   // The linear iteration count remains zero
     242         348 :   _n_linear_iterations = 0;
     243         348 :   vel->close();
     244         348 :   accel->close();
     245             : 
     246         348 :   return converged;
     247         348 : }
     248             : 
     249             : void
     250          34 : ExplicitMixedOrder::init()
     251             : {
     252          34 :   ExplicitTimeIntegrator::init();
     253             : 
     254             :   // Compute ICs for velocity
     255          34 :   computeICs();
     256             : 
     257             :   // Seperate variables into first and second time integration order and find
     258             :   // the local indices for each
     259          34 :   const auto & var_names_first = getParam<std::vector<VariableName>>("first_order_vars");
     260          68 :   const auto & var_names_second = getParam<std::vector<VariableName>>("second_order_vars");
     261             :   std::vector<unsigned int> var_num_vec;
     262             : 
     263          34 :   auto & lm_sys = _sys.system();
     264          34 :   lm_sys.get_all_variable_numbers(var_num_vec);
     265          34 :   std::unordered_set<unsigned int> var_nums(var_num_vec.begin(), var_num_vec.end());
     266             : 
     267          42 :   for (const auto & var_name : var_names_first)
     268           8 :     if (lm_sys.has_variable(var_name))
     269             :     {
     270           8 :       const auto var_num = lm_sys.variable_number(var_name);
     271           8 :       _vars_first.insert(var_num);
     272             :       var_nums.erase(var_num);
     273             :     }
     274             : 
     275          84 :   for (const auto & var_name : var_names_second)
     276          50 :     if (lm_sys.has_variable(var_name))
     277             :     {
     278          50 :       const auto var_num = lm_sys.variable_number(var_name);
     279          50 :       _vars_second.insert(var_num);
     280             :       var_nums.erase(var_num);
     281             :     }
     282             : 
     283             :   // If var_nums is empty then that means the user has specified all the variables in this system
     284          34 :   if (!var_nums.empty())
     285           4 :     mooseError("Not all nonlinear variables have their order specified.");
     286             : 
     287             :   std::vector<dof_id_type> var_dof_indices, work_vec;
     288          36 :   for (const auto var_num : _vars_first)
     289             :   {
     290           6 :     work_vec = _local_first_order_indices;
     291           6 :     _local_first_order_indices.clear();
     292           6 :     lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
     293           6 :     std::merge(work_vec.begin(),
     294             :                work_vec.end(),
     295             :                var_dof_indices.begin(),
     296             :                var_dof_indices.end(),
     297             :                std::back_inserter(_local_first_order_indices));
     298             :   }
     299             : 
     300             :   work_vec.clear();
     301             :   var_dof_indices.clear();
     302             : 
     303          78 :   for (const auto var_num : _vars_second)
     304             :   {
     305          48 :     work_vec = _local_second_order_indices;
     306          48 :     _local_second_order_indices.clear();
     307          48 :     lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
     308          48 :     std::merge(work_vec.begin(),
     309             :                work_vec.end(),
     310             :                var_dof_indices.begin(),
     311             :                var_dof_indices.end(),
     312             :                std::back_inserter(_local_second_order_indices));
     313             :   }
     314          30 : }
     315             : 
     316             : void
     317          34 : ExplicitMixedOrder::computeICs()
     318             : {
     319             :   // Compute the first-order approximation of the velocity at the current time step
     320             :   // using the Euler scheme, where the velocity is estimated as the difference
     321             :   // between the current solution and the previous time step, divided by the time
     322          34 :   auto vel = _sys.solutionUDot();
     323          34 :   *vel = *_solution;
     324          34 :   *vel -= _solution_old;
     325          34 :   *vel /= _dt;
     326          34 :   vel->close();
     327          34 : }
     328             : 
     329             : ExplicitMixedOrder::TimeOrder
     330         372 : ExplicitMixedOrder::findVariableTimeOrder(unsigned int var_num) const
     331             : {
     332         372 :   if (_vars_first.empty() && _vars_second.empty())
     333           0 :     mooseError("Time order sets are both empty.");
     334             :   if (_vars_first.count(var_num))
     335         120 :     return FIRST;
     336         252 :   else if (_vars_second.count(var_num))
     337         252 :     return SECOND;
     338             :   else
     339           0 :     mooseError("Variable " + _sys.system().variable_name(var_num) +
     340             :                " does not exist in time order sets.");
     341             : }

Generated by: LCOV version 1.14