libMesh
euler_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 #include "libmesh/euler_solver.h"
20 
21 #include "libmesh/adjoint_refinement_estimator.h"
22 #include "libmesh/diff_system.h"
23 #include "libmesh/error_vector.h"
24 #include "libmesh/int_range.h"
25 #include "libmesh/numeric_vector.h"
26 
27 namespace libMesh
28 {
29 
30 
31 
33  : FirstOrderUnsteadySolver(s), theta(1.)
34 {
35 }
36 
37 
38 
39 EulerSolver::~EulerSolver () = default;
40 
41 
42 
44 {
45  if (theta == 0.5)
46  return 2.;
47  return 1.;
48 }
49 
50 
51 
52 
53 bool EulerSolver::element_residual (bool request_jacobian,
54  DiffContext & context)
55 {
57 
58  return this->_general_residual(request_jacobian,
59  context,
66 }
67 
68 
69 
70 bool EulerSolver::side_residual (bool request_jacobian,
71  DiffContext & context)
72 {
73  return this->_general_residual(request_jacobian,
74  context,
80  false);
81 }
82 
83 
84 
85 bool EulerSolver::nonlocal_residual (bool request_jacobian,
86  DiffContext & context)
87 {
89 
90  return this->_general_residual(request_jacobian,
91  context,
98 }
99 
100 
101 
102 bool EulerSolver::_general_residual (bool request_jacobian,
103  DiffContext & context,
104  ResFuncType mass,
105  ResFuncType damping,
106  ResFuncType time_deriv,
107  ResFuncType constraint,
108  ReinitFuncType reinit_func,
109  bool compute_second_order_eqns)
110 {
111  unsigned int n_dofs = context.get_elem_solution().size();
112 
113  // We might need to save the old jacobian in case one of our physics
114  // terms later is unable to update it analytically.
115  DenseMatrix<Number> old_elem_jacobian(n_dofs, n_dofs);
116  if (request_jacobian)
117  old_elem_jacobian.swap(context.get_elem_jacobian());
118 
119  // Local nonlinear solution at old timestep
120  DenseVector<Number> old_elem_solution(n_dofs);
121  for (unsigned int i=0; i != n_dofs; ++i)
122  old_elem_solution(i) =
124 
125  // Local time derivative of solution
126  context.get_elem_solution_rate() = context.get_elem_solution();
127  context.get_elem_solution_rate() -= old_elem_solution;
129  context.get_elem_solution_rate() *=
131 
132  // If we are asked to compute residuals for second order variables,
133  // we also populate the acceleration part so the user can use that.
135  this->prepare_accel(context);
136 
137  // Local nonlinear solution at time t_theta
138  DenseVector<Number> theta_solution(context.get_elem_solution());
139  theta_solution *= theta;
140  theta_solution.add(1. - theta, old_elem_solution);
141 
144 
145  // If a fixed solution is requested, we'll use theta_solution
147  context.get_elem_fixed_solution() = theta_solution;
148 
149  // Move theta_->elem_, elem_->theta_
150  context.get_elem_solution().swap(theta_solution);
151 
152  // Move the mesh into place first if necessary, set t = t_{\theta}
153  (context.*reinit_func)(theta);
154 
155  // Get the time derivative at t_theta
156  bool jacobian_computed =
157  (_system.get_physics()->*time_deriv)(request_jacobian, context);
158 
159  jacobian_computed = (_system.get_physics()->*mass)(jacobian_computed, context) &&
160  jacobian_computed;
161 
162  // If we have second-order variables, we need to get damping terms
163  // and the velocity equations
165  {
166  jacobian_computed = (_system.get_physics()->*damping)(jacobian_computed, context) &&
167  jacobian_computed;
168 
169  jacobian_computed = this->compute_second_order_eqns(jacobian_computed, context) &&
170  jacobian_computed;
171  }
172 
173  // Restore the elem position if necessary, set t = t_{n+1}
174  (context.*reinit_func)(1);
175 
176  // Move elem_->elem_, theta_->theta_
177  context.get_elem_solution().swap(theta_solution);
178  context.elem_solution_derivative = 1;
179 
180  // Add the constraint term
181  jacobian_computed = (_system.get_physics()->*constraint)(jacobian_computed, context) &&
182  jacobian_computed;
183 
184  // Add back (or restore) the old jacobian
185  if (request_jacobian)
186  {
187  if (jacobian_computed)
188  context.get_elem_jacobian() += old_elem_jacobian;
189  else
190  context.get_elem_jacobian().swap(old_elem_jacobian);
191  }
192 
193  return jacobian_computed;
194 }
195 
197 {
198  // We use a numerical integration scheme consistent with the theta used for the timesolver.
199 
200  // Zero out the system.qoi vector
201  for (auto j : make_range(_system.n_qois()))
202  {
203  _system.set_qoi(j, 0.0);
204  }
205 
206  // Left and right side contributions
207  std::vector<Number> left_contribution(_system.n_qois(), 0.0);
208  Number time_left = 0.0;
209  std::vector<Number> right_contribution(_system.n_qois(), 0.0);
210  Number time_right = 0.0;
211 
212  time_left = _system.time;
213 
214  // Base class assumes a direct steady evaluation
215  this->_system.assemble_qoi();
216 
217  // Also get the spatially integrated errors for all the QoIs in the QoI set
218  for (auto j : make_range(_system.n_qois()))
219  {
220  left_contribution[j] = _system.get_qoi_value(j);
221  }
222 
223  // Advance to t_j+1
225 
226  time_right = _system.time;
227 
228  // Load the solution at the next timestep
230 
231  // Zero out the system.qoi vector
232  for (auto j : make_range(_system.n_qois()))
233  {
234  _system.set_qoi(j, 0.0);
235  }
236 
237  // Base class assumes a direct steady evaluation
238  this->_system.assemble_qoi();
239 
240  for(auto j : make_range(_system.n_qois()))
241  {
242  right_contribution[j] = _system.get_qoi_value(j);
243  }
244 
245  // Combine the left and right side contributions as per the specified theta
246  // theta = 0.5 (Crank-Nicholson) gives the trapezoidal rule.
247  for (auto j : make_range(_system.n_qois()))
248  {
249  _system.set_qoi(j, ( ((1.0 - theta)*left_contribution[j]) + (theta*right_contribution[j]) )*(time_right - time_left));
250  }
251 }
252 
253 #ifdef LIBMESH_ENABLE_AMR
254 void EulerSolver::integrate_adjoint_refinement_error_estimate(AdjointRefinementEstimator & adjoint_refinement_error_estimator, ErrorVector & QoI_elementwise_error)
255 {
256  // Currently, we only support this functionality when Backward-Euler time integration is used.
257  if (theta != 1.0)
258  libmesh_not_implemented();
259 
260  // There are two possibilities regarding the integration rule we need to use for time integration.
261  // If we have a instantaneous QoI, then we need to use a left sided Riemann sum, otherwise the trapezoidal rule for temporally smooth QoIs.
262 
263  // Create left and right error estimate vectors of the right size
264  std::vector<Number> qoi_error_estimates_left(_system.n_qois());
265  std::vector<Number> qoi_error_estimates_right(_system.n_qois());
266 
267  // Get t_j
268  Real time_left = _system.time;
269 
270  // Get f(t_j)
271  ErrorVector QoI_elementwise_error_left;
272 
273  // If we are at the very initial step, the error contribution is zero,
274  // otherwise the old ajoint vector has been filled and we are the left end
275  // of a subsequent timestep or sub-timestep
276  if(old_adjoints[0] != nullptr)
277  {
278  // For evaluating the residual, we need to use the deltat that was used
279  // to get us to this solution, so we save the current deltat as next_step_deltat
280  // and set _system.deltat to the last completed deltat.
283 
284  // The adjoint error estimate expression for a backwards facing step
285  // scheme needs the adjoint for the last time instant, so save the current adjoint for future use
286  for (auto j : make_range(_system.n_qois()))
287  {
288  // Swap for residual weighting
290  }
291 
292  _system.update();
293 
294  // The residual has to be evaluated at the last time
296 
297  adjoint_refinement_error_estimator.estimate_error(_system, QoI_elementwise_error_left);
298 
299  // Shift the time back
301 
302  // Swap back the current and old adjoints
303  for (auto j : make_range(_system.n_qois()))
304  {
306  }
307 
308  // Set the system deltat back to what it should be to march to the next time
310 
311  }
312  else
313  {
314  for (auto i : index_range(QoI_elementwise_error))
315  QoI_elementwise_error_left[i] = 0.0;
316  }
317 
318  // Also get the left side contributions for the spatially integrated errors for all the QoIs in the QoI set
319  for (auto j : make_range(_system.n_qois()))
320  {
321  // Skip this QoI if not in the QoI Set
322  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
323  {
324  // If we are at the initial time, the error contribution is zero
326  {
327  qoi_error_estimates_left[j] = adjoint_refinement_error_estimator.get_global_QoI_error_estimate(j);
328  }
329  else
330  {
331  qoi_error_estimates_left[j] = 0.0;
332  }
333  }
334  }
335 
336  // Advance to t_j+1
338 
339  // Get t_j+1
340  Real time_right = _system.time;
341 
342  // We will need to use the last step deltat for the weighted residual evaluation
344 
345  // The adjoint error estimate expression for a backwards facing step
346  // scheme needs the adjoint for the last time instant, so save the current adjoint for future use
347  for (auto j : make_range(_system.n_qois()))
348  {
350  }
351 
352  // Retrieve the state and adjoint vectors for the next time instant
354 
355  // Swap for residual weighting
356  for (auto j : make_range(_system.n_qois()))
357  {
359  }
360 
361  // Swap out the deltats as we did for the left side
364 
365  // Get f(t_j+1)
366  ErrorVector QoI_elementwise_error_right;
367 
368  _system.update();
369 
370  // The residual has to be evaluated at the last time
372 
373  adjoint_refinement_error_estimator.estimate_error(_system, QoI_elementwise_error_right);
374 
375  // Shift the time back
377 
378  // Set the system deltat back to what it needs to be able to march to the next time
380 
381  // Swap back now that the residual weighting is done
382  for (auto j : make_range(_system.n_qois()))
383  {
385  }
386 
387  // Also get the right side contributions for the spatially integrated errors for all the QoIs in the QoI set
388  for (auto j : make_range(_system.n_qois()))
389  {
390  // Skip this QoI if not in the QoI Set
391  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
392  {
393  qoi_error_estimates_right[j] = adjoint_refinement_error_estimator.get_global_QoI_error_estimate(j);
394  }
395  }
396 
397  // Error contribution from this timestep
398  for (auto i : index_range(QoI_elementwise_error))
399  QoI_elementwise_error[i] = float(((QoI_elementwise_error_right[i] + QoI_elementwise_error_left[i])/2)
400  * (time_right - time_left));
401 
402  // QoI set spatially integrated errors contribution from this timestep
403  for (auto j : make_range(_system.n_qois()))
404  {
405  // Skip this QoI if not in the QoI Set
406  if (adjoint_refinement_error_estimator.qoi_set().has_index(j))
407  {
408  _system.set_qoi_error_estimate(j, ( (1.0 - theta)*qoi_error_estimates_left[j] + theta*qoi_error_estimates_right[j] )*last_step_deltat);
409  }
410  }
411 
412 }
413 #endif // LIBMESH_ENABLE_AMR
414 
415 } // 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.
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; nonlocal_time_derivative() and nonlocal_constraint() to b...
Definition: euler_solver.C:85
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
virtual ~EulerSolver()
Destructor.
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
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:455
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 void integrate_qoi_timestep() override
A method to integrate the system::QoI functionals.
Definition: euler_solver.C:196
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
Real theta
The value for the theta method to employ: 1.0 corresponds to backwards Euler, 0.0 corresponds to forw...
Definition: euler_solver.h:117
virtual bool element_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics&#39; element_time_derivative() and element_constraint() to bui...
Definition: euler_solver.C:53
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
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.
Definition: euler_solver.C:102
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 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.
Definition: euler_solver.C:254
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.
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 Real error_order() const override
Error convergence order: 2 for Crank-Nicolson, 1 otherwise.
Definition: euler_solver.C:43
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
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Definition: diff_context.h:363
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
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition: system.C:493
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: euler_solver.C:70
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
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 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
EulerSolver(sys_type &s)
Constructor.
Definition: euler_solver.C:32
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