https://mooseframework.inl.gov
Loading...
Searching...
No Matches
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"
14#include "Moose.h"
15#include "MooseError.h"
16#include "MooseTypes.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
35registerMooseObjectRenamed("SolidMechanicsApp",
36 DirectCentralDifference,
37 "10/14/2025 00:00",
39
42{
44
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
53 params.addParam<bool>(
54 "recompute_mass_matrix_after_mesh_change",
55 false,
56 "If set to true, the mass matrix will be recomputed when the mesh changes (e.g. through "
57 "adaptivity). If use_constant_mass is set to true, adadaptivity is used, and this parameter "
58 "is not set to true, the simulation will error out when the mesh changes.");
59
60 params.addParam<TagName>("mass_matrix_tag", "mass", "The tag for the mass matrix");
61
62 params.addParam<std::vector<VariableName>>(
63 "second_order_vars",
64 {},
65 "A subset of variables that require second-order integration (velocity and acceleration) to "
66 "be applied by this time integrator.");
67
68 params.addParam<std::vector<VariableName>>(
69 "first_order_vars",
70 {},
71 "A subset of variables that require first-order integration (velocity only) to be applied by "
72 "this time integrator.");
73
74 // Prevent users from using variables option by accident.
75 params.suppressParameter<std::vector<VariableName>>("variables");
76
77 MooseEnum solve_type("consistent lumped lump_preconditioned", "lumped");
78 params.setParameters("solve_type", solve_type);
79 params.ignoreParameter<MooseEnum>("solve_type");
80 return params;
81}
82
84 : ExplicitTimeIntegrator(parameters),
85 _constant_mass(getParam<bool>("use_constant_mass")),
86 _recompute_mass_matrix_on_mesh_change(
87 getParam<bool>("recompute_mass_matrix_after_mesh_change")),
88 _mesh_changed(true),
89 _mass_matrix_name(getParam<TagName>("mass_matrix_tag")),
90 _mass_matrix_lumped(addVector("mass_matrix_lumped", true, GHOSTED)),
91 _solution_older(_sys.solutionState(2)),
92 _vars_first(declareRestartableData<std::unordered_set<unsigned int>>("first_order_vars")),
93 _local_first_order_indices(
94 declareRestartableData<std::vector<dof_id_type>>("first_local_indices")),
95 _vars_second(declareRestartableData<std::unordered_set<unsigned int>>("second_order_vars")),
96 _local_second_order_indices(
97 declareRestartableData<std::vector<dof_id_type>>("second_local_indices"))
98{
102}
103
104void
106{
107 /*
108 Because this is called in NonLinearSystemBase
109 this should not actually compute the time derivatives.
110 Calculating time derivatives here will cause issues for the
111 solution update.
112 */
113 return;
114}
115
116void
118{
120 paramError("recompute_mass_matrix_after_mesh_change",
121 "Must be set to true explicitly by the user to support adaptivity with "
122 "`use_constant_mass`.");
123
124 // after mesh changes we need to recompute the mass matrix, it is not interpolable as it contains
125 // a volume integrated quantity!
126 _mesh_changed = true;
127
129}
130
131TagID
136
137void
139{
140 // Getting the tagID for the mass matrix
141 auto mass_tag = massMatrixTagID();
142
143 // Reset iteration counts
146
148
149 auto & mass_matrix = _nonlinear_implicit_system->get_system_matrix();
150
151 if (_mesh_changed)
153
154 // Compute the mass matrix
156 {
157 // We only want to compute "inverted" lumped mass matrix once.
159 *_nonlinear_implicit_system->current_local_solution, mass_matrix, mass_tag);
160
161 // Calculate and record the lumped mass matrix for use in residual calculation
162 mass_matrix.vector_mult(*_mass_matrix_lumped, *_ones);
163 _mass_matrix_lumped->close();
164
165 // "Invert" the diagonal mass matrix
167 _mass_matrix_diag_inverted->reciprocal();
169 }
170
171 _mesh_changed = false;
172
173 // Set time to the time at which to evaluate the residual
176
177 // Evaluate residual and move it to the RHS
179
180 // Perform the linear solve
181 bool converged = performExplicitSolve(mass_matrix);
183
184 // Update the solution
187
189
191 _nonlinear_implicit_system->nonlinear_solver->converged = converged;
192}
193
194void
196{
197 // Compute the residual
200
201 // Move the residual to the RHS
202 *_explicit_residual *= -1.0;
203}
204
205void
206ExplicitMixedOrder::postResidual(NumericVector<Number> & residual)
207{
208 residual += *_Re_time;
209 residual += *_Re_non_time;
210 residual.close();
211
212 // Reset time to the time at which to evaluate nodal BCs, which comes next
214}
215
216bool
218{
219 bool converged = false;
220
221 // Grab all the vectors that we will need
222 auto accel = _sys.solutionUDotDot();
223 auto vel = _sys.solutionUDot();
224
225 // Compute Forward Euler
226 // Split diag mass and residual vectors into correct subvectors
227 const std::unique_ptr<NumericVector<Number>> mass_inv_first(
228 NumericVector<Number>::build(_communicator, libMesh::default_solver_package(), PARALLEL));
229 const std::unique_ptr<NumericVector<Real>> exp_res_first(
230 NumericVector<Number>::build(_communicator, libMesh::default_solver_package(), PARALLEL));
231 _mass_matrix_diag_inverted->create_subvector(*mass_inv_first, _local_first_order_indices, false);
232 _explicit_residual->create_subvector(*exp_res_first, _local_first_order_indices, false);
233
234 // Need velocity vector split into subvectors
235 auto vel_first = vel->get_subvector(_local_first_order_indices);
236
237 // Velocity update for foward euler
238 vel_first->pointwise_mult(*mass_inv_first, *exp_res_first);
239
240 // Restore the velocities
241 vel->restore_subvector(std::move(vel_first), _local_first_order_indices);
242
243 // Compute Central Difference
244 // Split diag mass and residual vectors into correct subvectors
245 const std::unique_ptr<NumericVector<Real>> mass_inv_second(
246 NumericVector<Number>::build(_communicator, libMesh::default_solver_package(), PARALLEL));
247 const std::unique_ptr<NumericVector<Real>> exp_res_second(
248 NumericVector<Number>::build(_communicator, libMesh::default_solver_package(), PARALLEL));
249 _mass_matrix_diag_inverted->create_subvector(
250 *mass_inv_second, _local_second_order_indices, false);
251 _explicit_residual->create_subvector(*exp_res_second, _local_second_order_indices, false);
252
253 // Only need acceleration and old velocity vector for central difference
254 auto accel_second = accel->get_subvector(_local_second_order_indices);
255
256 auto vel_second = vel->get_subvector(_local_second_order_indices);
257
258 // Compute acceleration for central difference
259 accel_second->pointwise_mult(*mass_inv_second, *exp_res_second);
260
261 // Scaling the acceleration
262 auto accel_scaled = accel_second->clone();
263 accel_scaled->scale((_dt + _dt_old) / 2);
264
265 // Velocity update for central difference
266 *vel_second += *accel_scaled;
267
268 // Restore acceleration
269 accel->restore_subvector(std::move(accel_second), _local_second_order_indices);
270
271 vel->restore_subvector(std::move(vel_second), _local_second_order_indices);
272
273 // Same solution update for both methods
274 *_solution_update = *vel;
275 _solution_update->scale(_dt);
276
277 // Check for convergence by seeing if there is a nan or inf
278 auto sum = _solution_update->sum();
279 converged = std::isfinite(sum);
280
281 // The linear iteration count remains zero
283 vel->close();
284 accel->close();
285
286 return converged;
287}
288
289void
291{
293
294 // Compute ICs for velocity
295 computeICs();
296
297 // Seperate variables into first and second time integration order and find
298 // the local indices for each
299 const auto & var_names_first = getParam<std::vector<VariableName>>("first_order_vars");
300 const auto & var_names_second = getParam<std::vector<VariableName>>("second_order_vars");
301 std::vector<unsigned int> var_num_vec;
302
303 auto & lm_sys = _sys.system();
304 lm_sys.get_all_variable_numbers(var_num_vec);
305 std::unordered_set<unsigned int> var_nums(var_num_vec.begin(), var_num_vec.end());
306
307 for (const auto & var_name : var_names_first)
308 if (lm_sys.has_variable(var_name))
309 {
310 const auto var_num = lm_sys.variable_number(var_name);
311 _vars_first.insert(var_num);
312 var_nums.erase(var_num);
313 }
314
315 for (const auto & var_name : var_names_second)
316 if (lm_sys.has_variable(var_name))
317 {
318 const auto var_num = lm_sys.variable_number(var_name);
319 _vars_second.insert(var_num);
320 var_nums.erase(var_num);
321 }
322
323 // If var_nums is empty then that means the user has specified all the variables in this system
324 if (!var_nums.empty())
325 mooseError("Not all nonlinear variables have their order specified.");
326}
327
328void
330{
331 auto & lm_sys = _sys.system();
332
333 std::vector<dof_id_type> var_dof_indices, work_vec;
334 for (const auto var_num : _vars_first)
335 {
338 lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
339 std::merge(work_vec.begin(),
340 work_vec.end(),
341 var_dof_indices.begin(),
342 var_dof_indices.end(),
343 std::back_inserter(_local_first_order_indices));
344 }
345
346 work_vec.clear();
347 var_dof_indices.clear();
348
349 for (const auto var_num : _vars_second)
350 {
353 lm_sys.get_dof_map().local_variable_indices(var_dof_indices, lm_sys.get_mesh(), var_num);
354 std::merge(work_vec.begin(),
355 work_vec.end(),
356 var_dof_indices.begin(),
357 var_dof_indices.end(),
358 std::back_inserter(_local_second_order_indices));
359 }
360}
361
362void
364{
365 // Compute the first-order approximation of the velocity at the current time step
366 // using the Euler scheme, where the velocity is estimated as the difference
367 // between the current solution and the previous time step, divided by the time
368 auto vel = _sys.solutionUDot();
369 *vel = *_solution;
370 *vel -= _solution_old;
371 *vel /= _dt;
372 vel->close();
373}
374
377{
378 if (_vars_first.empty() && _vars_second.empty())
379 mooseError("Time order sets are both empty.");
380 if (_vars_first.count(var_num))
381 return FIRST;
382 else if (_vars_second.count(var_num))
383 return SECOND;
384 else
385 mooseError("Variable " + _sys.system().variable_name(var_num) +
386 " does not exist in time order sets.");
387}
registerMooseObjectRenamed("SolidMechanicsApp", DirectCentralDifference, "10/14/2025 00:00", ExplicitMixedOrder)
registerMooseObject("SolidMechanicsApp", ExplicitMixedOrder)
unsigned int TagID
void ErrorVector unsigned int
Implements a form of the central difference time integrator that calculates acceleration directly fro...
std::unordered_set< unsigned int > & _vars_second
TimeOrder findVariableTimeOrder(unsigned int var_num) const
Retrieve the order of the highest time derivative of a variable.
virtual void meshChanged() override
const bool & _recompute_mass_matrix_on_mesh_change
Must be set to true to use adaptivity with a constant mass matrix.
ExplicitMixedOrder(const InputParameters &parameters)
virtual void evaluateRHSResidual()
Evaluate the RHS residual.
NumericVector< Real > * _mass_matrix_lumped
Lumped mass matrix.
virtual void postResidual(NumericVector< Number > &residual) override
const TagName & _mass_matrix_name
Mass matrix name.
virtual void solve() override
virtual TagID massMatrixTagID() const override
std::unordered_set< unsigned int > & _vars_first
const bool & _constant_mass
Whether we are reusing the mass matrix.
bool _mesh_changed
Whether the mesh changed just before the current solve.
void updateDOFIndices()
compile the dof indices for first and second order in time variables
std::vector< dof_id_type > & _local_second_order_indices
virtual void init() override
static InputParameters validParams()
std::vector< dof_id_type > & _local_first_order_indices
virtual bool performExplicitSolve(SparseMatrix< Number > &mass_matrix) override
virtual void computeTimeDerivatives() override
virtual void meshChanged() override
virtual void init() override
NumericVector< Real > * _mass_matrix_diag_inverted
NumericVector< Real > * _ones
static InputParameters validParams()
NumericVector< Real > * _solution_update
NumericVector< Real > * _explicit_residual
virtual void setUDotOldRequested(const bool u_dot_old_requested)
virtual void setUDotDotRequested(const bool u_dotdot_requested)
void computeResidual(libMesh::NonlinearImplicitSystem &sys, const NumericVector< libMesh::Number > &soln, NumericVector< libMesh::Number > &residual)
virtual Real & timeOld() const
virtual Real & time() const
virtual void setUDotRequested(const bool u_dot_requested)
virtual void computeJacobianTag(const NumericVector< libMesh::Number > &soln, libMesh::SparseMatrix< libMesh::Number > &jacobian, TagID tag)
void suppressParameter(const std::string &name)
void addParam(const std::string &name, const std::initializer_list< typename T::value_type > &value, const std::string &doc_string)
void ignoreParameter(const std::string &name)
void addClassDescription(const std::string &doc_string)
void setParameters(const std::string &name, const T &value, Ts... extra_input_parameters)
void paramError(const std::string &param, Args... args) const
void mooseError(Args &&... args) const
void overwriteNodeFace(NumericVector< Number > &soln)
NonlinearSystemBase * _nl
NumericVector< Number > * _Re_non_time
NumericVector< Number > * _Re_time
libMesh::NonlinearImplicitSystem * _nonlinear_implicit_system
void setSolution(const NumericVector< Number > &soln)
virtual TagID getMatrixTagID(const TagName &tag_name) const
virtual NumericVector< Number > * solutionUDot()
unsigned int number() const
NumericVector< Number > & solutionOld()
SubProblem & subproblem()
virtual libMesh::System & system()=0
virtual NumericVector< Number > * solutionUDotDot()
const NumericVector< Number > *const & _solution
unsigned int _n_linear_iterations
unsigned int _n_nonlinear_iterations
FEProblemBase & _fe_problem
const NumericVector< Number > & _solution_old
const SparseMatrix< Number > & get_system_matrix() const
std::unique_ptr< NonlinearSolver< Number > > nonlinear_solver
virtual void close()=0
const Parallel::Communicator & _communicator
std::unique_ptr< NumericVector< Number > > current_local_solution
void get_all_variable_numbers(std::vector< unsigned int > &all_variable_numbers) const
virtual void clear()
std::unique_ptr< NumericVector< Number > > solution
const std::string & variable_name(const unsigned int i) const
SolverPackage default_solver_package()