libMesh
Loading...
Searching...
No Matches
euler2_solver.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
28namespace libMesh
29{
30
31
32
37
38
39
41
42
43
45{
46 if (theta == 0.5)
47 return 2.;
48 return 1.;
49}
50
51
52
53
68
69
70
83
84
85
100
101
102
103bool 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
322void 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
393 if(std::abs(_system.time) > TOLERANCE*sqrt(TOLERANCE))
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
This class implements a "brute force" goal-oriented error estimator which computes an estimate of err...
QoISet & qoi_set()
Access to the QoISet (default: weight all QoIs equally) to use when computing errors.
Number & get_global_QoI_error_estimate(unsigned int qoi_index)
This is an accessor function to access the computed global QoI error estimates.
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,...
Defines a dense matrix for use in Finite Element-type computations.
void swap(DenseMatrix< T > &other_matrix)
STL-like swap method.
Defines a dense vector for use in Finite Element-type computations.
void swap(DenseVector< T > &other_vector)
STL-like swap method.
virtual unsigned int size() const override final
This class provides all data required for a physics package (e.g.
Real elem_solution_derivative
The derivative of elem_solution with respect to the current nonlinear solution.
const DenseVector< Number > & get_elem_solution_rate() const
Accessor for element solution rate of change w.r.t.
virtual void elem_reinit(Real)
Gives derived classes the opportunity to reinitialize data (FE objects in FEMSystem,...
virtual void elem_side_reinit(Real)
Gives derived classes the opportunity to reinitialize data needed for a side integration at a new poi...
const std::vector< dof_id_type > & get_dof_indices() const
Accessor for element dof indices.
Real elem_solution_rate_derivative
The derivative of elem_solution_rate with respect to the current nonlinear solution,...
const DenseVector< Number > & get_elem_solution() const
Accessor for element solution.
Real fixed_solution_derivative
The derivative of elem_fixed_solution with respect to the nonlinear solution, for use by systems cons...
const DenseVector< Number > & get_elem_fixed_solution() const
Accessor for element fixed solution.
virtual void nonlocal_reinit(Real)
Gives derived classes the opportunity to reinitialize data needed for nonlocal calculations at a new ...
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
virtual bool element_constraint(bool request_jacobian, DiffContext &)
Adds the constraint contribution on elem to elem_residual.
virtual bool side_mass_residual(bool request_jacobian, DiffContext &)
Subtracts a mass vector contribution on side of elem from elem_residual.
virtual bool side_time_derivative(bool request_jacobian, DiffContext &)
Adds the time derivative contribution on side of elem to elem_residual.
const std::set< unsigned int > & get_second_order_vars() const
virtual bool side_constraint(bool request_jacobian, DiffContext &)
Adds the constraint contribution on side of elem to elem_residual.
virtual bool nonlocal_constraint(bool request_jacobian, DiffContext &)
Adds any nonlocal constraint contributions (e.g.
virtual bool nonlocal_time_derivative(bool request_jacobian, DiffContext &)
Adds any nonlocal time derivative contributions (e.g.
virtual bool nonlocal_damping_residual(bool request_jacobian, DiffContext &)
Subtracts any nonlocal damping vector contributions (e.g.
virtual bool damping_residual(bool request_jacobian, DiffContext &)
Subtracts a damping vector contribution on elem from elem_residual.
bool _eulerian_time_deriv(bool request_jacobian, DiffContext &)
This method simply combines element_time_derivative() and eulerian_residual(), which makes its addres...
virtual bool side_damping_residual(bool request_jacobian, DiffContext &)
Subtracts a damping vector contribution on side of elem from elem_residual.
virtual bool mass_residual(bool request_jacobian, DiffContext &)
Subtracts a mass vector contribution on elem from elem_residual.
virtual bool nonlocal_mass_residual(bool request_jacobian, DiffContext &c)
Subtracts any nonlocal mass vector contributions (e.g.
This class provides a specific system class.
Definition diff_system.h:57
const DifferentiablePhysics * get_physics() const
Real deltat
For time-dependent problems, this is the amount delta t to advance the solution in time.
bool have_second_order_scalar_vars() const
Check for any second order vars that are also belong to FEFamily::SCALAR.
The ErrorVector is a specialization of the StatisticsVector for error data computed on a finite eleme...
virtual ~Euler2Solver()
Destructor.
Euler2Solver(sys_type &s)
Constructor.
Real theta
The value for the theta method to employ: 1.0 corresponds to backwards Euler, 0.0 corresponds to forw...
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.
virtual Real error_order() const override
Error convergence order: 2 for Crank-Nicolson, 1 otherwise.
virtual bool element_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics' element_time_derivative() and element_constraint() to bui...
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.
virtual bool nonlocal_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics' nonlocal_time_derivative() and nonlocal_constraint() to b...
virtual bool side_residual(bool request_jacobian, DiffContext &) override
This method uses the DifferentiablePhysics' side_time_derivative() and side_constraint() to build a f...
virtual void integrate_qoi_timestep() override
A method to integrate the system::QoI functionals.
virtual void assemble_qoi(const QoISet &qoi_indices=QoISet()) override
Prepares qoi for quantity of interest assembly, then calls user qoi function.
Generic class from which first order UnsteadySolvers should subclass.
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...
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 swap(NumericVector< T > &v)
Swaps the contents of this with v.
virtual std::unique_ptr< NumericVector< T > > clone() const =0
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
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:1625
void set_qoi(unsigned int qoi_index, Number qoi_value)
Definition system.C:2176
Number get_qoi_value(unsigned int qoi_index) const
Definition system.C:2184
Real time
For time-dependent problems, this is the time t at the beginning of the current timestep.
Definition system.h:1677
unsigned int n_qois() const
Number of currently active quantities of interest.
Definition system.h:2562
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
void set_qoi_error_estimate(unsigned int qoi_index, Number qoi_error_estimate)
Definition system.C:2204
NumericVector< Number > & get_adjoint_solution(unsigned int i=0)
Definition system.C:1232
sys_type & _system
A reference to the system we are solving.
std::vector< std::unique_ptr< NumericVector< Number > > > old_adjoints
A vector of pointers to vectors holding the adjoint solution at the last time step.
virtual void retrieve_timestep() override
This method retrieves all the stored solutions at the current system.time.
Number old_nonlinear_solution(const dof_id_type global_dof_number) const
Real last_step_deltat
We will need to move the system.time around to ensure that residuals are built with the right deltat ...
The libMesh namespace provides an interface to certain functionality in the library.
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:153
static constexpr Real TOLERANCE
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
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:176