Boundary conditions#
The boundary conditions of a domain are imposed in three ways, from the most to the least common:
on whole edges, exactly, with the flags of the approximation functions, attributes of
Shell, see Bardell’s hierarchical functions;at points or along lines, as prescribed displacements, with the null-space or the penalty method of a
MultiDomain, see Prescribed displacements imposed exactly, or as penalty springs at points of a single shell,Shell.calc_stiffness_point_constraint();between domains, with the connections of a
MultiDomain, see Multi-domain assemblies.
Flags of the edges#
A flag 0 removes the value (e.g. x1w) or the slope normal to the
edge (e.g. x1wr, the derivative with respect to \(x\) at \(x = 0\)) of a
field on the whole edge, and a flag 1 leaves it free, the natural
boundary condition following from the energy. The edges are \(x = 0\)
(x1), \(x = a\) (x2), \(y = 0\) (y1) and \(y = b\) (y2). For the
models based on the classical laminated plate theory (CLPT), on the edge \(x
= 0\), with \(v\) the displacement tangential to the edge and \(u\) the normal
one:
condition |
flags equal to |
|---|---|
simply supported, \(u\) and \(v\) restrained |
|
simply supported, \(u\) free |
|
clamped |
|
free |
none |
symmetry plane |
|
and likewise on the other edges, with the roles of \(u\) and \(v\) exchanged on
the edges \(y = const\). With the default flags, a Shell is simply
supported on its four edges with the in-plane displacements restrained.
The models based on shear deformation theories, FSDT and TSDT, have the
rotations \(\phi_x\) and \(\phi_y\) as independent fields, with the flags
x1phix, x1phixr, …, y2phiyr. By default only the rotation
tangential to each edge is removed, x1phiy = x2phiy = 0 and y1phix =
y2phix = 0, the hard simply supported condition. A clamped edge keeps
the cross-section planar and unrotated, \(u(z) = v(z) = 0\) through the
thickness, which removes on the edge \(x = 0\):
model |
flags of the clamped edge \(x = 0\) equal to |
|---|---|
CLPT |
|
FSDT |
|
TSDT |
|
The rotation of the FSDT is constant through the thickness, and its slope
of \(w\) stays free. The in-plane displacement of the TSDT, \(u(z) = u + z
\phi_x + p(z) (\phi_x + w_{,x})\) with the cubic \(p(z)\) of Eq.
(3), also contains the slope \(w_{,x}\), which must be
removed for \(u(z)\) to vanish at every \(z\); the rotation at the reference
surface, \(\theta_x\) of Eq. (5), is then zero for any
offset. The meaning of the default flags is described in Shell.
The function set_edge of the tests sets the simply supported (with the
normal displacement free), clamped and free edges of the three plate models
in this way:
def set_edge(s, edge, kind):
r"""Boundary condition of one edge
``kind`` is ``'S'`` for simply supported (SS-1), ``'C'`` for clamped or
``'F'`` for free. The tangential displacement and rotation of an edge of
constant `x` are `v` and `\phi_y`, and of constant `y`, `u` and
`\phi_x`. The clamped edge removes `u, v, w`, the slope of `w` normal
to the edge for the CLPT, the rotations for the FSDT, and both the
rotations and the slope for the TSDT.
"""
tangential, normal = ('v', 'u') if edge.startswith('x') else ('u', 'v')
rot_tangential, rot_normal = (('phiy', 'phix') if edge.startswith('x')
else ('phix', 'phiy'))
for field in ('u', 'v', 'w', 'phix', 'phiy'):
setattr(s, edge + field, 1.)
setattr(s, edge + field + 'r', 1.)
if kind == 'S':
setattr(s, edge + tangential, 0.)
setattr(s, edge + 'w', 0.)
setattr(s, edge + rot_tangential, 0.)
elif kind == 'C':
for field in ('u', 'v', 'w', 'phix', 'phiy'):
setattr(s, edge + field, 0.)
# the classical plate theory clamps the slope of w and the FSDT the
# rotations, with the slope free; the TSDT clamps both, such that
# the warping term p(z) (phi + w,n) of u(z) and v(z) vanishes too
# and the section stays planar and unrotated
if 'clpt' in s.model or 'tsdt' in s.model:
setattr(s, edge + 'wr', 0.)
elif kind != 'F':
raise ValueError(kind)
and the natural frequencies of thin isotropic square plates with combinations of these edges agree with the reference values of Leissa (1973) given in the test, for the three models:
@pytest.mark.parametrize('model', [CLPT, FSDT, TSDT])
@pytest.mark.parametrize('bcs', list(LEISSA_PLATES))
def test_frequency_thin_isotropic_plate_leissa(bcs, model):
r"""Leissa (1973), square plates, `\lambda = \omega a^2 \sqrt{\rho h/D}`
For a thin plate, `h/a = 0.001`, the shear deformation theories must
recover the classical plate theory for any boundary condition.
"""
a = 1.
h = 0.001*a
E = 70.e9
nu = 0.3
s = make_plate(model, a, h, [0], (E, nu), bcs=bcs)
# removing the in-plane rigid-body motions, which do not change the
# flexural modes of the isotropic plate
s.x1u = s.x2u = 0.
D = E*h**3/(12*(1 - nu**2))
omega = lowest_frequencies(s)[0]
# the references are given with 5 significant digits
assert np.isclose(omega*a**2*np.sqrt(RHO*h/D), LEISSA_PLATES[bcs],
rtol=5e-5)
For these thin plates the slope \(w_{,x}\) of a TSDT clamped edge changes the
frequencies by less than \(1.2 \times 10^{-5}\), e.g. 28.95027 with the slope
free and 28.95059 with x1wr = 0 for the plate 'SCSC', against 28.951
of Leissa. For thick plates the difference is large: for the square
isotropic plate with \(h/a = 0.1\) clamped on four edges, the first frequency
\(\omega a^2 \sqrt{\rho h/D}\) of the TSDT is 30.18 with the slope free and
32.64 with the slope clamped, close to the 32.62 of the FSDT.
The removed terms leave null rows and columns in the matrices, which the
solvers of structsolve
remove before solving. A free domain, all flags 1, has rigid-body
modes, and its stiffness matrix is singular.
Prescribed displacements#
Displacements different from zero, or zero displacements at points or
along lines inside the domain, are prescribed with
Shell.add_point_pd(), Shell.add_distr_pd_fixed_x() and
Shell.add_distr_pd_fixed_y(), constant (cte=True) or proportional
to the load factor (cte=False). They are imposed by the
MultiDomain that contains the shell, also an assembly of one shell
without connections, with pd_method:
'null-space': exactly, as linear constraints eliminated together with the connections, the solution being \(\boldsymbol{c} = \boldsymbol{Z}\boldsymbol{c}_r + \boldsymbol{c}_{p0} + \lambda \boldsymbol{c}_{p1}\), with the reactions ofMultiDomain.reaction_pd()andMultiDomain.reaction_resultant(), see Prescribed displacements imposed exactly;'penalty', the default: with springs between the structure and the support, whose stiffness the user adds to the stiffness matrix, e.g. withfkCpd(), and whose forces enter the external force vector,MultiDomain.calc_fext().
A single Shell can be supported at a point by the penalty springs
of Shell.calc_stiffness_point_constraint(), added by the user to the
constitutive stiffness matrix.
The in-plane displacements \(u\) and \(v\) are prescribed at the reference
surface, \(z = 0\), or at the thickness coordinate given by the keyword z
of these methods, with the same z given to the penalty kernels.
The example below prescribes a uniform end shortening of a simply supported plate with the null-space method, and verifies that it gives the same critical state as the equivalent applied load:
@pytest.mark.slow
def test_linear_buckling_plate_end_shortening_equals_load():
r"""Simply supported plate, the lateral edges free in-plane: a uniform
end-shortening `\delta` and the equivalent load `N_x = E h \delta/a`
give the same membrane state and the same critical load"""
a, b, h = 0.3, 0.2, 1e-3
p = plate(a, b, h, m=10, n=10)
end_shortening(p, 1.)
p.add_point_pd(0., 0., 0, None, 1., 0., 0, None)
md = MultiDomain([p], conn=[], pd_method='null-space')
eigvals, eigvecs = md.linear_buckling(num_eigvalues=3)
assert eigvecs.shape == (p.get_size(), 3)
q = plate(a, b, h, m=10, n=10)
q.x1u = 0.
q.add_point_pd(0., 0., 0, None, 1., 0., 0, None)
q.add_distr_load_fixed_x(a, funcx=lambda y: -E*h/a)
mdq = MultiDomain([q], conn=[], pd_method='null-space')
eigvals_N, _ = mdq.linear_buckling(num_eigvalues=3)
assert np.allclose(eigvals, eigvals_N, rtol=1e-9)
# a constant end-shortening delta0 at x = a and an incremented one
# lbd*delta1 at x = 0 give the same uniform state when delta0 + lbd*delta1
# is the critical one
dcr = eigvals[0]
p.clear_disps()
p.add_distr_pd_fixed_x(0., ku=1., funcu=lambda y: dcr, cte=False)
p.add_distr_pd_fixed_x(a, ku=1., funcu=lambda y: -0.5*dcr, cte=True)
p.add_point_pd(0., 0., 0, None, 1., 0., 0, None)
eigvals_mixed, _ = md.linear_buckling(num_eigvalues=3)
assert np.isclose(eigvals_mixed[0], 0.5, rtol=1e-8)
# the same end-shortening, constant and incremented at the same line,
# is a conflict: the line has a single prescribed value
p.clear_disps()
p.add_distr_pd_fixed_x(a, ku=1., funcu=lambda y: -0.5*dcr, cte=True)
p.add_distr_pd_fixed_x(a, ku=1., funcu=lambda y: -dcr, cte=False)
with pytest.raises(ValueError, match='each set must be consistent'):
md.get_cp()
More examples are given in Prescribed displacements.
Face-clamped edges#
In tests, an edge is clamped by restraining the displacements of the bottom
and top faces of the laminate, \(z = d \mp h/2\), the section being free to
deform in between. This face clamp, \(u = v = 0\) at both faces and \(w = 0\),
is prescribed with the keyword z of Shell.add_distr_pd_fixed_x()
and Shell.add_distr_pd_fixed_y(); the flags of the edge are left
free, 1. With the in-plane displacement at \(z\), see
nullspace.pd_constraint_matrix(),
with \(c_1 = 4/(3 h^2)\) for the TSDT and \(c_1 = 0\) for the FSDT, the two faces give \(u = \phi_x = 0\) for the FSDT, and \(u = w_{,x} = 0\) for the CLPT, \(u(z) = u - z w_{,x}\): the face clamp is the clamp of the flags. For the TSDT the difference of the two faces gives \((h/3)(2 \phi_x - w_{,x}) = 0\) and their sum \(u = -(d/2)(1 - 4 d^2/h^2) w_{,x}\), two conditions instead of the three of the planar clamp; the section warps,
which no combination of flags can express. Along the edge \(w = 0\) gives \(w_{,y} = 0\), and the two faces then give \(v = \phi_y = 0\). The edge is softer than the planar clamp and stiffer than the simple support, and, being defined by the physical faces, it does not depend on the reference surface. See the example Clamped and face-clamped edges.