libMesh
newmark_solver.C
Go to the documentation of this file.
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/newmark_solver.h"
19 
20 #include "libmesh/diff_solver.h"
21 #include "libmesh/diff_system.h"
22 #include "libmesh/dof_map.h"
23 #include "libmesh/numeric_vector.h"
24 
25 namespace libMesh
26 {
29  _beta(0.25),
30  _gamma(0.5),
31  _is_accel_solve(false),
32  _initial_accel_set(false)
33 {}
34 
36 
38 {
39  if (_gamma == 0.5)
40  return 2.;
41  return 1.;
42 }
43 
45 {
46  // We need to update velocity and acceleration before
47  // we update the nonlinear solution (displacement) and
48  // delta_t
49 
51  _system.get_vector("_old_solution_rate");
52 
54  _system.get_vector("_old_solution_accel");
55 
56  if (!first_solve)
57  {
58  NumericVector<Number> & old_nonlinear_soln =
59  _system.get_vector("_old_nonlinear_solution");
60 
61  NumericVector<Number> & nonlinear_solution =
62  *(_system.solution);
63 
64  // We need to cache the new solution_rate before updating the old_solution_rate
65  // so we can update acceleration with the proper old_solution_rate
66  // v_{n+1} = gamma/(beta*Delta t)*(x_{n+1}-x_n)
67  // - ((gamma/beta)-1)*v_n
68  // - (gamma/(2*beta)-1)*(Delta t)*a_n
69  std::unique_ptr<NumericVector<Number>> new_solution_rate = nonlinear_solution.clone();
70  (*new_solution_rate) -= old_nonlinear_soln;
71  (*new_solution_rate) *= (_gamma/(_beta*_system.deltat));
72  new_solution_rate->add( (1.0-_gamma/_beta), old_solution_rate );
73  new_solution_rate->add( (1.0-_gamma/(2.0*_beta))*_system.deltat, old_solution_accel );
74 
75  // a_{n+1} = (1/(beta*(Delta t)^2))*(x_{n+1}-x_n)
76  // - 1/(beta*Delta t)*v_n
77  // - (1-1/(2*beta))*a_n
78  std::unique_ptr<NumericVector<Number>> new_solution_accel = old_solution_accel.clone();
79  (*new_solution_accel) *= -(1.0/(2.0*_beta)-1.0);
80  new_solution_accel->add( -1.0/(_beta*_system.deltat), old_solution_rate );
81  new_solution_accel->add( 1.0/(_beta*_system.deltat*_system.deltat), nonlinear_solution );
82  new_solution_accel->add( -1.0/(_beta*_system.deltat*_system.deltat), old_nonlinear_soln );
83 
84  // Now update old_solution_rate
85  old_solution_rate = (*new_solution_rate);
86  old_solution_accel = (*new_solution_accel);
87  }
88 
89  // Localize updated vectors
90  old_solution_rate.localize
93 
94  old_solution_accel.localize
97 
98  // Now we can finish advancing the timestep
100 }
101 
103 {
104  libmesh_not_implemented();
105 }
106 
108 {
109  // We need to compute the initial acceleration based off of
110  // the initial position and velocity and, thus, acceleration
111  // is the unknown in diff_solver and not the displacement. So,
112  // We swap solution and acceleration. NewmarkSolver::_general_residual
113  // will check _is_accel_solve and provide the correct
114  // values to the FEMContext assuming this swap was made.
115  this->_is_accel_solve = true;
116 
117  //solution->accel, accel->solution
118  _system.solution->swap(_system.get_vector("_old_solution_accel"));
119  _system.update();
120 
121  this->_diff_solver->solve();
122 
123  // solution->solution, accel->accel
124  _system.solution->swap(_system.get_vector("_old_solution_accel"));
125  _system.update();
126 
127  // We're done, so no longer doing an acceleration solve
128  this->_is_accel_solve = false;
129 
130  this->set_initial_accel_avail(true);
131 }
132 
135 {
137  _system.get_vector("_old_solution_accel");
138 
140 
141  this->set_initial_accel_avail(true);
142 }
143 
144 void NewmarkSolver::set_initial_accel_avail( bool initial_accel_set )
145 {
146  _initial_accel_set = initial_accel_set;
147 }
148 
150 {
151  // First, check that the initial accel was set one way or another
152  libmesh_error_msg_if(!_initial_accel_set,
153  "ERROR: Must first set initial acceleration using one of:\n"
154  "NewmarkSolver::compute_initial_accel()\n"
155  "NewmarkSolver::project_initial_accel()\n");
156 
157  // That satisfied, now we can solve
159 }
160 
161 bool NewmarkSolver::element_residual (bool request_jacobian,
162  DiffContext & context)
163 {
164  return this->_general_residual(request_jacobian,
165  context,
171 }
172 
173 
174 
175 bool NewmarkSolver::side_residual (bool request_jacobian,
176  DiffContext & context)
177 {
178  return this->_general_residual(request_jacobian,
179  context,
185 }
186 
187 
188 
189 bool NewmarkSolver::nonlocal_residual (bool request_jacobian,
190  DiffContext & context)
191 {
192  return this->_general_residual(request_jacobian,
193  context,
199 }
200 
201 
202 
203 bool NewmarkSolver::_general_residual (bool request_jacobian,
204  DiffContext & context,
205  ResFuncType mass,
206  ResFuncType damping,
207  ResFuncType time_deriv,
208  ResFuncType constraint,
209  ReinitFuncType reinit_func)
210 {
211  unsigned int n_dofs = context.get_elem_solution().size();
212 
213  // We might need to save the old jacobian in case one of our physics
214  // terms later is unable to update it analytically.
215  DenseMatrix<Number> old_elem_jacobian(n_dofs, n_dofs);
216 
217  // Local velocity at old time step
218  DenseVector<Number> old_elem_solution_rate(n_dofs);
219  for (unsigned int i=0; i != n_dofs; ++i)
220  old_elem_solution_rate(i) =
221  old_solution_rate(context.get_dof_indices()[i]);
222 
223  // The user is computing the initial acceleration
224  // So upstream we've swapped _system.solution and _old_local_solution_accel
225  // So we need to give the context the correct entries since we're solving for
226  // acceleration here.
227  if (_is_accel_solve)
228  {
229  // System._solution is actually the acceleration right now so we need
230  // to reset the elem_solution to the right thing, which in this case
231  // is the initial guess for displacement, which got swapped into
232  // _old_solution_accel vector
233  DenseVector<Number> old_elem_solution(n_dofs);
234  for (unsigned int i=0; i != n_dofs; ++i)
235  old_elem_solution(i) =
236  old_solution_accel(context.get_dof_indices()[i]);
237 
238  context.elem_solution_derivative = 0.0;
239  context.elem_solution_rate_derivative = 0.0;
240  context.elem_solution_accel_derivative = 1.0;
241 
242  // Acceleration is currently the unknown so it's already sitting
243  // in elem_solution() thanks to FEMContext::pre_fe_reinit
244  context.get_elem_solution_accel() = context.get_elem_solution();
245 
246  // Now reset elem_solution() to what the user is expecting
247  context.get_elem_solution() = old_elem_solution;
248 
249  context.get_elem_solution_rate() = old_elem_solution_rate;
250 
251  // The user's Jacobians will be targeting derivatives w.r.t. u_{n+1}.
252  // Although the vast majority of cases will have the correct analytic
253  // Jacobians in this iteration, since we reset elem_solution_derivative*,
254  // if there are coupled/overlapping problems, there could be
255  // mismatches in the Jacobian. So we force finite differencing for
256  // the first iteration.
257  request_jacobian = false;
258  }
259  // Otherwise, the unknowns are the displacements and everything is straight
260  // forward and is what you think it is
261  else
262  {
263  if (request_jacobian)
264  old_elem_jacobian.swap(context.get_elem_jacobian());
265 
266  // Local displacement at old timestep
267  DenseVector<Number> old_elem_solution(n_dofs);
268  for (unsigned int i=0; i != n_dofs; ++i)
269  old_elem_solution(i) =
271 
272  // Local acceleration at old time step
273  DenseVector<Number> old_elem_solution_accel(n_dofs);
274  for (unsigned int i=0; i != n_dofs; ++i)
275  old_elem_solution_accel(i) =
276  old_solution_accel(context.get_dof_indices()[i]);
277 
278  // Convenience
280 
281  context.elem_solution_derivative = 1.0;
282 
283  // Local velocity at current time step
284  // v_{n+1} = gamma/(beta*Delta t)*(x_{n+1}-x_n)
285  // + (1-(gamma/beta))*v_n
286  // + (1-gamma/(2*beta))*(Delta t)*a_n
287  context.elem_solution_rate_derivative = (_gamma/(_beta*dt));
288 
289  context.get_elem_solution_rate() = context.get_elem_solution();
290  context.get_elem_solution_rate() -= old_elem_solution;
292  context.get_elem_solution_rate().add( (1.0-_gamma/_beta), old_elem_solution_rate);
293  context.get_elem_solution_rate().add( (1.0-_gamma/(2.0*_beta))*dt, old_elem_solution_accel);
294 
295 
296 
297  // Local acceleration at current time step
298  // a_{n+1} = (1/(beta*(Delta t)^2))*(x_{n+1}-x_n)
299  // - 1/(beta*Delta t)*v_n
300  // - (1/(2*beta)-1)*a_n
301  context.elem_solution_accel_derivative = 1.0/(_beta*dt*dt);
302 
303  context.get_elem_solution_accel() = context.get_elem_solution();
304  context.get_elem_solution_accel() -= old_elem_solution;
306  context.get_elem_solution_accel().add(-1.0/(_beta*dt), old_elem_solution_rate);
307  context.get_elem_solution_accel().add(-(1.0/(2.0*_beta)-1.0), old_elem_solution_accel);
308 
309  // Move the mesh into place first if necessary, set t = t_{n+1}
310  (context.*reinit_func)(1.);
311  }
312 
313  // If a fixed solution is requested, we'll use x_{n+1}
315  context.get_elem_fixed_solution() = context.get_elem_solution();
316 
317  // Get the time derivative at t_{n+1}, F(u_{n+1})
318  bool jacobian_computed = (_system.get_physics()->*time_deriv)(request_jacobian, context);
319 
320  // Damping at t_{n+1}, C(u_{n+1})
321  jacobian_computed = (_system.get_physics()->*damping)(jacobian_computed, context) &&
322  jacobian_computed;
323 
324  // Mass at t_{n+1}, M(u_{n+1})
325  jacobian_computed = (_system.get_physics()->*mass)(jacobian_computed, context) &&
326  jacobian_computed;
327 
328  // Add the constraint term
329  jacobian_computed = (_system.get_physics()->*constraint)(jacobian_computed, context) &&
330  jacobian_computed;
331 
332  // Add back (or restore) the old jacobian
333  if (request_jacobian)
334  {
335  if (jacobian_computed)
336  context.get_elem_jacobian() += old_elem_jacobian;
337  else
338  context.get_elem_jacobian().swap(old_elem_jacobian);
339  }
340 
341  return jacobian_computed;
342 }
343 
344 }
virtual bool element_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; element_time_derivative() and element_constraint() to bui...
virtual bool side_constraint(bool request_jacobian, DiffContext &)
Adds the constraint contribution on side of elem to elem_residual.
Definition: diff_physics.h:195
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
Real _gamma
The value for to employ.
Number old_solution_accel(const dof_id_type global_dof_number) const
void swap(DenseMatrix< T > &other_matrix)
STL-like swap method.
Definition: dense_matrix.h:865
boostcopy::enable_if_c< ScalarTraits< T2 >::value, void >::type add(const T2 factor, const DenseVector< T3 > &vec)
Adds factor times vec to this vector.
Definition: dense_vector.h:477
This class provides all data required for a physics package (e.g.
Definition: diff_context.h:55
bool _initial_accel_set
This method requires an initial acceleration.
std::unique_ptr< DiffSolver > _diff_solver
An implicit linear or nonlinear solver to use at each timestep.
Definition: time_solver.h:302
const DenseVector< Number > & get_elem_fixed_solution() const
Accessor for element fixed solution.
Definition: diff_context.h:210
const DenseVector< Number > & get_elem_solution_rate() const
Accessor for element solution rate of change w.r.t.
Definition: diff_context.h:144
virtual bool side_damping_residual(bool request_jacobian, DiffContext &)
Subtracts a damping vector contribution on side of elem from elem_residual.
Definition: diff_physics.h:378
Number old_nonlinear_solution(const dof_id_type global_dof_number) const
virtual bool side_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; side_time_derivative() and side_constraint() to build a f...
virtual std::unique_ptr< NumericVector< T > > clone() const =0
virtual bool nonlocal_time_derivative(bool request_jacobian, DiffContext &)
Adds any nonlocal time derivative contributions (e.g.
Definition: diff_physics.h:214
virtual bool damping_residual(bool request_jacobian, DiffContext &)
Subtracts a damping vector contribution on elem from elem_residual.
Definition: diff_physics.h:360
std::unique_ptr< NumericVector< Number > > _old_local_solution_accel
Serial vector of previous time step acceleration .
The libMesh namespace provides an interface to certain functionality in the library.
virtual bool nonlocal_constraint(bool request_jacobian, DiffContext &)
Adds any nonlocal constraint contributions (e.g.
Definition: diff_physics.h:233
virtual void elem_reinit(Real)
Gives derived classes the opportunity to reinitialize data (FE objects in FEMSystem, for example) needed for an interior integration at a new point within a timestep.
Definition: diff_context.h:77
bool _eulerian_time_deriv(bool request_jacobian, DiffContext &)
This method simply combines element_time_derivative() and eulerian_residual(), which makes its addres...
Definition: diff_physics.C:96
virtual void compute_initial_accel()
This method uses the specified initial displacement and velocity to compute the initial acceleration ...
virtual ~NewmarkSolver()
Destructor.
virtual bool mass_residual(bool request_jacobian, DiffContext &)
Subtracts a mass vector contribution on elem from elem_residual.
Definition: diff_physics.h:302
NewmarkSolver(sys_type &s)
Constructor.
This class provides a specific system class.
Definition: diff_system.h:54
sys_type & _system
A reference to the system we are solving.
Definition: time_solver.h:312
virtual bool _general_residual(bool request_jacobian, DiffContext &, ResFuncType mass, ResFuncType damping, ResFuncType time_deriv, ResFuncType constraint, ReinitFuncType reinit)
This method is the underlying implementation of the public residual methods.
Real elem_solution_rate_derivative
The derivative of elem_solution_rate with respect to the current nonlinear solution, for use by systems with non default mass_residual terms.
Definition: diff_context.h:503
const DenseVector< Number > & get_elem_solution() const
Accessor for element solution.
Definition: diff_context.h:112
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:280
bool use_fixed_solution
A boolean to be set to true by systems using elem_fixed_solution, for optional use by e...
Definition: system.h:1563
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition: system.h:1593
Real elem_solution_derivative
The derivative of elem_solution with respect to the current nonlinear solution.
Definition: diff_context.h:496
void set_initial_accel_avail(bool initial_accel_set)
Allow the user to (re)set whether the initial acceleration is available.
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
virtual void advance_timestep() override
This method advances the solution to the next timestep, after a solve() has been performed.
Number old_solution_rate(const dof_id_type global_dof_number) const
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; nonlocal_time_derivative() and nonlocal_constraint() to b...
Generic class from which second order UnsteadySolvers should subclass.
virtual void solve() override
This method solves for the solution at the next timestep.
const DifferentiablePhysics * get_physics() const
Definition: diff_system.h:181
virtual void advance_timestep() override
This method advances the solution to the next timestep, after a solve() has been performed.
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:510
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual void elem_side_reinit(Real)
Gives derived classes the opportunity to reinitialize data needed for a side integration at a new poi...
Definition: diff_context.h:83
virtual bool nonlocal_damping_residual(bool request_jacobian, DiffContext &)
Subtracts any nonlocal damping vector contributions (e.g.
Definition: diff_physics.h:394
const DenseVector< Number > & get_elem_solution_accel() const
Accessor for element solution accel of change w.r.t.
Definition: diff_context.h:177
virtual unsigned int size() const override final
Definition: dense_vector.h:104
virtual Real error_order() const override
Error convergence order: 2 for , 1 otherwise.
void project_vector(NumericVector< Number > &new_vector, FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr, int is_adjoint=-1) const
Projects arbitrary functions onto a vector of degree of freedom values for the current system...
bool _is_accel_solve
Need to be able to indicate to _general_residual if we are doing an acceleration solve or not...
virtual void solve() override
This method solves for the solution at the next timestep.
virtual bool side_mass_residual(bool request_jacobian, DiffContext &)
Subtracts a mass vector contribution on side of elem from elem_residual.
Definition: diff_physics.h:320
std::unique_ptr< NumericVector< Number > > _old_local_solution_rate
Serial vector of previous time step velocity .
virtual void add(const numeric_index_type i, const T value)=0
Adds value to the vector entry specified by i.
virtual bool side_time_derivative(bool request_jacobian, DiffContext &)
Adds the time derivative contribution on side of elem to elem_residual.
Definition: diff_physics.h:174
virtual bool element_constraint(bool request_jacobian, DiffContext &)
Adds the constraint contribution on elem to elem_residual.
Definition: diff_physics.h:144
const DofMap & get_dof_map() const
Definition: system.h:2374
Real elem_solution_accel_derivative
The derivative of elem_solution_accel with respect to the current nonlinear solution, for use by systems with non default mass_residual terms.
Definition: diff_context.h:510
const std::vector< dof_id_type > & get_send_list() const
Definition: dof_map.h:526
virtual bool nonlocal_mass_residual(bool request_jacobian, DiffContext &c)
Subtracts any nonlocal mass vector contributions (e.g.
Definition: diff_physics.C:57
virtual void adjoint_advance_timestep() override
This method advances the adjoint solution to the previous timestep, after an adjoint_solve() has been...
void project_initial_accel(FunctionBase< Number > *f, FunctionBase< Gradient > *g=nullptr)
Specify non-zero initial acceleration.
Real _beta
The value for the to employ.
const NumericVector< Number > & get_vector(std::string_view vec_name) const
Definition: system.C:943
bool first_solve
A bool that will be true the first time solve() is called, and false thereafter.
virtual void nonlocal_reinit(Real)
Gives derived classes the opportunity to reinitialize data needed for nonlocal calculations at a new ...
Definition: diff_context.h:95