libMesh
Loading...
Searching...
No Matches
coupled_system.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// Example includes
20#include "coupled_system.h"
21
22// libMesh includes
23#include "libmesh/dirichlet_boundaries.h"
24#include "libmesh/dof_map.h"
25#include "libmesh/fe_base.h"
26#include "libmesh/fe_interface.h"
27#include "libmesh/fem_context.h"
28#include "libmesh/getpot.h"
29#include "libmesh/mesh.h"
30#include "libmesh/parallel.h"
31#include "libmesh/quadrature.h"
32#include "libmesh/string_to_enum.h"
33#include "libmesh/zero_function.h"
34
35// C++ includes
36#include <functional> // std::reference_wrapper
37#include <memory>
38
39// Bring in everything from the libMesh namespace
40using namespace libMesh;
41
42// Function to set the Dirichlet boundary function for the inlet boundary velocities
43class BdyFunction : public FunctionBase<Number>
44{
45public:
46 BdyFunction (unsigned int u_var,
47 unsigned int v_var,
48 int sign) :
49 _u_var(u_var),
50 _v_var(v_var),
51 _sign(sign)
52 { this->_initialized = true; }
53
54 virtual Number operator() (const Point &,
55 const Real = 0)
56 { libmesh_not_implemented(); }
57
58 virtual void operator() (const Point & p,
59 const Real,
60 DenseVector<Number> & output)
61 {
62 output.resize(2);
63 output.zero();
64 const Real y=p(1);
65 // Set the parabolic inflow boundary conditions at stations 0 & 1
66 output(_u_var) = (_sign)*((y-2) * (y-3));
67 output(_v_var) = 0;
68 }
69
70 virtual std::unique_ptr<FunctionBase<Number>> clone() const
71 { return std::make_unique<BdyFunction>(_u_var, _v_var, int(_sign)); }
72
73private:
74 const unsigned int _u_var, _v_var;
75 const Real _sign;
76};
77
78
80{
81 // Check the input file for Reynolds number, application type,
82 // approximation type
83 GetPot infile("coupled_system.in");
84 Peclet = infile("Peclet", 1.);
85 unsigned int pressure_p = infile("pressure_p", 1);
86 std::string fe_family = infile("fe_family", std::string("LAGRANGE"));
87
88 // LBB needs better-than-quadratic velocities for better-than-linear
89 // pressures, and libMesh needs non-Lagrange elements for
90 // better-than-quadratic velocities.
91 libmesh_assert((pressure_p == 1) || (fe_family != "LAGRANGE"));
92
93 FEFamily fefamily = Utility::string_to_enum<FEFamily>(fe_family);
94
95 // Add the velocity components "u" & "v". They
96 // will be approximated using second-order approximation.
97 u_var = this->add_variable ("u", static_cast<Order>(pressure_p+1),
98 fefamily);
99 v_var = this->add_variable ("v", static_cast<Order>(pressure_p+1),
100 fefamily);
101
102 // Add the pressure variable "p". This will
103 // be approximated with a first-order basis,
104 // providing an LBB-stable pressure-velocity pair.
105 p_var = this->add_variable ("p", static_cast<Order>(pressure_p),
106 fefamily);
107
108 // Add the Concentration variable "C". They will
109 // be approximated using second-order approximation, the same as the velocity components
110 C_var = this->add_variable ("C", static_cast<Order>(pressure_p+1),
111 fefamily);
112
113 // Tell the system to march velocity and concentration forward in
114 // time, with first order time derivatives, but leave pressure as a
115 // constraint only
116 this->time_evolving(u_var, 1);
117 this->time_evolving(v_var, 1);
118 this->time_evolving(C_var, 1);
119
120 // Useful debugging options
121 this->verify_analytic_jacobians = infile("verify_analytic_jacobians", 0.);
122
123 // Set Dirichlet boundary conditions
124 const boundary_id_type left_inlet_id = 0;
125
126 const boundary_id_type right_inlet_id = 1;
127 std::set<boundary_id_type> right_inlet_bdy;
128 right_inlet_bdy.insert(right_inlet_id);
129
130 const boundary_id_type outlets_id = 2;
131 std::set<boundary_id_type> outlets_bdy;
132 outlets_bdy.insert(outlets_id);
133
134 const boundary_id_type wall_id = 3;
135
136 // The zero and constant functions
139
140 // We need two boundary functions for the inlets, because the signs on the velocities
141 // will be different
142 int velocity_sign = 1;
143 BdyFunction inflow_left(u_var, v_var, -velocity_sign);
144 BdyFunction inflow_right(u_var, v_var, velocity_sign);
145
146#ifdef LIBMESH_ENABLE_DIRICHLET
147 // On the walls we will apply the no slip and no penetration boundary condition, u=0, v=0
149 (DirichletBoundary ({wall_id}, {u_var,v_var}, &zero));
150
151 // On the inlet (left), we apply parabolic inflow boundary conditions for the velocity, u = - (y-2)*(y-3), v=0
152 // and set C = 1
154 (DirichletBoundary ({left_inlet_id}, {u_var,v_var}, &inflow_left));
156 (DirichletBoundary ({left_inlet_id}, {C_var}, &one));
157
158 // On the inlet (right), we apply parabolic inflow boundary conditions for the velocity, u = (y-2)*(y-3), v=0
159 // and set C = 0
161 (DirichletBoundary (right_inlet_bdy, {u_var,v_var}, &inflow_right));
163 (DirichletBoundary (right_inlet_bdy, {C_var}, &zero));
164#endif // LIBMESH_ENABLE_DIRICHLET
165
166 // Note that the remaining boundary conditions are the natural boundary conditions for the concentration
167 // on the wall (grad(c) dot n = 0) and natural boundary conditions for the velocity and the concentration
168 // on the outlets ((grad(velocity) dot n - p n) dot t = 0, grad(C) dot n = 0)
169
170 // Do the parent's initialization after variables and boundary constraints are defined
171 FEMSystem::init_data();
172}
173
175{
176 FEMContext & c = cast_ref<FEMContext &>(context);
177
178 // We should prerequest all the data
179 // we will need to build the linear system.
180 // Note that the concentration and velocity components
181 // use the same basis.
182 FEBase * u_elem_fe = nullptr;
183 c.get_element_fe(u_var, u_elem_fe);
184 u_elem_fe->get_JxW();
185 u_elem_fe->get_phi();
186 u_elem_fe->get_dphi();
187 u_elem_fe->get_xyz();
188
189 FEBase * p_elem_fe = nullptr;
190 c.get_element_fe(p_var, p_elem_fe);
191 p_elem_fe->get_phi();
192
193 FEBase * side_fe = nullptr;
194 c.get_side_fe(u_var, side_fe);
195
196 side_fe->get_JxW();
197 side_fe->get_phi();
198 side_fe->get_xyz();
199
200 // Don't waste time on side computations for p
201 FEBase * p_side_fe = nullptr;
202 c.get_side_fe(p_var, p_side_fe);
203 p_side_fe->get_nothing();
204}
205
206
207bool CoupledSystem::element_time_derivative (bool request_jacobian,
208 DiffContext & context)
209{
210 FEMContext & c = cast_ref<FEMContext &>(context);
211
212 // First we get some references to cell-specific data that
213 // will be used to assemble the linear system.
214 FEBase * u_elem_fe = nullptr;
215 c.get_element_fe(u_var, u_elem_fe);
216
217 // Element Jacobian * quadrature weights for interior integration
218 const std::vector<Real> & JxW = u_elem_fe->get_JxW();
219
220 // The velocity shape functions at interior quadrature points.
221 const std::vector<std::vector<Real>> & phi = u_elem_fe->get_phi();
222
223 // The velocity shape function gradients at interior
224 // quadrature points.
225 const std::vector<std::vector<RealGradient>> & dphi = u_elem_fe->get_dphi();
226
227 // The pressure shape functions at interior
228 // quadrature points.
229 FEBase * p_elem_fe = nullptr;
230 c.get_element_fe(p_var, p_elem_fe);
231
232 const std::vector<std::vector<Real>> & psi = p_elem_fe->get_phi();
233
234 // The number of local degrees of freedom in each variable
235 const unsigned int n_p_dofs = c.n_dof_indices(p_var);
236 const unsigned int n_u_dofs = c.n_dof_indices(u_var);
237 libmesh_assert_equal_to (n_u_dofs, c.n_dof_indices(v_var));
238
239 // Stokes equation stiffness matrix diagonal blocks. There is no
240 // uv/vu coupling for linear Stokes.
241 std::reference_wrapper<DenseSubMatrix<Number>> Kdiag[2] =
242 {
245 };
246
247 // Stokes equation velocity-pressure coupling blocks
248 // Kup
249 // Kvp
250 std::reference_wrapper<DenseSubMatrix<Number>> B[2] =
251 {
254 };
255
256 // Stokes equation residuals
257 // Fu
258 // Fv
259 std::reference_wrapper<DenseSubVector<Number>> F[2] =
260 {
263 };
264
265 // Concentration/velocity coupling blocks
266 // KCu
267 // KCv
268 std::reference_wrapper<DenseSubMatrix<Number>> C[2] =
269 {
272 };
273
274 // Concentration diagonal block and residual
277
278 // Now we will build the element Jacobian and residual.
279 // Constructing the residual requires the solution and its
280 // gradient from the previous timestep. This must be
281 // calculated at each quadrature point by summing the
282 // solution degree-of-freedom values by the appropriate
283 // weight functions.
284 unsigned int n_qpoints = c.get_element_qrule().n_points();
285
286 // Store (grad_u, grad_v) at current Newton iterate
287 Gradient grad_uv[2];
288
289 // Velocity at current Newton iterate
291
292 for (unsigned int qp=0; qp != n_qpoints; qp++)
293 {
294 // Compute the solution & its gradient at the current Newton iterate
295 Number p = c.interior_value(p_var, qp);
296 Gradient grad_C = c.interior_gradient(C_var, qp);
297
298 c.interior_value(u_var, qp, U(0));
299 c.interior_value(v_var, qp, U(1));
300 c.interior_gradient(u_var, qp, grad_uv[0]);
301 c.interior_gradient(v_var, qp, grad_uv[1]);
302
303 // First, an i-loop over the velocity degrees of freedom.
304 // We know that n_u_dofs == n_v_dofs so we can compute contributions
305 // for both at the same time.
306 for (unsigned int i=0; i != n_u_dofs; i++)
307 {
308 // Stokes equations residuals
309 for (unsigned int d=0; d<2; ++d)
310 F[d](i) += JxW[qp] *
311 (p*dphi[i][qp](d) - // pressure term
312 (grad_uv[d]*dphi[i][qp])); // diffusion term
313
314 // Concentration Equation Residual
315 FC(i) += JxW[qp] *
316 ((U*grad_C)*phi[i][qp] + // convection term
317 (1./Peclet)*(grad_C*dphi[i][qp])); // diffusion term
318
319 // Note that the Fp block is identically zero unless we are using
320 // some kind of artificial compressibility scheme...
321
322 if (request_jacobian && c.elem_solution_derivative)
323 {
325
326 for (unsigned int j=0; j != n_u_dofs; j++)
327 {
328 for (unsigned int d=0; d<2; ++d)
329 {
330 // Matrix contributions for the uu and vv couplings.
331 Kdiag[d](i,j) += JxW[qp] * (-(dphi[i][qp]*dphi[j][qp]));
332
333 // Matrix contributions for the Cu and Cv couplings.
334 C[d](i,j) += JxW[qp] * ((phi[j][qp]*grad_C(d))*phi[i][qp]);
335 }
336
337 // Concentration equation Jacobian contribution
338 KCC(i,j) += JxW[qp]*
339 ((U*dphi[j][qp])*phi[i][qp] + // nonlinear term (convection)
340 (1./Peclet)*(dphi[j][qp]*dphi[i][qp])); // diffusion term
341 }
342
343 // Matrix contributions for the up and vp couplings.
344 for (unsigned int j=0; j != n_p_dofs; j++)
345 for (unsigned int d=0; d<2; ++d)
346 B[d](i,j) += JxW[qp]*psi[j][qp]*dphi[i][qp](d);
347 }
348 }
349 } // end of the quadrature point qp-loop
350
351 return request_jacobian;
352}
353
354
355bool CoupledSystem::element_constraint (bool request_jacobian,
356 DiffContext & context)
357{
358 FEMContext & c = cast_ref<FEMContext &>(context);
359
360 // Here we define some references to cell-specific data that
361 // will be used to assemble the linear system.
362 FEBase * u_elem_fe = nullptr;
363 c.get_element_fe(u_var, u_elem_fe);
364
365 FEBase * p_elem_fe = nullptr;
366 c.get_element_fe(p_var, p_elem_fe);
367
368 // Element Jacobian * quadrature weight for interior integration
369 const std::vector<Real> & JxW = u_elem_fe->get_JxW();
370
371 // The velocity shape function gradients at interior
372 // quadrature points.
373 const std::vector<std::vector<RealGradient>> & dphi = u_elem_fe->get_dphi();
374
375 // The pressure shape functions at interior
376 // quadrature points.
377 const std::vector<std::vector<Real>> & psi = p_elem_fe->get_phi();
378
379 // The number of local degrees of freedom in each variable
380 const unsigned int n_u_dofs = c.n_dof_indices(u_var);
381 const unsigned int n_p_dofs = c.n_dof_indices(p_var);
382
383 // pressure-velocity coupling blocks
384 // Kpu Kpv
385 std::reference_wrapper<DenseSubMatrix<Number>> B[2] =
386 {
389 };
390
391 // The subvectors and submatrices we need to fill:
393
394 // Add the constraint given by the continuity equation
395 unsigned int n_qpoints = c.get_element_qrule().n_points();
396
397 // Store (grad_u, grad_v) at current Newton iterate
398 Gradient grad_uv[2];
399
400 for (unsigned int qp=0; qp != n_qpoints; qp++)
401 {
402 // Compute the velocity gradient at the old Newton iterate
403 c.interior_gradient(u_var, qp, grad_uv[0]),
404 c.interior_gradient(v_var, qp, grad_uv[1]);
405
406 // Now a loop over the pressure degrees of freedom. This
407 // computes the contributions of the continuity equation.
408 for (unsigned int i=0; i != n_p_dofs; i++)
409 {
410 for (unsigned int d=0; d<2; ++d)
411 Fp(i) += JxW[qp] * psi[i][qp] * grad_uv[d](d);
412
413 if (request_jacobian && c.elem_solution_derivative)
414 {
416
417 for (unsigned int j=0; j != n_u_dofs; j++)
418 for (unsigned int d=0; d<2; ++d)
419 B[d](i,j) += JxW[qp]*psi[i][qp]*dphi[j][qp](d);
420 }
421 }
422 } // end of the quadrature point qp-loop
423
424 return request_jacobian;
425}
426
428{
429 // We need to overload the postprocess function to set the
430 // computed_QoI variable of the CoupledSystem class to the qoi value
431 // stored in System::qoi[0]
432 computed_QoI = this->get_qoi_value(0);
433}
434
435// These functions supply the nonlinear weighting for the adjoint residual error estimate which
436// arise due to the convection term in the convection-diffusion equation:
437// ||e((u_1)_h C,1)||_{L2} ||e(C^*)||_{L2} + ||e(u1 C,1_h)||_{L2} ||e(C^*)||_{L2}
438// ||e((u_2)_h C,2)||_{L2} ||e(C^*)||_{L2} + ||e(u2 C,2_h)||_{L2} ||e(C^*)||_{L2}
439// These functions compute (u_1)_h or C,1_h , and (u_2)_h or C,2_h , and supply it to the weighted patch recovery error estimator
440// In CoupledFEMFunctionsx, the object built with var = 0, returns the (u_1)_h weight, while
441// the object built with var = 1, returns the C,1_h weight. The switch statement
442// distinguishes the behavior of the two objects
443// Same thing for CoupledFEMFunctionsy
445 const Point & p,
446 const Real /* time */)
447{
448 Number weight = 0.0;
449
450 switch(var)
451 {
452 case 0:
453 {
454 Gradient grad_C = c.point_gradient(3, p);
455
456 weight = grad_C(0);
457 }
458 break;
459
460 case 3:
461 {
462 Number u = c.point_value(0, p);
463
464 weight = u;
465 }
466 break;
467
468 default:
469 libmesh_error_msg("Wrong variable number " \
470 << var \
471 << " passed to CoupledFEMFunctionsx object! Quitting!");
472 }
473
474 return weight;
475}
476
478 const Point & p,
479 const Real /* time */)
480{
481 Number weight = 0.0;
482
483 switch(var)
484 {
485 case 1:
486 {
487 Gradient grad_C = c.point_gradient(3, p);
488
489 weight = grad_C(1);
490 }
491 break;
492
493 case 3:
494 {
495 Number v = c.point_value(1, p);
496
497 weight = v;
498 }
499 break;
500
501 default:
502 libmesh_error_msg("Wrong variable number " \
503 << var \
504 << " passed to CoupledFEMFunctionsy object! Quitting!");
505 }
506
507 return weight;
508}
virtual Number operator()(const Point &, const Real=0)
virtual std::unique_ptr< FunctionBase< Number > > clone() const
const unsigned int _u_var
const Real _sign
BdyFunction(unsigned int u_var, unsigned int v_var, int sign)
const unsigned int _v_var
virtual void operator()(const FEMContext &, const Point &, const Real, DenseVector< Number > &)
Evaluation function for time-dependent vector-valued functions.
virtual void operator()(const FEMContext &, const Point &, const Real, DenseVector< Number > &)
Evaluation function for time-dependent vector-valued functions.
virtual void init_context(DiffContext &context)
virtual bool element_time_derivative(bool request_jacobian, DiffContext &context)
Adds the time derivative contribution on elem to elem_residual.
unsigned int u_var
virtual void init_data()
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
unsigned int p_var
virtual bool element_constraint(bool request_jacobian, DiffContext &context)
Adds the constraint contribution on elem to elem_residual.
virtual void postprocess()
Runs a postprocessing loop over all elements, and if postprocess_sides is true over all sides.
unsigned int v_var
unsigned int C_var
Function that returns a single value that never changes.
Defines a dense submatrix for use in Finite Element-type computations.
Defines a dense subvector for use in finite element computations.
Defines a dense vector for use in Finite Element-type computations.
void resize(const unsigned int n)
Resize the vector.
virtual void zero() override final
Set every element in the vector to 0.
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.
unsigned int n_dof_indices() const
Total number of dof indices on the element.
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 void time_evolving(unsigned int var, unsigned int order)
Tells the DiffSystem that variable var is evolving with respect to time.
This class allows one to associate Dirichlet boundary values with a given set of mesh boundary ids an...
void add_dirichlet_boundary(const DirichletBoundary &dirichlet_boundary)
Adds a copy of the specified Dirichlet boundary to the system.
void get_nothing() const
virtual_for_inffe const std::vector< Real > & get_JxW() const
virtual_for_inffe const std::vector< Point > & get_xyz() const
This class forms the foundation from which generic finite elements may be derived.
Definition fe_base.h:86
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition fe_base.h:207
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition fe_base.h:230
This class provides all data required for a physics package (e.g.
Definition fem_context.h:63
void get_side_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for edge/face (2D/3D) finite element object for variable var for the largest dimension in th...
Gradient interior_gradient(unsigned int var, unsigned int qp) const
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
Number interior_value(unsigned int var, unsigned int qp) const
void get_element_fe(unsigned int var, FEGenericBase< OutputShape > *&fe) const
Accessor for interior finite element object for variable var for the largest dimension in the mesh.
Number point_value(unsigned int var, const Point &p) const
Gradient point_gradient(unsigned int var, const Point &p) const
Real verify_analytic_jacobians
If verify_analytic_jacobian is equal to zero (as it is by default), no numeric jacobians will be calc...
Definition fem_system.h:215
Base class for functors that can be evaluated at a point and (optionally) time.
bool _initialized
When init() was called so that everything is ready for calls to operator() (...), then this bool is t...
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
unsigned int n_points() const
Definition quadrature.h:131
unsigned int add_variable(std::string_view var, const FEType &type, const std::set< subdomain_id_type > *const active_subdomains=nullptr)
Adds the variable var to the list of variables for this system.
Definition system.C:1344
Number get_qoi_value(unsigned int qoi_index) const
Definition system.C:2184
const DofMap & get_dof_map() const
Definition system.h:2417
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
ConstFunction that simply returns 0.
The libMesh namespace provides an interface to certain functionality in the library.
int8_t boundary_id_type
Definition id_types.h:51
libmesh_assert(ctx)
const Number zero
.
Definition libmesh.h:297
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
Definition assembly.h:39