libMesh
Loading...
Searching...
No Matches
elasticity_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// Example includes
19#include "elasticity_system.h"
20
21// libMesh includes
22#include "libmesh/dof_map.h"
23#include "libmesh/elem.h"
24#include "libmesh/fe_base.h"
25#include "libmesh/fem_context.h"
26#include "libmesh/zero_function.h"
27#include "libmesh/dirichlet_boundaries.h"
28#include "libmesh/quadrature.h"
29#include "libmesh/unsteady_solver.h"
30#include "libmesh/parallel_implementation.h" // set_union
31#include "libmesh/boundary_info.h"
32
33// C++ includes
34#include <functional> // std::reference_wrapper
35
36using namespace libMesh;
37
44
45#if LIBMESH_DIM > 2
46
48{
49 _u_var = this->add_variable ("Ux", _fe_type);
50 if (_dim > 1)
51 _v_var = this->add_variable ("Uy", _fe_type);
52 else
53 _v_var = _u_var;
54 if (_dim > 2)
55 _w_var = this->add_variable ("Uz", _fe_type);
56 else
57 _w_var = _v_var;
58
59 this->time_evolving(_u_var,2);
60 this->time_evolving(_v_var,2);
61 this->time_evolving(_w_var,2);
62
63#ifdef LIBMESH_ENABLE_DIRICHLET
64
65 std::set<boundary_id_type> all_boundary_ids, dirichlet_boundary_ids,
66 dirichlet_u_boundary_ids, dirichlet_v_boundary_ids;
67 all_boundary_ids = this->get_mesh().get_boundary_info().get_boundary_ids();
68 this->comm().set_union(all_boundary_ids);
69
70 if (all_boundary_ids.count(boundary_id_min_x))
71 dirichlet_boundary_ids.insert(boundary_id_min_x);
72 if (all_boundary_ids.count(node_boundary_id))
73 dirichlet_boundary_ids.insert(node_boundary_id);
74 if (all_boundary_ids.count(edge_boundary_id))
75 dirichlet_boundary_ids.insert(edge_boundary_id);
76 if (all_boundary_ids.count(fixed_u_boundary_id))
77 dirichlet_u_boundary_ids.insert(fixed_u_boundary_id);
78 if (all_boundary_ids.count(fixed_v_boundary_id))
79 dirichlet_v_boundary_ids.insert(fixed_v_boundary_id);
80
81 std::vector<unsigned int> variables {_u_var};
82 if (_dim > 1)
83 variables.push_back(_v_var);
84 if (_dim > 2)
85 variables.push_back(_w_var);
86
88
89 // Most DirichletBoundary users will want to supply a "locally
90 // indexed" functor
91 DirichletBoundary dirichlet_bc(dirichlet_boundary_ids, variables,
93 this->get_dof_map().add_dirichlet_boundary(dirichlet_bc);
94
95 if (!dirichlet_u_boundary_ids.empty())
96 {
97 DirichletBoundary dirichlet_u_bc(dirichlet_u_boundary_ids,
98 {_u_var}, zf,
100 this->get_dof_map().add_dirichlet_boundary(dirichlet_u_bc);
101 }
102
103 if (!dirichlet_v_boundary_ids.empty())
104 {
105 DirichletBoundary dirichlet_v_bc(dirichlet_v_boundary_ids,
106 {_v_var}, zf,
108 this->get_dof_map().add_dirichlet_boundary(dirichlet_v_bc);
109 }
110
111#endif // LIBMESH_ENABLE_DIRICHLET
112
113 // Do the parent's initialization after variables and boundary constraints are defined
114 FEMSystem::init_data();
115}
116
118{
119 FEMContext & c = cast_ref<FEMContext &>(context);
120
121 FEBase * u_elem_fe;
122 FEBase * u_side_fe;
123
124 c.get_element_fe(_u_var, u_elem_fe);
125 c.get_side_fe(_u_var, u_side_fe);
126
127 // We should prerequest all the data
128 // we will need to build the residuals.
129 u_elem_fe->get_JxW();
130 u_elem_fe->get_phi();
131 u_elem_fe->get_dphi();
132
133 u_side_fe->get_JxW();
134 u_side_fe->get_phi();
135
136 // We might want to apply traction perpendicular to some boundaries.
137 u_side_fe->get_normals();
138
139 // If this is an IGA mesh then we have spline control NodeElem
140 // points
141 if (c.elem_dimensions().count(0))
142 {
143 FEBase * null_fe;
144 c.get_element_fe(_u_var, null_fe, 0);
145 null_fe->get_JxW();
146 null_fe->get_phi();
147 null_fe->get_dphi();
148
149 c.get_side_fe(_u_var, null_fe, 0);
150 null_fe->get_nothing();
151 }
152}
153
155 DiffContext & context)
156{
157 FEMContext & c = cast_ref<FEMContext &>(context);
158
159 // If we have an unsteady solver, then we need to extract the corresponding
160 // velocity variable. This allows us to use either a FirstOrderUnsteadySolver
161 // or a SecondOrderUnsteadySolver. That is, we get back the velocity variable
162 // index for FirstOrderUnsteadySolvers or, if it's a SecondOrderUnsteadySolver,
163 // this is actually just giving us back the same variable index.
164
165 // If we only wanted to use a SecondOrderUnsteadySolver, then this
166 // step would be unnecessary and we would just
167 // populate the _u_var, etc. blocks of the residual and Jacobian.
168 unsigned int u_dot_var = this->get_second_order_dot_var(_u_var);
169 unsigned int v_dot_var = this->get_second_order_dot_var(_v_var);
170 unsigned int w_dot_var = this->get_second_order_dot_var(_w_var);
171
172 FEBase * u_elem_fe;
173 c.get_element_fe(_u_var, u_elem_fe);
174
175 // The number of local degrees of freedom in each variable
176 const unsigned int n_u_dofs = c.n_dof_indices(_u_var);
177
178 // Element Jacobian * quadrature weights for interior integration
179 const std::vector<Real> & JxW = u_elem_fe->get_JxW();
180
181 const std::vector<std::vector<Real>> & phi = u_elem_fe->get_phi();
182 const std::vector<std::vector<RealGradient>> & grad_phi = u_elem_fe->get_dphi();
183
184 // Get rhs DenseSubVector references
185 // std::reference_wrapper allows us to have a C-style array of
186 // references, which makes the indexing below a bit easier.
187 // We set _w_var=_v_var etc in lower dimensions so this is sane.
188 std::reference_wrapper<DenseSubVector<Number>> F[3] =
189 {
190 c.get_elem_residual(u_dot_var),
191 c.get_elem_residual(v_dot_var),
192 c.get_elem_residual(w_dot_var)
193 };
194
195 // Get DenseSubMatrix references
196 // Kuu, Kuv, Kuw
197 // Kvu, Kvv, Kvw
198 // Kwu, Kwv, Kww
199 std::reference_wrapper<DenseSubMatrix<Number>> K[3][3] =
200 {
201 {c.get_elem_jacobian(u_dot_var, _u_var), c.get_elem_jacobian(u_dot_var, _v_var), c.get_elem_jacobian(u_dot_var, _w_var)},
202 {c.get_elem_jacobian(v_dot_var, _u_var), c.get_elem_jacobian(v_dot_var, _v_var), c.get_elem_jacobian(v_dot_var, _w_var)},
203 {c.get_elem_jacobian(w_dot_var, _u_var), c.get_elem_jacobian(w_dot_var, _v_var), c.get_elem_jacobian(w_dot_var, _w_var)}
204 };
205
206 unsigned int n_qpoints = c.get_element_qrule().n_points();
207
208 Gradient body_force(0.0, 0.0, -1.0);
209
210 for (unsigned int qp=0; qp != n_qpoints; qp++)
211 {
212 Gradient grad_u, grad_v, grad_w;
213 c.interior_gradient(_u_var, qp, grad_u);
214 if (_dim > 1)
215 c.interior_gradient(_v_var, qp, grad_v);
216 if (_dim > 2)
217 c.interior_gradient(_w_var, qp, grad_w);
218
219 // Convenience
220 Tensor grad_U (grad_u, grad_v, grad_w);
221
222 Tensor tau;
223 for (unsigned int i = 0; i < _dim; i++)
224 for (unsigned int j = 0; j < _dim; j++)
225 for (unsigned int k = 0; k < _dim; k++)
226 for (unsigned int l = 0; l < _dim; l++)
227 tau(i,j) += elasticity_tensor(i,j,k,l)*grad_U(k,l);
228
229 for (unsigned int i=0; i != n_u_dofs; i++)
230 {
231 for (unsigned int alpha = 0; alpha < _dim; alpha++)
232 {
233 for (unsigned int d = 0; d < _dim; ++d)
234 F[d](i) += (tau(d,alpha)*grad_phi[i][qp](alpha) - body_force(d)*phi[i][qp])*JxW[qp];
235
236 if (request_jacobian)
237 {
238 for (unsigned int j=0; j != n_u_dofs; j++)
239 {
240 for (unsigned int beta = 0; beta < _dim; beta++)
241 {
242 // Convenience
243 const Real c0 = grad_phi[j][qp](beta)*c.get_elem_solution_derivative();
244
245 for (unsigned int k = 0; k < _dim; ++k)
246 for (unsigned int l = 0; l < _dim; ++l)
247 K[k][l](i,j) += elasticity_tensor(k, alpha, l, beta) *
248 c0*grad_phi[i][qp](alpha)*JxW[qp];
249 }
250 }
251 }
252 }
253 }
254
255 } // qp loop
256
257 // If the Jacobian was requested, we computed it. Otherwise, we didn't.
258 return request_jacobian;
259}
260
261bool ElasticitySystem::side_time_derivative (bool request_jacobian,
262 DiffContext & context)
263{
264 FEMContext & c = cast_ref<FEMContext &>(context);
265
266 // If we're on the correct side, apply the traction
269 {
270 // If we have an unsteady solver, then we need to extract the corresponding
271 // velocity variable. This allows us to use either a FirstOrderUnsteadySolver
272 // or a SecondOrderUnsteadySolver. That is, we get back the velocity variable
273 // index for FirstOrderUnsteadySolvers or, if it's a SecondOrderUnsteadySolver,
274 // this is actually just giving us back the same variable index.
275
276 // If we only wanted to use a SecondOrderUnsteadySolver, then this
277 // step would be unnecessary and we would just
278 // populate the _u_var, etc. blocks of the residual and Jacobian.
279 // We set _w_var=_v_var etc in lower dimensions so this is sane.
280 unsigned int u_dot_var = this->get_second_order_dot_var(_u_var);
281 unsigned int v_dot_var = this->get_second_order_dot_var(_v_var);
282 unsigned int w_dot_var = this->get_second_order_dot_var(_w_var);
283
284 FEBase * u_side_fe;
285 c.get_side_fe(_u_var, u_side_fe);
286
287 // The number of local degrees of freedom in each variable
288 const unsigned int n_u_dofs = c.n_dof_indices(_u_var);
289
290 // Get rhs DenseSubVector references
291 std::reference_wrapper<DenseSubVector<Number>> F[3] =
292 {
293 c.get_elem_residual(u_dot_var),
294 c.get_elem_residual(v_dot_var),
295 c.get_elem_residual(w_dot_var)
296 };
297
298 // Element Jacobian * quadrature weights for interior integration
299 const std::vector<Real> & JxW = u_side_fe->get_JxW();
300
301 const std::vector<std::vector<Real>> & phi = u_side_fe->get_phi();
302
303 const std::vector<Point> & normals = u_side_fe->get_normals();
304
305 unsigned int n_qpoints = c.get_side_qrule().n_points();
306
307 Real pressure = 100;
308 Gradient traction;
309 traction(_dim-1) = -1;
310
311 const bool pressureforce =
313
314 for (unsigned int qp=0; qp != n_qpoints; qp++)
315 {
316 if (pressureforce)
317 traction = pressure * normals[qp];
318
319 for (unsigned int i=0; i != n_u_dofs; i++)
320 for (unsigned int d = 0; d < _dim; ++d)
321 F[d](i) -= traction(d)*phi[i][qp]*JxW[qp];
322 }
323 }
324
325 // If the Jacobian was requested, we computed it (in this case it's zero).
326 // Otherwise, we didn't.
327 return request_jacobian;
328}
329
330bool ElasticitySystem::mass_residual(bool request_jacobian,
331 DiffContext & context)
332{
333 FEMContext & c = cast_ref<FEMContext &>(context);
334
335 // We need to extract the corresponding velocity variable.
336 // This allows us to use either a FirstOrderUnsteadySolver
337 // or a SecondOrderUnsteadySolver. That is, we get back the velocity variable
338 // index for FirstOrderUnsteadySolvers or, if it's a SecondOrderUnsteadySolver,
339 // this is actually just giving us back the same variable index.
340
341 // If we only wanted to use a SecondOrderUnsteadySolver, then this
342 // step would be unnecessary and we would just
343 // populate the _u_var, etc. blocks of the residual and Jacobian.
344 // We set _w_var=_v_var etc in lower dimensions so this is sane.
345 unsigned int u_dot_var = this->get_second_order_dot_var(_u_var);
346 unsigned int v_dot_var = this->get_second_order_dot_var(_v_var);
347 unsigned int w_dot_var = this->get_second_order_dot_var(_w_var);
348
349 FEBase * u_elem_fe;
350 c.get_element_fe(u_dot_var, u_elem_fe);
351
352 // The number of local degrees of freedom in each variable
353 const unsigned int n_u_dofs = c.n_dof_indices(u_dot_var);
354
355 // Element Jacobian * quadrature weights for interior integration
356 const std::vector<Real> & JxW = u_elem_fe->get_JxW();
357
358 const std::vector<std::vector<Real>> & phi = u_elem_fe->get_phi();
359
360 // Get rhs DenseSubVector references
361 std::reference_wrapper<DenseSubVector<Number>> F[3] =
362 {
363 c.get_elem_residual(u_dot_var),
364 c.get_elem_residual(v_dot_var),
365 c.get_elem_residual(w_dot_var)
366 };
367
368 // Get references to stiffness matrix diagonal blocks
369 std::reference_wrapper<DenseSubMatrix<Number>> Kdiag[3] =
370 {
371 c.get_elem_jacobian(u_dot_var, u_dot_var),
372 c.get_elem_jacobian(v_dot_var, v_dot_var),
373 c.get_elem_jacobian(w_dot_var, w_dot_var)
374 };
375
376 unsigned int n_qpoints = c.get_element_qrule().n_points();
377
378 for (unsigned int qp=0; qp != n_qpoints; qp++)
379 {
380 // If we only cared about using FirstOrderUnsteadySolvers for time-stepping,
381 // then we could actually just use interior rate, but using interior_accel
382 // allows this assembly to function for both FirstOrderUnsteadySolvers
383 // and SecondOrderUnsteadySolvers
384 std::vector<Number> accel(3);
385 c.interior_accel(u_dot_var, qp, accel[0]);
386 if (_dim > 1)
387 c.interior_accel(v_dot_var, qp, accel[1]);
388 if (_dim > 2)
389 c.interior_accel(w_dot_var, qp, accel[2]);
390
391 for (unsigned int i=0; i != n_u_dofs; i++)
392 {
393 for (unsigned int d = 0; d < _dim; ++d)
394 F[d](i) += _rho*accel[d]*phi[i][qp]*JxW[qp];
395
396 if (request_jacobian)
397 {
398 for (unsigned int j=0; j != n_u_dofs; j++)
399 {
400 Real jac_term =
402 _rho*phi[i][qp]*phi[j][qp]*JxW[qp];
403
404 for (unsigned int d = 0; d < _dim; ++d)
405 Kdiag[d](i,j) += jac_term;
406 }
407 }
408 }
409 }
410
411 // If the Jacobian was requested, we computed it. Otherwise, we didn't.
412 return request_jacobian;
413}
414#endif // LIBMESH_DIM > 2
415
416Real ElasticitySystem::elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
417{
418 // Hard code material parameters for the sake of simplicity
419 const Real poisson_ratio = 0.3;
420 const Real young_modulus = 1.0e2;
421
422 // Define the Lame constants
423 const Real lambda_1 = (young_modulus*poisson_ratio)/((1.+poisson_ratio)*(1.-2.*poisson_ratio));
424 const Real lambda_2 = young_modulus/(2.*(1.+poisson_ratio));
425
426 return
427 lambda_1 * kronecker_delta(i, j) * kronecker_delta(k, l) +
428 lambda_2 * (kronecker_delta(i, k) * kronecker_delta(j, l) + kronecker_delta(i, l) * kronecker_delta(j, k));
429}
virtual bool mass_residual(bool request_jacobian, DiffContext &context)
Subtracts a mass vector contribution on elem from elem_residual.
virtual void init_data()
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used.
virtual bool element_time_derivative(bool request_jacobian, DiffContext &context)
Adds the time derivative contribution on elem to elem_residual.
virtual bool side_time_derivative(bool request_jacobian, DiffContext &context)
Adds the time derivative contribution on side of elem to elem_residual.
Real kronecker_delta(unsigned int i, unsigned int j)
Real elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
virtual void init_context(DiffContext &context)
void set_union(T &data, const unsigned int root_id) const
const std::set< boundary_id_type > & get_boundary_ids() const
This class provides all data required for a physics package (e.g.
Real get_elem_solution_derivative() const
The derivative of the current elem_solution w.r.t.
Real get_elem_solution_accel_derivative() const
The derivative of the current elem_solution_accel w.r.t.
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.
unsigned int get_second_order_dot_var(unsigned int var) const
For a given second order (in time) variable var, this method will return the index to the correspondi...
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< Point > & get_normals() const
virtual_for_inffe const std::vector< Real > & get_JxW() 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 std::set< unsigned char > & elem_dimensions() const
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
void interior_accel(unsigned int var, unsigned int qp, OutputType &u) 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.
const QBase & get_side_qrule() const
Accessor for element side quadrature rule for the dimension of the current _elem.
bool has_side_boundary_id(boundary_id_type id) const
Reports if the boundary id is found on the current side.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
const Parallel::Communicator & comm() const
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
const DofMap & get_dof_map() const
Definition system.h:2417
const MeshBase & get_mesh() const
Definition system.h:2401
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
ConstFunction that simply returns 0.
boundary_id_type boundary_id_max_x
boundary_id_type boundary_id_min_y
boundary_id_type boundary_id_max_z
boundary_id_type boundary_id_max_y
boundary_id_type boundary_id_min_z
boundary_id_type boundary_id_min_x
const boundary_id_type fixed_v_boundary_id
static const boundary_id_type & traction_boundary_id
const boundary_id_type edge_boundary_id
const boundary_id_type pressure_boundary_id
const boundary_id_type node_boundary_id
const boundary_id_type fixed_u_boundary_id
boundary_id_type boundary_id_min_x
The libMesh namespace provides an interface to certain functionality in the library.
int8_t boundary_id_type
Definition id_types.h:51
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real