1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503
|
.. # gedit: set fileencoding=utf8 :
.. _demo_elastodynamics:
============================================
Time-integration of elastodynamics equation
============================================
This demo is implemented in a single Python file,
:download:`demo_elastodynamics.py`, which contains both the
variational forms and the solver.
This demo shows how to perform time integration of transient elastodynamics using the generalized-:math:`\alpha` method [ERL2002]_. In particular it demonstrates how to:
* formulate mass and damping forms of the elastodynamics equation
* implement the generalized-:math:`\alpha` method and its influence on the solution
* perform efficient computation of stresses using ``LocalSolver``
The deformed structure evolution over time along with the axial stress will look as follows:
.. image:: elastodynamics-transient_deformation.gif
:width: 70%
:align: center
-----------------------------------------
Introduction and elastodynamics equation
-----------------------------------------
The elastodynamics equation combine the balance of linear momentum:
.. math::
\nabla \cdot \sigma + \rho b = \rho \ddot{u}
where :math:`u` is the displacement vector field, :math:`\ddot{u}=\partial^2 u/\partial t^2` is the acceleration,
:math:`\rho` the material density, :math:`b` a given body force and :math:`\sigma` the stress tensor which is related
to the displacement through a constitutive equation. In the case of isotropic linearized elasticity, one has:
.. math::
\sigma =\lambda \text{tr}(\varepsilon)\mathbb{1} + 2\mu\varepsilon
where :math:`\varepsilon = (\nabla u + (\nabla u)^T)/2` is the linearized strain tensor, :math:`\mathbb{1}` is the
identity of second-rank tensors and :math:`\lambda=\dfrac{E\nu}{(1+\nu)(1-2\nu)},\mu=\dfrac{E}{2(1+\nu)}` are the
Lame coefficients given as functions of the Young modulus :math:`E` and the Poisson ratio :math:`\nu`.
The weak form is readily obtained by integrating by part the balance equation using a test function :math:`v\in V`
with :math:`V` being a suitable function space that satisfies the displacement boundary conditions:
.. math::
\int_{\Omega} \rho \ddot{u}\cdot v \, {\rm d} x + \int_{\Omega} \sigma(u):\varepsilon(v) \, {\rm d} x =
\int_{\Omega} \rho b \cdot v \, {\rm d} x + \int_{\partial\Omega} (\sigma\cdot n) \cdot v \, {\rm d} s \quad \text{for all } v\in V
The previous equation can be written as follows:
.. math::
\text{Find }u\in V\text{ such that } m(\ddot{u},v) + k(u,v) = L(v) \quad \text{for all } v\in V
where :math:`m` is the symmetric bilinear form associated with the mass matrix and :math:`k` the one associated with the stiffness matrix.
After introducing the finite element space interpolation, one obtains the corresponding discretized evolution equation:
.. math::
\text{Find }\{u\}\in\mathbb{R}^n\text{ such that } \{v\}^T[M]\{\ddot{u}\} + \{v\}^T[K]\{u\} = \{v\}^T\{F\} \quad \text{for all } \{v\}\in\mathbb{R}^n
which is a generalized :math:`n`-dof harmonic oscillator equation.
Quite often in structural dynamics, structures do not oscillate perfectly but lose energy through various dissipative mechanisms (friction with air or supports,
internal dissipation through plasticity, damage, etc.). Dissipative terms can be introduced at the level of the constitutive equation if these mechanisms are well
known but quite often it is not the case. Dissipation can then be modeled by adding an *ad hoc* damping term depending on the structure velocity :math:`\dot{u}`
to the previous evolution equation:
.. math::
\text{Find }u\in V\text{ such that } m(\ddot{u},v) + c(\dot{u},v) + k(u,v) = L(v) \quad \text{for all } v\in V
The damping form will be considered here as bilinear and symmetric, being therefore associated with a damping matrix :math:`[C]`.
~~~~~~~~~~~~~~~~~
Rayleigh damping
~~~~~~~~~~~~~~~~~
When little is known about the origin of damping in the structure, a popular choice for the damping matrix, known as *Rayleigh damping*, consists in using
a linear combination of the mass and stiffness matrix :math:`[C] = \eta_M[M]+\eta_K[K]` with two positive parameters :math:`\eta_M,\eta_K` which
can be fitted against experimental measures for instance (usually by measuring the damping ratio of two natural modes of vibration).
---------------------------------------------------------------
Time discretization using the generalized-:math:`\alpha` method
---------------------------------------------------------------
We now introduce a time discretization of the interval study :math:`[0;T]` in :math:`N+1` time increments :math:`t_0=0,t_1,\ldots,t_N,t_{N+1}=T`
with :math:`\Delta t=T/N` denoting the time step (supposed constant). The resolution will make use of the generalized-:math:`\alpha` method
which can be seen as an extension of the widely used Newmark-:math:`\beta` method in structural dynamics. As an implicit method, it is unconditionally
stable for a proper choice of coefficients so that quite large time steps can be used. It also allows for high frequency dissipation and offers a
second-order accuracy, i.e. in :math:`O(\Delta t^2)`.
The method consists in solving the dynamic evolution equation at intermediate time between :math:`t_n` and :math:`t_{n+1}` as follows:
.. math::
[M]\{\ddot{u}_{n+1-\alpha_m}\} + [C]\{\dot{u}_{n+1-\alpha_f}\}+[K]\{u_{n+1-\alpha_f}\} = \{F(t_{n+1-\alpha_f})\}
with the notation :math:`X_{n+1-\alpha} = (1-\alpha)X_{n+1}+\alpha X_{n}`. In addition, the following approximation for the displacement and velocity
at :math:`t_{n+1}` are used:
.. math::
\begin{align*}
\{u_{n+1}\} &= \{u_{n}\}+\Delta t \{\dot{u}_{n}\} + \dfrac{\Delta t^2}{2}\left((1-2\beta)\{\ddot{u}_{n}\}+2\beta\{\ddot{u}_{n+1}\}\right) \\
\{\dot{u}_{n+1}\} &= \{\dot{u}_{n}\} + \Delta t\left((1-\gamma)\{\ddot{u}_{n}\}+\gamma\{\ddot{u}_{n+1}\}\right)
\end{align*}
It can be seen that these are the relations of the Newmark method. The latter is therefore obtained as a particular case when :math:`\alpha_f=\alpha_m=0`.
The problem can then be formulated in terms of unkown displacement at :math:`t_{n+1}` with:
.. math::
\{\ddot{u}_{n+1}\} = \dfrac{1}{\beta\Delta t^2}\left(\{u_{n+1}\} - \{u_{n}\}-\Delta t \{\dot{u}_{n}\} \right) - \dfrac{1-2\beta}{2\beta}\{\ddot{u}_{n}\}
After plugging into the evolution and rearranging the known and unknown terms, one obtains the following system to solve:
.. math::
\begin{align*}
[\bar{K}]\{u_{n+1}\} &= \{F(t_{n+1-\alpha_f})\} - \alpha_f[K]\{u_n\} \\
&- [C](c_1\{u_n\}+c_2\{\dot{u}_n\}+c_3\{\ddot{u}_n\})-[M](m_1\{u_n\}+m_2\{\dot{u}_n\}+m_3\{\ddot{u}_n\})
\end{align*}
where:
* :math:`[\bar{K}] = [K]+c_1[C]+m_1[M]`
* :math:`c_1 = \dfrac{\gamma(1-\alpha_f)}{\beta\Delta t}`
* :math:`c_2 = 1-\gamma(1-\alpha_f)/\beta`
* :math:`c_3 = \Delta t(1-\alpha_f)(1-\dfrac{\gamma}{2\beta})`
* :math:`m_1 = \dfrac{(1-\alpha_m)}{\beta\Delta t^2}`
* :math:`m_2 = \dfrac{(1-\alpha_m)}{\beta\Delta t}`
* :math:`m_3 = 1-\dfrac{1-\alpha_m}{2\beta}`
Once the linear system has been solved for :math:`\{u_{n+1}\}`, the new velocity and acceleration are computed using the previous formulae.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Popular choice of parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The most popular choice for the parameters is: :math:`\alpha_m,\alpha_f \leq 1/2` and :math:`\gamma=\dfrac{1}{2}+\alpha_m-\alpha_f`,
:math:`\beta=\dfrac{1}{4}\left(\gamma+\dfrac{1}{2}\right)^2` which ensures unconditional stability, optimal dissipation and second-order accuracy.
---------------
Implementation
---------------
We consider a rectangular beam clamped at one end and loaded by a uniform vertical traction at the other end.
After importing the relevant modules, the mesh and subdomains for boundary conditions are defined::
from dolfin import *
import numpy as np
import matplotlib.pyplot as plt
# Form compiler options
parameters["form_compiler"]["cpp_optimize"] = True
parameters["form_compiler"]["optimize"] = True
# Define mesh
mesh = BoxMesh(Point(0., 0., 0.), Point(1., 0.1, 0.04), 60, 10, 5)
# Sub domain for clamp at left end
def left(x, on_boundary):
return near(x[0], 0.) and on_boundary
# Sub domain for rotation at right end
def right(x, on_boundary):
return near(x[0], 1.) and on_boundary
Material parameters for the elastic constitutive relation, the material density :math:`\rho`
for the mass matrix and the two parameters defining the Rayleigh damping :math:`\eta_M,\eta_K`
(initially zero damping is considered but this value can be changed) are now defined::
# Elastic parameters
E = 1000.0
nu = 0.3
mu = Constant(E / (2.0*(1.0 + nu)))
lmbda = Constant(E*nu / ((1.0 + nu)*(1.0 - 2.0*nu)))
# Mass density
rho = Constant(1.0)
# Rayleigh damping coefficients
eta_m = Constant(0.)
eta_k = Constant(0.)
Parameters used for the time discretization scheme are now defined. First, the four parameters used by the
generalized-:math:`\alpha` method are chosen. Here, we used the optimal dissipation and second-order accuracy
choice for :math:`\beta` and :math:`\gamma`, namely :math:`\beta=\dfrac{1}{4}\left(\gamma+\dfrac{1}{2}\right)^2` and
:math:`\gamma=\dfrac{1}{2}+\alpha_m-\alpha_f` with :math:`\alpha_m=0.2` and :math:`\alpha_f=0.4` ensuring unconditional stability::
# Generalized-alpha method parameters
alpha_m = Constant(0.2)
alpha_f = Constant(0.4)
gamma = Constant(0.5+alpha_f-alpha_m)
beta = Constant((gamma+0.5)**2/4.)
We also define the final time of the interval, the number of time steps and compute the associated time interval
between two steps::
# Time-stepping parameters
T = 4.0
Nsteps = 50
dt = Constant(T/Nsteps)
We now define the time-dependent loading. Body forces are zero and the imposed loading consists of a uniform vertical traction
applied at the ``right`` extremity. The loading amplitude will vary linearly from :math:`0` to :math:`p_0=1` over the time interval
:math:`[0;T_c=T/5]`, after :math:`T_c` the loading is removed. For this purpose, we used the following JIT-compiled ``Expression``.
In particular, it uses a conditional syntax using operators ``?`` and ``:`` ::
p0 = 1.
cutoff_Tc = T/5
# Define the loading as an expression depending on t
p = Expression(("0", "t <= tc ? p0*t/tc : 0", "0"), t=0, tc=cutoff_Tc, p0=p0, degree=0)
A standard vectorial :math:`P^1` FunctionSpace is now defined for the displacement, velocity and acceleration fields. We also
define a tensorial DG-0 FunctionSpace for saving the stress field evolution::
# Define function space for displacement, velocity and acceleration
V = VectorFunctionSpace(mesh, "CG", 1)
# Define function space for stresses
Vsig = TensorFunctionSpace(mesh, "DG", 0)
Test and trial functions are defined and the unkown displacement (corresponding to :math:`\{u_{n+1}\}` for the current time step)
will be represented by the Function ``u``. Displacement, velocity and acceleration fields of the previous increment
:math:`t_n` will respectively be represented by functions ``u_old``, ``v_old`` and ``a_old``::
# Test and trial functions
du = TrialFunction(V)
u_ = TestFunction(V)
# Current (unknown) displacement
u = Function(V, name="Displacement")
# Fields from previous time step (displacement, velocity, acceleration)
u_old = Function(V)
v_old = Function(V)
a_old = Function(V)
We now use a ``MeshFunction`` for distinguishing the different boundaries and mark the right extremity using an ``AutoSubDomain``.
The exterior surface measure ``ds`` is then defined using the boundary subdomains. Simple Dirichlet boundary conditions are also defined at the left extremity::
# Create mesh function over the cell facets
boundary_subdomains = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary_subdomains.set_all(0)
force_boundary = AutoSubDomain(right)
force_boundary.mark(boundary_subdomains, 3)
# Define measure for boundary condition integral
dss = ds(subdomain_data=boundary_subdomains)
# Set up boundary condition at left end
zero = Constant((0.0, 0.0, 0.0))
bc = DirichletBC(V, zero, left)
Python functions are now defined to obtain the elastic stress tensor :math:`\sigma` (linear isotropic elasticity), the bilinear mass and stiffness forms as well
as the damping form obtained as a linear combination of the mass and stiffness forms (Rayleigh damping). The linear form corresponding to the work of external forces is also defined::
# Stress tensor
def sigma(r):
return 2.0*mu*sym(grad(r)) + lmbda*tr(sym(grad(r)))*Identity(len(r))
# Mass form
def m(u, u_):
return rho*inner(u, u_)*dx
# Elastic stiffness form
def k(u, u_):
return inner(sigma(u), sym(grad(u_)))*dx
# Rayleigh damping form
def c(u, u_):
return eta_m*m(u, u_) + eta_k*k(u, u_)
# Work of external forces
def Wext(u_):
return dot(u_, p)*dss(3)
Functions for implementing the time stepping scheme are also defined. ``update_a`` returns :math:`\{\ddot{u}_{n+1}\}`
as a function of the variables at the previous increment and of the new displacement :math:`\{u_{n+1}\}`. The function accepts a keyword ``ufl`` so that the expressions involved can be used with UFL representations if ``True`` or with array of values if ``False`` (we will make use of both possibilities later).
In particular, the time step ``dt`` and time-stepping scheme parameters are either ``Constant`` or floats depending on the case.
Function ``update_v`` does the same but for the new velocity :math:`\{\dot{u}_{n+1}\}` as a function of the previous variables
and of the new acceleration. Finally, function ``update_fields`` performs the final update at the end of the time step when the new
displacement :math:`\{u_{n+1}\}` has effectively been computed. In this context, the new acceleration and velocities are computed
using the vector representation of the different fields. The variables keeping track of the values at the previous increment are now assigned the new values computed for the current increment::
# Update formula for acceleration
# a = 1/(2*beta)*((u - u0 - v0*dt)/(0.5*dt*dt) - (1-2*beta)*a0)
def update_a(u, u_old, v_old, a_old, ufl=True):
if ufl:
dt_ = dt
beta_ = beta
else:
dt_ = float(dt)
beta_ = float(beta)
return (u-u_old-dt_*v_old)/beta_/dt_**2 - (1-2*beta_)/2/beta_*a_old
# Update formula for velocity
# v = dt * ((1-gamma)*a0 + gamma*a) + v0
def update_v(a, u_old, v_old, a_old, ufl=True):
if ufl:
dt_ = dt
gamma_ = gamma
else:
dt_ = float(dt)
gamma_ = float(gamma)
return v_old + dt_*((1-gamma_)*a_old + gamma_*a)
def update_fields(u, u_old, v_old, a_old):
"""Update fields at the end of each time step."""
# Get vectors (references)
u_vec, u0_vec = u.vector(), u_old.vector()
v0_vec, a0_vec = v_old.vector(), a_old.vector()
# use update functions using vector arguments
a_vec = update_a(u_vec, u0_vec, v0_vec, a0_vec, ufl=False)
v_vec = update_v(a_vec, u0_vec, v0_vec, a0_vec, ufl=False)
# Update (u_old <- u)
v_old.vector()[:], a_old.vector()[:] = v_vec, a_vec
u_old.vector()[:] = u.vector()
The system variational form is now built by expressing the new acceleration :math:`\{\ddot{u}_{n+1}\}` as a function of
the TrialFunction ``du`` using ``update_a``, which here works as a UFL expression. Using this new acceleration, the same is
done for the new velocity using ``update_v``. Intermediate averages using parameters :math:`\alpha_m,\alpha_f` of the generalized- :math:`\alpha`
method are obtained with a user-defined fuction ``avg``. The weak form evolution equation is then written using all these
quantities. Since the problem is linear, we then extract the bilinear and linear parts using ``rhs`` and ``lhs``::
def avg(x_old, x_new, alpha):
return alpha*x_old + (1-alpha)*x_new
# Residual
a_new = update_a(du, u_old, v_old, a_old, ufl=True)
v_new = update_v(a_new, u_old, v_old, a_old, ufl=True)
res = m(avg(a_old, a_new, alpha_m), u_) + c(avg(v_old, v_new, alpha_f), u_) \
+ k(avg(u_old, du, alpha_f), u_) - Wext(u_)
a_form = lhs(res)
L_form = rhs(res)
Alternatively, the use of ``derivative`` can be made for non-linear problems for instance or one can also directly
formulate the system to solve, involving the modified stiffness matrix :math:`[\bar{K}]` and the various coefficients introduced earlier.
Since the system matrix to solve is the same for each time step (constant time step), it is not necessary to factorize the system at each increment.
It can be done once and for all and only perform assembly of the varying right-hand side and backsubstitution to obtain the solution
much more efficiently. This is done by defining a ``LUSolver`` object while PETSc handles caching factorizations::
# Define solver for reusing factorization
K, res = assemble_system(a_form, L_form, bc)
solver = LUSolver(K, "mumps")
solver.parameters["symmetric"] = True
We now initiate the time stepping loop. We will keep track of the beam vertical tip displacement over time as well as the different
parts of the system total energy. We will also compute the stress field and save it, along with the displacement field, in a ``XDMFFile``.
The option `flush_ouput` enables to open the result file before the loop is finished, the ``function_share_mesh`` option tells that only one
mesh is used for all functions of a given time step (displacement and stress) while the ``rewrite_function_mesh`` enforces that the same mesh
is used for all time steps. These two options enables writing the mesh information only once instead of :math:`2N_{steps}` times::
# Time-stepping
time = np.linspace(0, T, Nsteps+1)
u_tip = np.zeros((Nsteps+1,))
energies = np.zeros((Nsteps+1, 4))
E_damp = 0
E_ext = 0
sig = Function(Vsig, name="sigma")
xdmf_file = XDMFFile("elastodynamics-results.xdmf")
xdmf_file.parameters["flush_output"] = True
xdmf_file.parameters["functions_share_mesh"] = True
xdmf_file.parameters["rewrite_function_mesh"] = False
The time loop is now started, the loading is first evaluated at :math:`t=t_{n+1-\alpha_f}`. The corresponding system right-hand side is then
assembled and the system is solved. The different fields are then updated with the newly computed quantities. Finally, some post-processing is
performed: stresses are computed and written to the result file and the tip displacement and the different energies are recorded::
def local_project(v, V, u=None):
"""Element-wise projection using LocalSolver"""
dv = TrialFunction(V)
v_ = TestFunction(V)
a_proj = inner(dv, v_)*dx
b_proj = inner(v, v_)*dx
solver = LocalSolver(a_proj, b_proj)
solver.factorize()
if u is None:
u = Function(V)
solver.solve_local_rhs(u)
return u
else:
solver.solve_local_rhs(u)
return
for (i, dt) in enumerate(np.diff(time)):
t = time[i+1]
print("Time: ", t)
# Forces are evaluated at t_{n+1-alpha_f}=t_{n+1}-alpha_f*dt
p.t = t-float(alpha_f*dt)
# Solve for new displacement
res = assemble(L_form)
bc.apply(res)
solver.solve(K, u.vector(), res)
# Update old fields with new quantities
update_fields(u, u_old, v_old, a_old)
# Save solution to XDMF format
xdmf_file.write(u, t)
# Compute stresses and save to file
local_project(sigma(u), Vsig, sig)
xdmf_file.write(sig, t)
p.t = t
# Record tip displacement and compute energies
# Note: Only works in serial
if MPI.comm_world.size == 1:
u_tip[i+1] = u(1., 0.05, 0.)[1]
E_elas = assemble(0.5*k(u_old, u_old))
E_kin = assemble(0.5*m(v_old, v_old))
E_damp += dt*assemble(c(v_old, v_old))
# E_ext += assemble(Wext(u-u_old))
E_tot = E_elas+E_kin+E_damp #-E_ext
energies[i+1, :] = np.array([E_elas, E_kin, E_damp, E_tot])
Note that in the above, the stresses are computed using a ``LocalSolver`` through the ``local_project`` function. Since the stress function space
is a DG-0 space, the projection on this space can be performed element-wise in a very efficient manner. We therefore take advantage of the ``LocalSolver``
functionality which is precisely dedicated to such situations. Since this projection is performed at each time step, the savings in terms of computing
time can be quite important.
As regards the computation of the various energies, the elastic and kinetic energies are respectively given by:
.. math::
E_{elas} = \int_{\Omega} \dfrac{1}{2}\sigma(u):\varepsilon(u) \, {\rm d} x
.. math::
E_{kin} = \int_{\Omega} \dfrac{1}{2}\rho \dot{u}\cdot\dot{u} \, {\rm d} x
which are readily computed from the respective stiffness and mass forms ``k`` and ``m`` and the current displacement and velocity. The energy related to damping
is computed from the corresponding dissipation term :math:`\mathcal{D}=c(\dot{u},\dot{u})` and integrated over time:
.. math::
E_{damp} = \int_0^T \mathcal{D} \, {\rm d} t
As for the work developed by the external forces, the contribution to the energy is added at each time step. Finally, the total energy of the sytem is given by:
.. math::
E_{tot} = E_{elas}+E_{kin}+E_{damp}-E_{ext}
When the time evolution loop is finished, the evolution of the tip displacement as well as the different contributions of the energy are plotted as functions of time::
if MPI.comm_world.size == 1:
# Plot tip displacement evolution
plt.figure()
plt.plot(time, u_tip)
plt.xlabel("Time")
plt.ylabel("Tip displacement")
plt.ylim(-0.5, 0.5)
plt.show()
if (MPI.comm_world.rank == 0):
# Plot energies evolution
plt.figure()
plt.plot(time, energies)
plt.legend(("elastic", "kinetic", "damping", "total"))
plt.xlabel("Time")
plt.ylabel("Energies")
plt.ylim(0, 0.0011)
plt.show()
---------------------
Analyzing the results
---------------------
We first consider the case of zero Rayleigh damping :math:`\eta_M=\eta_K=0`. In this case, it can be observed that the evolution of the total energy depends
on the choice of the time-stepping scheme parameters. With :math:`\alpha_m=\alpha_f=0`, we recover the Newmark-:math:`\beta` method with :math:`\beta=0.25,\gamma=0.5`.
This scheme is known for being conservative. This can be observed (figure-left) in the constant total energy for :math:`t\geq T_c` when the loading is removed. On the contrary,
for non zero alpha parameters, e.g. :math:`\alpha_m=0.2, \alpha_f=0.4`, it can be observed (figure-right) that the energy is decreasing during this phase, indicating numerical damping.
For both cases, the scheme is unconditionally stable. Moreover, these differences vanish when reducing the time step.
.. figure:: elastodynamics-energies_newmark.png
:width: 60%
:align: center
Newmark-:math:`\beta` method :math:`\alpha_m=\alpha_f=0`
.. figure:: elastodynamics-energies_generalized_alpha.png
:width: 60%
:align: center
Generalized-:math:`\alpha` with :math:`\alpha_m=0.2, \alpha_f=0.4`
For non-zero Rayleigh damping :math:`\eta_M=\eta_K=0.01`, the total energy including viscous dissipation tends to oscillate around a constant value, with oscillations vanishing for decreasing time steps.
.. image:: elastodynamics-energies_generalized_alpha_damping.png
:width: 60%
:align: center
-----------
References
-----------
.. [ERL2002] Silvano Erlicher, Luca Bonaventura, Oreste Bursi. The analysis of the Generalized-alpha method for non-linear dynamic problems. Computational Mechanics, Springer Verlag, 2002, 28, pp.83-104, doi:10.1007/s00466-001-0273-z
|