Source code for felupe.assembly._axi
# -*- coding: utf-8 -*-
"""
This file is part of FElupe.
FElupe is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FElupe is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FElupe. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
from ..field import Field, FieldAxisymmetric
from ._cartesian import IntegralFormCartesian
[docs]
class IntegralFormAxisymmetric(IntegralFormCartesian):
r"""An Integral Form for axisymmetric fields.
Parameters
----------
fun : array
The pre-evaluated function array. If the array is not contiguous, a C-contiguous
copy is made.
v : FieldAxisymmetric
The axisymmetric test field.
dV : array
The differential volumes.
u : FieldAxisymmetric, optional
If a field is passed, a bilinear form is created (default is None).
grad_v : bool, optional
Flag to activate the gradient on the test field ``v`` (default is False).
grad_u : bool, optional
Flag to activate the gradient on the trial field ``u`` (default is False).
Notes
-----
Axisymmetric integral forms are created with a two-dimensional mesh, a two-
dimensional element formulation and an axisymmetric field.
.. note::
The rotation axis is chosen along the global X-axis
:math:`(X,Y,Z) \widehat{=} (Z,R,\varphi)`.
The three-dimensional deformation gradient consists of four in-plane components and
one additional non-zero entry for the out-of-plane stretch, which is equal to the
ratio of the deformed and the undeformed radius.
.. math::
\boldsymbol{F} = \begin{bmatrix}
\boldsymbol{F}_{(2D)} & \boldsymbol{0} \\
\boldsymbol{0}^T & \frac{r}{R}
\end{bmatrix}
The variation of the deformation gradient consists of both in- and out-of-plane
contributions.
.. math::
\delta \boldsymbol{F}_{(2D)} = \delta \left( \frac{
\partial \boldsymbol{u}}{\partial \boldsymbol{X}
} \right) \qquad \text{and} \qquad \delta \left(\frac{r}{R}\right)
= \frac{\delta u_r}{R}
Again, the internal virtual work leads to two seperate terms.
.. math::
-\delta W_{int} = \int_V \boldsymbol{P} : \delta \boldsymbol{F} \ dV
= \int_V \boldsymbol{P}_{(2D)} : \delta \boldsymbol{F}_{(2D)} \ dV
+ \int_V \frac{P_{33}}{R} : \delta u_r \ dV
The differential volume is further expressed as a product of the differential
in-plane area and the differential arc length. The arc length integral is finally
pre-evaluated.
.. math::
\int_V dV = \int_{\varphi=0}^{2\pi} \int_A R\ dA\ d\varphi = 2\pi \int_A R\ dA
Inserting the differential volume integral into the expression of internal virtual
work, this leads to:
.. math::
-\delta W_{int} = 2\pi \int_A \boldsymbol{P}_{(2D)}
: \delta \boldsymbol{F}_{(2D)} \ R \ dA
+ 2\pi \int_A P_{33} : \delta u_r \ dA
A Linearization of the internal virtual work expression gives four terms.
.. math::
-\Delta \delta W_{int} &= \Delta_{(2D)} \delta_{(2D)} W_{int}
+ \Delta_{33} \delta_{(2D)} W_{int} + \Delta_{(2D)} \delta_{33} W_{int}
+ \Delta_{33} \delta_{33} W_{int}
-\Delta_{(2D)} \delta_{(2D)} W_{int} &= 2\pi \int_A
\delta \boldsymbol{F}_{(2D)} : \mathbb{A}_{(2D),(2D)} :
\Delta \boldsymbol{F}_{(2D)} \ R \ dA
-\Delta_{33} \delta_{(2D)} W_{int} &= 2\pi \int_A
\delta \boldsymbol{F}_{(2D)} : \mathbb{A}_{(2D),33} : \Delta u_r \ dA
-\Delta_{(2D)} \delta_{33} W_{int} &= 2\pi \int_A
\delta u_r : \mathbb{A}_{33,(2D)} : \Delta \boldsymbol{F}_{(2D)} \ dA
-\Delta_{33} \delta_{33} W_{int} &= 2\pi \int_A
\delta u_r : \frac{\mathbb{A}_{33,33}}{R} : \Delta u_r \ dA
with
.. math::
\mathbb{A}_{(2D)~(2D)} &= \frac{
\partial \psi}{\partial \boldsymbol{F}_{(2D)}~\partial \boldsymbol{F}_{(2D)}
}
\mathbb{A}_{(2D)~33} &= \frac{
\partial \psi}{\partial \boldsymbol{F}_{(2D)}\ \partial F_{33}}
\left ( = \mathbb{A}_{33~(2D)} \right )
\mathbb{A}_{33~33} &= \frac{\partial \psi}{\partial F_{33}~\partial F_{33}}
See Also
--------
felupe.IntegralForm : Mixed-field integral form container with methods for
integration and assembly.
felupe.assembly.IntegralFormCartesian : Single-field integral form.
"""
def __init__(self, fun, v, dV, u=None, grad_v=True, grad_u=True):
R = v.radius
self.dV = 2 * np.pi * R * dV
if u is None:
if isinstance(v, FieldAxisymmetric):
self.mode = 1
if grad_v:
fun_2d = fun[:-1, :-1]
fun_zz = fun[(-1,), (-1,)] / R
else:
fun_2d = fun[:-1]
fun_zz = fun[-1].reshape(1, *fun[-1].shape) / R
form_a = IntegralFormCartesian(fun_2d, v, self.dV, grad_v=grad_v)
form_b = IntegralFormCartesian(fun_zz, v.scalar, self.dV)
self.forms = [form_a, form_b]
else:
self.mode = 10
form_a = IntegralFormCartesian(fun, v, self.dV, grad_v=False)
self.forms = [
form_a,
]
else:
if isinstance(v, FieldAxisymmetric) and isinstance(u, FieldAxisymmetric):
self.mode = 2
if grad_v and grad_u:
form_aa = IntegralFormCartesian(
fun[:-1, :-1, :-1, :-1], v, self.dV, u, True, True
)
form_bb = IntegralFormCartesian(
fun[-1, -1, -1, -1] / R**2,
v.scalar,
self.dV,
u.scalar,
False,
False,
)
form_ba = IntegralFormCartesian(
fun[-1, -1, :-1, :-1] / R, v.scalar, self.dV, u, False, True
)
form_ab = IntegralFormCartesian(
fun[:-1, :-1, -1, -1] / R, v, self.dV, u.scalar, True, False
)
if not grad_v and grad_u:
form_aa = IntegralFormCartesian(
fun[:-1, :-1, :-1], v, self.dV, u, False, True
)
form_bb = IntegralFormCartesian(
fun[-1, -1, -1] / R**2,
v.scalar,
self.dV,
u.scalar,
False,
False,
)
form_ba = IntegralFormCartesian(
fun[-1, :-1, :-1] / R, v.scalar, self.dV, u, False, True
)
form_ab = IntegralFormCartesian(
fun[:-1, -1, -1] / R, v, self.dV, u.scalar, False, False
)
self.forms = [form_aa, form_bb, form_ba, form_ab]
elif isinstance(v, FieldAxisymmetric) and isinstance(u, Field):
self.mode = 30
form_a = IntegralFormCartesian(
fun[:-1, :-1], v, self.dV, u, True, False
)
form_b = IntegralFormCartesian(
fun[-1, -1] / R, v.scalar, self.dV, u, False, False
)
self.forms = [form_a, form_b]
elif isinstance(v, Field) and isinstance(u, Field):
self.mode = 40
form_a = IntegralFormCartesian(fun, v, self.dV, u, False, False)
self.forms = [
form_a,
]
[docs]
def integrate(self, parallel=False, out=None):
values = [form.integrate(parallel=parallel) for form in self.forms]
if self.mode == 1:
values[0] += np.pad(values[1], ((0, 0), (1, 0), (0, 0)))
val = values[0]
if self.mode == 30:
if len(values[0].shape) > 4:
values[0] = values[0][:, :, 0, 0]
if len(values[1].shape) > 4:
values[1] = values[1][:, :, 0, 0]
a, b, e = values[1].shape
values[1] = values[1].reshape(a, 1, b, e)
values[0] += np.pad(values[1], ((0, 0), (1, 0), (0, 0), (0, 0)))
val = values[0]
elif self.mode == 2:
a, b, e = values[1].shape
values[1] = values[1].reshape(a, 1, b, 1, e)
values[1] = np.pad(values[1], ((0, 0), (1, 0), (0, 0), (1, 0), (0, 0)))
a, b, i, e = values[2].shape
values[2] = values[2].reshape(a, 1, b, i, e)
values[2] = np.pad(values[2], ((0, 0), (1, 0), (0, 0), (0, 0), (0, 0)))
a, i, b, e = values[3].shape
values[3] = values[3].reshape(a, i, b, 1, e)
values[3] = np.pad(values[3], ((0, 0), (0, 0), (0, 0), (1, 0), (0, 0)))
for i in range(1, len(values)):
values[0] += values[i]
val = values[0]
elif self.mode == 10 or self.mode == 40:
val = values[0]
return val
[docs]
def assemble(self, values=None, parallel=False, out=None):
if values is None:
values = self.integrate(parallel=parallel, out=out)
return self.forms[0].assemble(values)