libMesh
elasticity_system.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 // 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 
36 using 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 
87  ZeroFunction<> zf;
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
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 
154 bool ElasticitySystem::element_time_derivative(bool request_jacobian,
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 
261 bool 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 
330 bool 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 
416 Real 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 }
boundary_id_type boundary_id_min_x
Real get_elem_solution_derivative() const
The derivative of the current elem_solution w.r.t.
Definition: diff_context.h:432
const boundary_id_type edge_boundary_id
const DenseMatrix< Number > & get_elem_jacobian() const
Const accessor for element Jacobian.
Definition: diff_context.h:274
This class provides all data required for a physics package (e.g.
Definition: diff_context.h:55
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...
Definition: fem_context.h:317
Real elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
Definition: assembly.C:43
ConstFunction that simply returns 0.
Definition: zero_function.h:38
virtual void init_context(DiffContext &context)
Real kronecker_delta(unsigned int i, unsigned int j)
Definition: assembly.C:37
void interior_accel(unsigned int var, unsigned int qp, OutputType &u) const
Definition: fem_context.C:1385
unsigned int n_dof_indices() const
Total number of dof indices on the element.
Definition: diff_context.h:395
virtual bool mass_residual(bool request_jacobian, DiffContext &context)
Subtracts a mass vector contribution on elem from elem_residual.
This class allows one to associate Dirichlet boundary values with a given set of mesh boundary ids an...
This class defines a vector in LIBMESH_DIM dimensional Real or Complex space.
The libMesh namespace provides an interface to certain functionality in the library.
bool has_side_boundary_id(boundary_id_type id) const
Reports if the boundary id is found on the current side.
Definition: fem_context.C:298
boundary_id_type boundary_id_max_x
const std::vector< std::vector< OutputGradient > > & get_dphi() const
Definition: fe_base.h:230
Gradient interior_gradient(unsigned int var, unsigned int qp) const
Definition: fem_context.C:462
int8_t boundary_id_type
Definition: id_types.h:51
boundary_id_type boundary_id_max_z
const boundary_id_type node_boundary_id
virtual void init_data() override
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
Definition: fem_system.C:873
virtual_for_inffe const std::vector< Real > & get_JxW() const
Definition: fe_abstract.h:295
This class provides all data required for a physics package (e.g.
Definition: fem_context.h:62
boundary_id_type boundary_id_max_y
unsigned int n_points() const
Definition: quadrature.h:123
virtual bool side_time_derivative(bool request_jacobian, DiffContext &context)
Adds the time derivative contribution on side of elem to elem_residual.
void get_nothing() const
Definition: fe_abstract.h:261
Real elasticity_tensor(unsigned int i, unsigned int j, unsigned int k, unsigned int l)
const boundary_id_type pressure_boundary_id
Real get_elem_solution_accel_derivative() const
The derivative of the current elem_solution_accel w.r.t.
Definition: diff_context.h:450
virtual void init_data()
Initializes the member data fields associated with the system, so that, e.g., assemble() may be used...
const DenseVector< Number > & get_elem_residual() const
Const accessor for element residual.
Definition: diff_context.h:242
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual_for_inffe const std::vector< Point > & get_normals() const
Definition: fe_abstract.h:451
const boundary_id_type fixed_u_boundary_id
static const boundary_id_type & traction_boundary_id
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...
Definition: fem_context.h:277
boundary_id_type boundary_id_min_y
virtual bool element_time_derivative(bool request_jacobian, DiffContext &context)
Adds the time derivative contribution on elem to elem_residual.
This class forms the foundation from which generic finite elements may be derived.
const QBase & get_element_qrule() const
Accessor for element interior quadrature rule for the dimension of the current _elem.
Definition: fem_context.h:802
const std::set< unsigned char > & elem_dimensions() const
Definition: fem_context.h:951
const QBase & get_side_qrule() const
Accessor for element side quadrature rule for the dimension of the current _elem. ...
Definition: fem_context.h:809
This class defines a tensor in LIBMESH_DIM dimensional Real or Complex space.
const std::vector< std::vector< OutputShape > > & get_phi() const
Definition: fe_base.h:207
boundary_id_type boundary_id_min_z
const boundary_id_type fixed_v_boundary_id