Clamped and face-clamped edges#
A clamped edge of the third-order shear deformation theory (TSDT) removes
the slope of \(w\) normal to the edge together with the rotations, x1wr =
0 and x1phix = x1phiy = 0 on \(x = 0\), such that the cross-section
stays planar and unrotated; an edge clamped by its faces, \(u = v = 0\) at
\(z = d \mp h/2\) and \(w = 0\), leaves the section free to warp and is
prescribed with the keyword z of Shell.add_distr_pd_fixed_x(),
see Boundary conditions. The square plates below, \(h/a = 0.1\), are
clamped at \(x = 0, a\), free at \(y = 0, b\), and loaded by a uniform
pressure:
def plate(model, offset=0., m=10, n=10):
r"""Square isotropic plate, `h/a = 0.1`, all the flags ``1``, under a
uniform pressure"""
s = Shell(model=model, a=A, b=B, stack=[0.], plyt=H, laminaprop=(E, NU),
offset=offset, m=m, n=n)
for edge in ('x1', 'x2', 'y1', 'y2'):
for field in FIELDS:
setattr(s, edge + field, 1.)
setattr(s, edge + field + 'r', 1.)
s.add_pressure_load(Q)
return s
def clamp_by_flags(s, edge):
r"""Clamped edge by the boundary condition flags: `u, v, w` and the
rotations, plus the slope of `w` normal to the edge for the CLPT and the
TSDT"""
for field in FIELDS:
setattr(s, edge + field, 0.)
if 'fsdt' not in s.model:
setattr(s, edge + 'wr', 0.)
def face_clamp(s, x, h, k=1., cte=True):
r"""Face clamp of the edge at ``x``: `u = v = 0` at the faces `z = d \mp
h/2` and `w = 0`, prescribed with :meth:`.Shell.add_distr_pd_fixed_x`
and the keyword ``z``"""
zero = lambda y: 0.
for z in (s.offset - h/2, s.offset + h/2):
s.add_distr_pd_fixed_x(x, ku=k, kv=k, funcu=zero, funcv=zero, z=z,
cte=cte)
s.add_distr_pd_fixed_x(x, kw=k, funcw=zero, cte=cte)
For the CLPT and the FSDT the face clamp is the clamp of the flags:
@pytest.mark.parametrize('model', ['plate_clpt_donnell', 'plate_fsdt_donnell'])
def test_face_clamp_equals_clamp_for_clpt_and_fsdt(model):
r"""For the CLPT and the FSDT `u(z)` is linear in `z`: `u = v = 0` at
both faces is the clamp of the flags"""
s = plate(model)
for x in (0., A):
face_clamp(s, x, H)
_, c = static(s)
t = plate(model)
clamp_by_flags(t, 'x1')
clamp_by_flags(t, 'x2')
_, ct = static(t)
xs, ys = np.meshgrid(np.linspace(0, A, 7), np.linspace(0, B, 5))
f = fields(s, c, xs.ravel(), ys.ravel())
ft = fields(t, ct, xs.ravel(), ys.ravel())
for k in FIELDS:
assert np.allclose(f[k], ft[k], rtol=0, atol=1e-8*np.abs(ft['w']).max())
For the TSDT, \(u(z) = 0\) prescribed at a third coordinate gives the planar clamp of the flags, which the former clamp with the slope of \(w\) free does not:
@pytest.mark.parametrize('offset', [0., 0.3*H])
def test_planar_clamp_is_u_zero_at_three_coordinates(offset):
r"""For the TSDT `u(z)` depends on three constants, `u`, `\phi_x` and
`w_{,x}`: `u = v = 0` at three coordinates `z`, here the faces and `z =
d + h/4`, gives `u(z) = v(z) = 0` for every `z`, the clamp of the flags
``x1u = x1v = x1w = x1phix = x1phiy = x1wr = 0``; the mid-surface `z =
d` would not do, `u(d) = 0` following from the faces. Without ``x1wr =
0`` the edge is softer"""
model = 'plate_tsdt_donnell'
s = plate(model, offset)
zero = lambda y: 0.
for x in (0., A):
face_clamp(s, x, H)
s.add_distr_pd_fixed_x(x, ku=1., kv=1., funcu=zero, funcv=zero,
z=offset + H/4)
_, c = static(s)
t = plate(model, offset)
clamp_by_flags(t, 'x1')
clamp_by_flags(t, 'x2')
_, ct = static(t)
xs, ys = np.meshgrid(np.linspace(0, A, 7), np.linspace(0, B, 5))
f = fields(s, c, xs.ravel(), ys.ravel())
ft = fields(t, ct, xs.ravel(), ys.ravel())
for k in FIELDS:
assert np.allclose(f[k], ft[k], rtol=0, atol=1e-8*np.abs(ft['w']).max())
# the former clamp of the shear deformation theories, with w,x free
t.x1wr = t.x2wr = 1.
_, cf = static(t)
assert center_w(t, cf) > 1.05*center_w(t, ct)
The face clamp lies between the planar clamp and the simple support, here with the central deflections \(w D/(q a^4)\) = 0.002901 (planar clamp), 0.002953 (face clamp) and 0.013459 (simple support): the displacements of both faces vanish along the edges, whereas the section warps, with \(\phi_x = w_{,x}/2\):
def test_face_clamp_tsdt_thick_plate():
r"""TSDT plate, `h/a = 0.1`, clamped at `x = 0, a` and free at `y = 0,
b`: the face clamp is softer than the planar clamp and stiffer than the
simple support; `u` and `v` vanish at both faces along the clamped
edges, whereas the section warps, `\phi_x = w_{,x}/2`, i.e. `\gamma_{xz}
= 3 \phi_x`, with `u(z) \neq 0` between the faces"""
model = 'plate_tsdt_donnell'
s = plate(model)
for x in (0., A):
face_clamp(s, x, H)
_, c = static(s)
planar = plate(model)
clamp_by_flags(planar, 'x1')
clamp_by_flags(planar, 'x2')
_, cpl = static(planar)
ss = plate(model)
for edge in ('x1', 'x2'):
for field in ('u', 'v', 'w', 'phiy'):
setattr(ss, edge + field, 0.)
_, css = static(ss)
w_face = center_w(s, c)
w_planar = center_w(planar, cpl)
w_ss = center_w(ss, css)
assert w_planar < 0.99*w_face
assert w_face < 0.99*w_ss
# central deflections w D/(q a^4) of the documentation
D = E*H**3/(12*(1 - NU**2))
for w, ref in ((w_planar, 0.002901), (w_face, 0.002953), (w_ss, 0.013459)):
assert np.isclose(w*D/(Q*A**4), ref, rtol=2e-4)
ys = np.linspace(0, B, 11)
for x in (0., A):
f = fields(s, c, np.full_like(ys, x), ys)
# the face displacement inside the plate
scale = np.abs(uv_at(s, fields(s, c, np.full_like(ys, A/4), ys),
H/2, H)[0]).max()
for z in (-H/2, H/2):
u, v = uv_at(s, f, z, H)
assert np.abs(u).max() < 1e-8*scale
assert np.abs(v).max() < 1e-8*scale
assert np.abs(f['w']).max() < 1e-8*w_face
# warping: phi = w,x/2 and u(z) = (w,x/2) zeta (1 - 4 zeta^2/h^2)
assert np.allclose(f['gxz'], 3*f['phix'], rtol=1e-6, atol=0)
u, _ = uv_at(s, f, H/4, H)
assert np.abs(u).max() > 1e3*np.abs(uv_at(s, f, H/2, H)[0]).max()
assert np.allclose(u, f['gxz']/3*H/4*(1 - 4*(H/4)**2/H**2),
rtol=1e-6, atol=0)
Both clamps are defined by the physical section, and the results do not depend on the reference surface of the laminate:
def test_face_clamp_does_not_depend_on_the_reference_surface():
r"""The faces are physical surfaces: the face-clamped homogeneous plate
gives the same deflection for the reference surface at the
mid-surface, `d = 0`, and at `d = 0.3 h` or `d = h/2`, as the planar
clamp of the flags"""
model = 'plate_tsdt_donnell'
xs, ys = np.meshgrid(np.linspace(0, A, 5), np.linspace(0, B, 5))
res = {}
for offset in (0., 0.3*H, 0.5*H):
s = plate(model, offset)
for x in (0., A):
face_clamp(s, x, H)
_, c = static(s)
t = plate(model, offset)
clamp_by_flags(t, 'x1')
clamp_by_flags(t, 'x2')
_, ct = static(t)
res[offset] = (fields(s, c, xs.ravel(), ys.ravel())['w'],
fields(t, ct, xs.ravel(), ys.ravel())['w'])
for offset in (0.3*H, 0.5*H):
for i in (0, 1):
assert np.allclose(res[offset][i], res[0.][i], rtol=1e-7, atol=0)
assert res[0.][1].max() < 0.99*res[0.][0].max()
With the penalty method, the kernels of the prescribed displacements are
given the same z:
@pytest.mark.parametrize('offset', [0., 0.3*H])
def test_face_clamp_penalty_and_null_space_agree(offset):
r"""Face clamp at `x = 0` and the faces of `x = a` pushed by `\delta`,
`u(d \mp h/2) = -\delta`, with the pressure: the penalty method, with the
kernels of the prescribed displacements at the same `z`, converges to
the null-space method"""
model = 'plate_tsdt_donnell'
delta = 1e-5
def build(k):
s = plate(model, offset)
face_clamp(s, 0., H, k=k)
for z in (offset - H/2, offset + H/2):
s.add_distr_pd_fixed_x(A, ku=k, kv=k, funcu=lambda y: -delta,
funcv=lambda y: 0., z=z)
s.add_distr_pd_fixed_x(A, kw=k, funcw=lambda y: 0.)
s._rebuild()
return s
s = build(1.)
_, c = static(s)
k = 1e4*E
p = build(k)
md = MultiDomain([p], conn=[])
size = md.get_size()
kCp = 0
for x in (0., A):
for z in (offset - H/2, offset + H/2):
kCp = kCp + fkCld_xcte(k, k, 0., p, x, size, 0, 0, z=z)
kCp = kCp + fkCld_xcte(0., 0., k, p, x, size, 0, 0)
K = md.calc_kC() + finalize_symmetric_matrix(kCp)
cp = solve(K, md.calc_fext(), silent=True)
xs, ys = np.meshgrid(np.linspace(0, A, 6), np.linspace(0, B, 5))
f = fields(s, c, xs.ravel(), ys.ravel())
fp = fields(p, cp, xs.ravel(), ys.ravel())
for key in ('u', 'w'):
assert np.allclose(fp[key], f[key], rtol=0,
atol=1e-3*np.abs(f[key]).max())
# the faces of x = a are at -delta
f = fields(s, c, np.full(5, A), np.linspace(0, B, 5))
for z in (offset - H/2, offset + H/2):
assert np.allclose(uv_at(s, f, z, H)[0], -delta, rtol=1e-8)