https://mooseframework.inl.gov
ExplicitMixedOrder.C
Go to the documentation of this file.
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",
39 
42 {
44 
45  params.addClassDescription(
46  "Implementation of explicit time integration without invoking any of the nonlinear solver.");
47 
48  params.addParam<bool>("use_constant_mass",
49  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  params.addParam<TagName>("mass_matrix_tag", "mass", "The tag for the mass matrix");
53 
54  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  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  params.suppressParameter<std::vector<VariableName>>("variables");
68 
69  MooseEnum solve_type("consistent lumped lump_preconditioned", "lumped");
70  params.setParameters("solve_type", solve_type);
71  params.ignoreParameter<MooseEnum>("solve_type");
72  return params;
73 }
74 
76  : ExplicitTimeIntegrator(parameters),
77  _constant_mass(getParam<bool>("use_constant_mass")),
78  _mass_matrix(getParam<TagName>("mass_matrix_tag")),
79  _solution_older(_sys.solutionState(2)),
80  _vars_first(declareRestartableData<std::unordered_set<unsigned int>>("first_order_vars")),
81  _local_first_order_indices(
82  declareRestartableData<std::vector<dof_id_type>>("first_local_indices")),
83  _vars_second(declareRestartableData<std::unordered_set<unsigned int>>("second_order_vars")),
84  _local_second_order_indices(
85  declareRestartableData<std::vector<dof_id_type>>("second_local_indices"))
86 {
90 }
91 
92 void
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  return;
102 }
103 
104 TagID
106 {
108 }
109 
110 void
112 {
113  // Getting the tagID for the mass matrix
114  auto mass_tag = massMatrixTagID();
115 
116  // Reset iteration counts
119 
121 
122  auto & mass_matrix = _nonlinear_implicit_system->get_system_matrix();
123 
124  // Compute the mass matrix
125  if (!_constant_mass || _t_step == 1)
126  {
127  // We only want to compute "inverted" lumped mass matrix once.
129  *_nonlinear_implicit_system->current_local_solution, mass_matrix, mass_tag);
130 
131  // Calculating the lumped mass matrix for use in residual calculation
132  mass_matrix.vector_mult(*_mass_matrix_diag_inverted, *_ones);
133 
134  // "Invert" the diagonal mass matrix
135  _mass_matrix_diag_inverted->reciprocal();
137  }
138 
139  // Set time to the time at which to evaluate the residual
142 
143  // Compute the residual
144  _explicit_residual->zero();
147 
148  // Move the residual to the RHS
149  *_explicit_residual *= -1.0;
150 
151  // Perform the linear solve
152  bool converged = performExplicitSolve(mass_matrix);
154 
155  // Update the solution
158 
160 
163 }
164 
165 void
167 {
168  residual += *_Re_time;
169  residual += *_Re_non_time;
170  residual.close();
171 
172  // Reset time to the time at which to evaluate nodal BCs, which comes next
174 }
175 
176 bool
178 {
179 
180  bool converged = false;
181 
182  // Grab all the vectors that we will need
183  auto accel = _sys.solutionUDotDot();
184  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(
190  const std::unique_ptr<NumericVector<Real>> exp_res_first(
192  _mass_matrix_diag_inverted->create_subvector(*mass_first, _local_first_order_indices, false);
193  _explicit_residual->create_subvector(*exp_res_first, _local_first_order_indices, false);
194 
195  // Need velocity vector split into subvectors
196  auto vel_first = vel->get_subvector(_local_first_order_indices);
197 
198  // Velocity update for foward euler
199  vel_first->pointwise_mult(*mass_first, *exp_res_first);
200 
201  // Restore the velocities
202  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(
208  const std::unique_ptr<NumericVector<Real>> exp_res_second(
210  _mass_matrix_diag_inverted->create_subvector(*mass_second, _local_second_order_indices, false);
211  _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  auto accel_second = accel->get_subvector(_local_second_order_indices);
215 
216  auto vel_second = vel->get_subvector(_local_second_order_indices);
217 
218  // Compute acceleration for central difference
219  accel_second->pointwise_mult(*mass_second, *exp_res_second);
220 
221  // Scaling the acceleration
222  auto accel_scaled = accel_second->clone();
223  accel_scaled->scale((_dt + _dt_old) / 2);
224 
225  // Velocity update for central difference
226  *vel_second += *accel_scaled;
227 
228  // Restore acceleration
229  accel->restore_subvector(std::move(accel_second), _local_second_order_indices);
230 
231  vel->restore_subvector(std::move(vel_second), _local_second_order_indices);
232 
233  // Same solution update for both methods
234  *_solution_update = *vel;
235  _solution_update->scale(_dt);
236 
237  // Check for convergence by seeing if there is a nan or inf
238  auto sum = _solution_update->sum();
239  converged = std::isfinite(sum);
240 
241  // The linear iteration count remains zero
243  vel->close();
244  accel->close();
245 
246  return converged;
247 }
248 
249 void
251 {
253 
254  // Compute ICs for velocity
255  computeICs();
256 
257  // Seperate variables into first and second time integration order and find
258  // the local indices for each
259  const auto & var_names_first = getParam<std::vector<VariableName>>("first_order_vars");
260  const auto & var_names_second = getParam<std::vector<VariableName>>("second_order_vars");
261  std::vector<unsigned int> var_num_vec;
262 
263  auto & lm_sys = _sys.system();
264  lm_sys.get_all_variable_numbers(var_num_vec);
265  std::unordered_set<unsigned int> var_nums(var_num_vec.begin(), var_num_vec.end());
266 
267  for (const auto & var_name : var_names_first)
268  if (lm_sys.has_variable(var_name))
269  {
270  const auto var_num = lm_sys.variable_number(var_name);
271  _vars_first.insert(var_num);
272  var_nums.erase(var_num);
273  }
274 
275  for (const auto & var_name : var_names_second)
276  if (lm_sys.has_variable(var_name))
277  {
278  const auto var_num = lm_sys.variable_number(var_name);
279  _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  if (!var_nums.empty())
285  mooseError("Not all nonlinear variables have their order specified.");
286 
287  std::vector<dof_id_type> var_dof_indices, work_vec;
288  for (const auto var_num : _vars_first)
289  {
290  work_vec = _local_first_order_indices;
292  lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
293  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  for (const auto var_num : _vars_second)
304  {
305  work_vec = _local_second_order_indices;
307  lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
308  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 }
315 
316 void
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  auto vel = _sys.solutionUDot();
323  *vel = *_solution;
324  *vel -= _solution_old;
325  *vel /= _dt;
326  vel->close();
327 }
328 
330 ExplicitMixedOrder::findVariableTimeOrder(unsigned int var_num) const
331 {
332  if (_vars_first.empty() && _vars_second.empty())
333  mooseError("Time order sets are both empty.");
334  if (_vars_first.count(var_num))
335  return FIRST;
336  else if (_vars_second.count(var_num))
337  return SECOND;
338  else
339  mooseError("Variable " + _sys.system().variable_name(var_num) +
340  " does not exist in time order sets.");
341 }
virtual void computeJacobianTag(const NumericVector< libMesh::Number > &soln, libMesh::SparseMatrix< libMesh::Number > &jacobian, TagID tag)
virtual TagID massMatrixTagID() const override
std::unique_ptr< NonlinearSolver< Number > > nonlinear_solver
void overwriteNodeFace(NumericVector< Number > &soln)
virtual Real & time() const
NumericVector< Real > * _solution_update
unsigned int TagID
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
virtual void setUDotDotRequested(const bool u_dotdot_requested)
NumericVector< Real > * _ones
std::unordered_set< unsigned int > & _vars_second
FEProblemBase & _fe_problem
NonlinearSystemBase * _nl
const bool & _constant_mass
Whether we are reusing the mass matrix.
virtual libMesh::System & system()=0
const TagName & _mass_matrix
Mass matrix name.
void setSolution(const NumericVector< Number > &soln)
void get_all_variable_numbers(std::vector< unsigned int > &all_variable_numbers) const
TimeOrder findVariableTimeOrder(unsigned int var_num) const
Retrieve the order of the highest time derivative of a variable.
virtual void postResidual(NumericVector< Number > &residual) override
Implements a form of the central difference time integrator that calculates acceleration directly fro...
const Parallel::Communicator & _communicator
NumericVector< Number > * _Re_time
void suppressParameter(const std::string &name)
virtual void setUDotOldRequested(const bool u_dot_old_requested)
bool converged(const std::vector< std::pair< unsigned int, Real >> &residuals, const std::vector< Real > &abs_tolerances)
Based on the residuals, determine if the iterative process converged or not.
libMesh::NonlinearImplicitSystem * _nonlinear_implicit_system
static InputParameters validParams()
std::unordered_set< unsigned int > & _vars_first
void ignoreParameter(const std::string &name)
std::vector< dof_id_type > & _local_second_order_indices
virtual TagID getMatrixTagID(const TagName &tag_name) const
registerMooseObject("SolidMechanicsApp", ExplicitMixedOrder)
virtual void setUDotRequested(const bool u_dot_requested)
NumericVector< Real > * _explicit_residual
virtual NumericVector< Number > * solutionUDot()
SubProblem & subproblem()
std::unique_ptr< NumericVector< Number > > solution
static InputParameters validParams()
unsigned int _n_linear_iterations
virtual void computeTimeDerivatives() override
const std::string & variable_name(const unsigned int i) const
unsigned int number() const
virtual void close()=0
void computeResidual(libMesh::NonlinearImplicitSystem &sys, const NumericVector< libMesh::Number > &soln, NumericVector< libMesh::Number > &residual)
registerMooseObjectRenamed("SolidMechanicsApp", DirectCentralDifference, "10/14/2025 00:00", ExplicitMixedOrder)
const NumericVector< Number > *const & _solution
NumericVector< Real > * _mass_matrix_diag_inverted
virtual void init() override
virtual bool performExplicitSolve(SparseMatrix< Number > &mass_matrix) override
virtual void init() override
static std::unique_ptr< NumericVector< Number > > build(const Parallel::Communicator &comm, SolverPackage solver_package=libMesh::default_solver_package(), ParallelType parallel_type=AUTOMATIC)
void mooseError(Args &&... args) const
std::unique_ptr< NumericVector< Number > > current_local_solution
void addClassDescription(const std::string &doc_string)
virtual Real & timeOld() const
const NumericVector< Number > & _solution_old
unsigned int _n_nonlinear_iterations
virtual void solve() override
ExplicitMixedOrder(const InputParameters &parameters)
virtual NumericVector< Number > * solutionUDotDot()
NumericVector< Number > * _Re_non_time
NumericVector< Number > & solutionOld()
const SparseMatrix< Number > & get_system_matrix() const
std::vector< dof_id_type > & _local_first_order_indices
void ErrorVector unsigned int
void setParameters(const std::string &name, const T &value, Ts... extra_input_parameters)
uint8_t dof_id_type