libMesh
euler2_solver.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2024 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 
19 // Local includes
20 #include "libmesh/euler2_solver.h"
21 
22 #include "libmesh/adjoint_refinement_estimator.h"
23 #include "libmesh/diff_system.h"
24 #include "libmesh/error_vector.h"
25 #include "libmesh/int_range.h"
26 #include "libmesh/numeric_vector.h"
27 
28 namespace libMesh
29 {
30 
31 
32 
34  : FirstOrderUnsteadySolver(s), theta(1.)
35 {
36 }
37 
38 
39 
40 Euler2Solver::~Euler2Solver () = default;
41 
42 
43 
45 {
46  if (theta == 0.5)
47  return 2.;
48  return 1.;
49 }
50 
51 
52 
53 
54 bool Euler2Solver::element_residual (bool request_jacobian,
55  DiffContext & context)
56 {
58 
59  return this->_general_residual(request_jacobian,
60  context,
67 }
68 
69 
70 
71 bool Euler2Solver::side_residual (bool request_jacobian,
72  DiffContext & context)
73 {
74  return this->_general_residual(request_jacobian,
75  context,
81  false);
82 }
83 
84 
85 
86 bool Euler2Solver::nonlocal_residual (bool request_jacobian,
87  DiffContext & context)
88 {
90 
91  return this->_general_residual(request_jacobian,
92  context,
99 }
100 
101 
102 
103 bool Euler2Solver::_general_residual (bool request_jacobian,
104  DiffContext & context,
105  ResFuncType mass,
106  ResFuncType damping,
107  ResFuncType time_deriv,
108  ResFuncType constraint,
109  ReinitFuncType reinit_func,
110  bool compute_second_order_eqns)
111 {
112  unsigned int n_dofs = context.get_elem_solution().size();
113 
114  // Local nonlinear solution at old timestep
115  DenseVector<Number> old_elem_solution(n_dofs);
116  for (unsigned int i=0; i != n_dofs; ++i)
117  old_elem_solution(i) =
119 
120  // Local time derivative of solution
121  context.get_elem_solution_rate() = context.get_elem_solution();
122  context.get_elem_solution_rate() -= old_elem_solution;
124  context.get_elem_solution_rate() *=
126 
127  // Our first evaluations are at the final elem_solution
128  context.elem_solution_derivative = 1.0;
129 
130  // If a fixed solution is requested, we'll use the elem_solution
131  // at the new timestep
132  // FIXME - should this be the theta solution instead?
134  context.get_elem_fixed_solution() = context.get_elem_solution();
135 
136  context.fixed_solution_derivative = 1.0;
137 
138  // We need to save the old jacobian and old residual since we'll be
139  // multiplying some of the new contributions by theta or 1-theta
140  DenseMatrix<Number> old_elem_jacobian(n_dofs, n_dofs);
141  DenseVector<Number> old_elem_residual(n_dofs);
142  old_elem_residual.swap(context.get_elem_residual());
143  if (request_jacobian)
144  old_elem_jacobian.swap(context.get_elem_jacobian());
145 
146  // Local time derivative of solution
147  context.get_elem_solution_rate() = context.get_elem_solution();
148  context.get_elem_solution_rate() -= old_elem_solution;
150  context.get_elem_solution_rate() *=
152 
153  // If we are asked to compute residuals for second order variables,
154  // we also populate the acceleration part so the user can use that.
156  this->prepare_accel(context);
157 
158  // Move the mesh into place first if necessary, set t = t_{n+1}
159  (context.*reinit_func)(1.);
160 
161  // First, evaluate time derivative at the new timestep.
162  // The element should already be in the proper place
163  // even for a moving mesh problem.
164  bool jacobian_computed =
165  (_system.get_physics()->*time_deriv)(request_jacobian, context);
166 
167  // Next, evaluate the mass residual at the new timestep
168 
169  jacobian_computed = (_system.get_physics()->*mass)(jacobian_computed, context) &&
170  jacobian_computed;
171 
172  // If we have second-order variables, we need to get damping terms
173  // and the velocity equations
175  {
176  jacobian_computed = (_system.get_physics()->*damping)(jacobian_computed, context) &&
177  jacobian_computed;
178 
179  jacobian_computed = this->compute_second_order_eqns(jacobian_computed, context) &&
180  jacobian_computed;
181  }
182 
183  // Add the constraint term
184  jacobian_computed = (_system.get_physics()->*constraint)(jacobian_computed, context) &&
185  jacobian_computed;
186 
187  // The new solution's contribution is scaled by theta
188  context.get_elem_residual() *= theta;
189  context.get_elem_jacobian() *= theta;
190 
191  // Save the new solution's term
192  DenseMatrix<Number> elem_jacobian_newterm(n_dofs, n_dofs);
193  DenseVector<Number> elem_residual_newterm(n_dofs);
194  elem_residual_newterm.swap(context.get_elem_residual());
195  if (request_jacobian)
196  elem_jacobian_newterm.swap(context.get_elem_jacobian());
197 
198  // Add the time-dependent term for the old solution
199 
200  // Make sure elem_solution is set up for elem_reinit to use
201  // Move elem_->old_, old_->elem_
202  context.get_elem_solution().swap(old_elem_solution);
203  context.elem_solution_derivative = 0.0;
204 
205  // Move the mesh into place if necessary, set t = t_{n}
206  (context.*reinit_func)(0.);
207 
208  jacobian_computed =
209  (_system.get_physics()->*time_deriv)(jacobian_computed, context) &&
210  jacobian_computed;
211 
212  // Add the mass residual term for the old solution
213 
214  // Evaluating the mass residual at both old and new timesteps will be
215  // redundant in most problems but may be necessary for time accuracy
216  // or stability in moving mesh problems or problems with user-overridden
217  // mass_residual functions
218 
219  jacobian_computed =
220  (_system.get_physics()->*mass)(jacobian_computed, context) &&
221  jacobian_computed;
222 
223  // If we have second-order variables, we need to get damping terms
224  // and the velocity equations
226  {
227  jacobian_computed = (_system.get_physics()->*damping)(jacobian_computed, context) &&
228  jacobian_computed;
229 
230  jacobian_computed = this->compute_second_order_eqns(jacobian_computed, context) &&
231  jacobian_computed;
232  }
233 
234  // The old solution's contribution is scaled by (1-theta)
235  context.get_elem_residual() *= (1-theta);
236  context.get_elem_jacobian() *= (1-theta);
237 
238  // Restore the elem_solution
239  // Move elem_->elem_, old_->old_
240  context.get_elem_solution().swap(old_elem_solution);
241  context.elem_solution_derivative = 1;
242 
243  // Restore the elem position if necessary, set t = t_{n+1}
244  (context.*reinit_func)(1.);
245 
246  // Add back (or restore) the old residual/jacobian
247  context.get_elem_residual() += old_elem_residual;
248  if (request_jacobian)
249  {
250  if (jacobian_computed)
251  context.get_elem_jacobian() += old_elem_jacobian;
252  else
253  context.get_elem_jacobian().swap(old_elem_jacobian);
254  }
255 
256  // Add the saved new-solution terms
257  context.get_elem_residual() += elem_residual_newterm;
258  if (jacobian_computed)
259  context.get_elem_jacobian() += elem_jacobian_newterm;
260 
261  return jacobian_computed;
262 }
263 
265 {
266  // We use a numerical integration scheme consistent with the theta used for the timesolver.
267 
268  // Zero out the system.qoi vector
269  for (auto j : make_range(_system.n_qois()))
270  {
271  _system.set_qoi(j, 0.0);
272  }
273 
274  // Left and right side contributions
275  std::vector<Number> left_contribution(_system.n_qois(), 0.0);
276  Number time_left = 0.0;
277  std::vector<Number> right_contribution(_system.n_qois(), 0.0);
278  Number time_right = 0.0;
279 
280  time_left = _system.time;
281 
282  // Base class assumes a direct steady evaluation
283  this->_system.assemble_qoi();
284 
285  // Also get the spatially integrated errors for all the QoIs in the QoI set
286  for (auto j : make_range(_system.n_qois()))
287  {
288  left_contribution[j] = _system.get_qoi_value(j);
289  }
290 
291  // Advance to t_j+1
293 
294  time_right = _system.time;
295 
296  // Load the solution at the next timestep
298 
299  // Zero out the system.qoi vector
300  for (auto j : make_range(_system.n_qois()))
301  {
302  _system.set_qoi(j, 0.0);
303  }
304 
305  // Base class assumes a direct steady evaluation
306  this->_system.assemble_qoi();
307 
308  for(auto j : make_range(_system.n_qois()))
309  {
310  right_contribution[j] = _system.get_qoi_value(j);
311  }
312 
313  // Combine the left and right side contributions as per the specified theta
314  // theta = 0.5 (Crank-Nicholson) gives the trapezoidal rule.
315  for (auto j : make_range(_system.n_qois()))
316  {
317  _system.set_qoi(j, ( ( ((1.0 - theta)*left_contribution[j]) + (theta*right_contribution[j]) )/2.0 )*(time_right - time_left));
318  }
319 }
320 
321 #ifdef LIBMESH_ENABLE_AMR
322 void Euler2Solver::integrate_adjoint_refinement_error_estimate(AdjointRefinementEstimator & adjoint_refinement_error_estimator, ErrorVector & QoI_elementwise_error)
323 {
324  // Currently, we only support this functionality when Backward-Euler time integration is used.
325  if (theta != 1.0)
326  libmesh_not_implemented();
327 
328  // There are two possibilities regarding the integration rule we need to use for time integration.
329  // If we have a instantaneous QoI, then we need to use a left sided Riemann sum, otherwise the trapezoidal rule for temporally smooth QoIs.
330 
331  // Create left and right error estimate vectors of the right size
332  std::vector<Number> qoi_error_estimates_left(_system.n_qois());
333  std::vector<Number> qoi_error_estimates_right(_system.n_qois());
334 
335  // Get t_j
336  Real time_left = _system.time;
337 
338  // Get f(t_j)
339  ErrorVector QoI_elementwise_error_left;
340 
341  // If we are at the very initial step, the error contribution is zero,
342  // otherwise the old ajoint vector has been filled and we are the left end
343  // of a subsequent timestep or sub-timestep
344  if(old_adjoints[0] != nullptr)
345  {
346  // For evaluating the residual, we need to use the deltat that was used
347  // to get us to this solution, so we save the current deltat as next_step_deltat
348  // and set _system.deltat to the last completed deltat.
351 
352  // The adjoint error estimate expression for a backwards facing step
353  // scheme needs the adjoint for the last time instant, so save the current adjoint for future use
354  for (auto j : make_range(_system.n_qois()))
355  {
356  // Swap for residual weighting
358  }
359 
360  _system.update();
361 
362  // The residual has to be evaluated at the last time
364 
365  adjoint_refinement_error_estimator.estimate_error(_system, QoI_elementwise_error_left);
366 
367  // Shift the time back
369 
370  // Swap back the current and old adjoints
371  for (auto j : make_range(_system.n_qois()))
372  {
374  }
375 
376  // Set the system deltat back to what it should be to march to the next time
378 
379  }
380  else
381  {
382  for(auto i : index_range(QoI_elementwise_error))
383  QoI_elementwise_error_left[i] = 0.0;
384  }
385 
386  // Also get the left side contributions for the spatially integrated errors for all the QoIs in the QoI set
387  for (auto j : make_range(_system.n_qois()))
388  {
389  // Skip this QoI if not in the QoI Set
390  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
391  {
392  // If we are at the initial time, the error contribution is zero
394  {
395  qoi_error_estimates_left[j] = adjoint_refinement_error_estimator.get_global_QoI_error_estimate(j);
396  }
397  else
398  {
399  qoi_error_estimates_left[j] = 0.0;
400  }
401  }
402  }
403 
404  // Advance to t_j+1
406 
407  // Get t_j+1
408  Real time_right = _system.time;
409 
410  // We will need to use the last step deltat for the weighted residual evaluation
412 
413  // The adjoint error estimate expression for a backwards facing step
414  // scheme needs the adjoint for the last time instant, so save the current adjoint for future use
415  for (auto j : make_range(_system.n_qois()))
416  {
418  }
419 
420  // Retrieve the state and adjoint vectors for the next time instant
422 
423  // Swap for residual weighting
424  for (auto j : make_range(_system.n_qois()))
425  {
427  }
428 
429  // Swap out the deltats as we did for the left side
432 
433  // Get f(t_j+1)
434  ErrorVector QoI_elementwise_error_right;
435 
436  _system.update();
437 
438  // The residual has to be evaluated at the last time
440 
441  adjoint_refinement_error_estimator.estimate_error(_system, QoI_elementwise_error_right);
442 
443  // Shift the time back
445 
446  // Set the system deltat back to what it needs to be able to march to the next time
448 
449  // Swap back now that the residual weighting is done
450  for (auto j : make_range(_system.n_qois()))
451  {
453  }
454 
455  // Also get the right side contributions for the spatially integrated errors for all the QoIs in the QoI set
456  for (auto j : make_range(_system.n_qois()))
457  {
458  // Skip this QoI if not in the QoI Set
459  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
460  {
461  qoi_error_estimates_right[j] = adjoint_refinement_error_estimator.get_global_QoI_error_estimate(j);
462  }
463  }
464 
465  // Error contribution from this timestep
466  for (auto i : index_range(QoI_elementwise_error))
467  QoI_elementwise_error[i] = float(((QoI_elementwise_error_right[i] + QoI_elementwise_error_left[i])/2)
468  * (time_right - time_left));
469 
470  // QoI set spatially integrated errors contribution from this timestep
471  for (auto j : make_range(_system.n_qois()))
472  {
473  // Skip this QoI if not in the QoI Set
474  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
475  {
476  _system.set_qoi_error_estimate(j, ( (1.0 - theta)*qoi_error_estimates_left[j] + theta*qoi_error_estimates_right[j] )*last_step_deltat);
477  }
478  }
479 
480 }
481 #endif // LIBMESH_ENABLE_AMR
482 
483 } // namespace libMesh
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition: system.h:1595
virtual bool side_constraint(bool request_jacobian, DiffContext &)
Adds the constraint contribution on side of elem to elem_residual.
Definition: diff_physics.h:195
QoISet & qoi_set()
Access to the QoISet (default: weight all QoIs equally) to use when computing errors.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
bool compute_second_order_eqns(bool compute_jacobian, DiffContext &c)
If there are second order variables, then we need to compute their residual equations and correspondi...
Real fixed_solution_derivative
The derivative of elem_fixed_solution with respect to the nonlinear solution, for use by systems cons...
Definition: diff_context.h:517
void swap(DenseMatrix< T > &other_matrix)
STL-like swap method.
Definition: dense_matrix.h:865
This class provides all data required for a physics package (e.g.
Definition: diff_context.h:55
void prepare_accel(DiffContext &context)
If there are second order variables in the system, then we also prepare the accel for those variables...
virtual bool _general_residual(bool request_jacobian, DiffContext &, ResFuncType mass, ResFuncType damping, ResFuncType time_deriv, ResFuncType constraint, ReinitFuncType reinit, bool compute_second_order_eqns)
This method is the underlying implementation of the public residual methods.
Euler2Solver(sys_type &s)
Constructor.
Definition: euler2_solver.C:33
const DenseVector< Number > & get_elem_fixed_solution() const
Accessor for element fixed solution.
Definition: diff_context.h:210
This class implements a "brute force" goal-oriented error estimator which computes an estimate of err...
static constexpr Real TOLERANCE
const DenseVector< Number > & get_elem_solution_rate() const
Accessor for element solution rate of change w.r.t.
Definition: diff_context.h:144
Number get_qoi_value(unsigned int qoi_index) const
Definition: system.C:2334
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition: system.h:2516
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
Definition: error_vector.h:50
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 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
ADRealEigenVector< T, D, asd > sqrt(const ADRealEigenVector< T, D, asd > &)
Definition: type_vector.h:53
The libMesh namespace provides an interface to certain functionality in the library.
virtual ~Euler2Solver()
Destructor.
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
void swap(DenseVector< T > &other_vector)
STL-like swap method.
Definition: dense_vector.h:365
virtual bool mass_residual(bool request_jacobian, DiffContext &)
Subtracts a mass vector contribution on elem from elem_residual.
Definition: diff_physics.h:302
This class provides a specific system class.
Definition: diff_system.h:54
ADRealEigenVector< T, D, asd > abs(const ADRealEigenVector< T, D, asd > &)
Definition: type_vector.h:57
bool has_index(std::size_t) const
Return whether or not this index is in the set to be calculated.
Definition: qoi_set.h:224
sys_type & _system
A reference to the system we are solving.
Definition: time_solver.h:312
std::vector< std::unique_ptr< NumericVector< Number > > > old_adjoints
A vector of pointers to vectors holding the adjoint solution at the last time step.
const std::set< unsigned int > & get_second_order_vars() const
Definition: diff_physics.h:519
Generic class from which first order UnsteadySolvers should subclass.
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
virtual bool element_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; element_time_derivative() and element_constraint() to bui...
Definition: euler2_solver.C:54
virtual void retrieve_timestep() override
This method retrieves all the stored solutions at the current system.time.
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time...
Definition: diff_system.h:278
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:1543
Real elem_solution_derivative
The derivative of elem_solution with respect to the current nonlinear solution.
Definition: diff_context.h:496
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; nonlocal_time_derivative() and nonlocal_constraint() to b...
Definition: euler2_solver.C:86
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
Real theta
The value for the theta method to employ: 1.0 corresponds to backwards Euler, 0.0 corresponds to forw...
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...
Definition: euler2_solver.C:71
virtual void integrate_qoi_timestep() override
A method to integrate the system::QoI functionals.
Number & get_global_QoI_error_estimate(unsigned int qoi_index)
This is an accessor function to access the computed global QoI error estimates.
bool have_second_order_scalar_vars() const
Check for any second order vars that are also belong to FEFamily::SCALAR.
Definition: diff_system.C:335
virtual void estimate_error(const System &system, ErrorVector &error_per_cell, const NumericVector< Number > *solution_vector=nullptr, bool estimate_parent_error=false) override
This function does uniform refinements and an adjoint solve to get an adjoint solution on each cell...
const DifferentiablePhysics * get_physics() const
Definition: diff_system.h:181
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
Definition: diff_context.h:242
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:493
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
void set_qoi_error_estimate(unsigned int qoi_index, Number qoi_error_estimate)
Definition: system.C:2354
virtual void swap(NumericVector< T > &v)
Swaps the contents of this with v.
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
virtual void assemble_qoi(const QoISet &qoi_indices=QoISet()) override
Prepares qoi for quantity of interest assembly, then calls user qoi function.
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:134
virtual void integrate_adjoint_refinement_error_estimate(AdjointRefinementEstimator &adjoint_refinement_error_estimator, ErrorVector &QoI_elementwise_error) override
A method to compute the adjoint refinement error estimate at the current timestep.
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition: system.C:1193
virtual unsigned int size() const override final
Definition: dense_vector.h:104
void set_qoi(unsigned int qoi_index, Number qoi_value)
Definition: system.C:2326
Real last_step_deltat
We will need to move the system.time around to ensure that residuals are built with the right deltat ...
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
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
virtual Real error_order() const override
Error convergence order: 2 for Crank-Nicolson, 1 otherwise.
Definition: euler2_solver.C:44
virtual bool nonlocal_mass_residual(bool request_jacobian, DiffContext &c)
Subtracts any nonlocal mass vector contributions (e.g.
Definition: diff_physics.C:57
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:111
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