"""Clamped-clamped beam under central point load.
Reference geometry
------------------
Prismatic slender beam of length :math:`L`, rectangular cross-
section, clamped rigidly at *both* ends (every DOF of the end
faces fixed) and loaded by a central transverse point load
:math:`P` at :math:`x = L/2`.
Closed-form quantities
----------------------
The fixed-end boundary conditions carry extra rotational
constraints that reduce the mid-span deflection by a factor of
4 compared to the simply-supported case:
.. math::
\\delta_{\\text{mid}} = \\frac{P L^{3}}{192 E I}, \\qquad
I = \\frac{w h^{3}}{12}.
The clamping moments at each end equal :math:`\\pm P L / 8`.
References
----------
* Timoshenko, S. P. *Strength of Materials, Part I*, 3rd ed.,
Van Nostrand, 1955, §5.7 — fixed-end beam with central load.
* Gere & Goodno (2018), *Mechanics of Materials* 9th ed., §10.3
Table 10-1 — fixed-fixed beam deflection formulas.
Cross-references (public verification manuals — fair-use
citation of problem IDs only; no vendor content vendored):
* **Abaqus AVM 1.5.x** fixed-fixed beam family.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
import numpy as np
import pyvista as pv
import femorph_solver
from femorph_solver import ELEMENTS
from femorph_solver.validation._benchmark import (
BenchmarkProblem,
PublishedValue,
)
[docs]
@dataclass
class ClampedClampedBeamCentralLoad(BenchmarkProblem):
"""Clamped-clamped beam mid-span deflection under central point load."""
name: str = "cc_beam_central_load"
description: str = (
"Prismatic clamped-clamped beam with central transverse "
"point load — Euler-Bernoulli closed-form "
"(Timoshenko 1955 §5.7)."
)
analysis: str = "static"
#: Beam length [m].
L: float = 1.0
#: Cross-section width [m].
width: float = 0.05
#: Cross-section height [m].
height: float = 0.05
#: Young's modulus [Pa].
E: float = 2.0e11
#: Poisson's ratio.
nu: float = 0.3
rho: float = 7850.0
#: Total central transverse load [N] (acts in ``-z``).
P: float = 1.0e3
@property
def published_values(self) -> tuple[PublishedValue, ...]:
I = self.width * self.height**3 / 12.0 # noqa: E741
delta = self.P * self.L**3 / (192.0 * self.E * I)
return (
PublishedValue(
name="mid_span_deflection",
value=delta,
unit="m",
source="Timoshenko 1955 §5.7",
formula="delta = P L^3 / (192 E I)",
tolerance=0.05,
),
)
[docs]
def build_model(self, **mesh_params: Any) -> femorph_solver.Model:
nx = int(mesh_params.get("nx", 40))
if nx % 2 != 0:
nx += 1
ny = int(mesh_params.get("ny", 3))
nz = int(mesh_params.get("nz", 3))
xs = np.linspace(0.0, self.L, nx + 1)
ys = np.linspace(0.0, self.width, ny + 1)
zs = np.linspace(0.0, self.height, nz + 1)
grid = pv.StructuredGrid(
*np.meshgrid(xs, ys, zs, indexing="ij")
).cast_to_unstructured_grid()
m = femorph_solver.Model.from_grid(grid)
m.assign(
ELEMENTS.HEX8(integration="enhanced_strain"),
material={"EX": self.E, "PRXY": self.nu, "DENS": self.rho},
)
pts = np.asarray(m.grid.points)
# Full clamp on both end faces — pinning every DOF at
# x = 0 and x = L. Unlike the SS case, the fixed-fixed
# configuration locks out both end rotations and end
# translations, so no knife-edge idealisation is needed.
both_ends = np.where((pts[:, 0] < 1e-9) | (pts[:, 0] > self.L - 1e-9))[0]
m.fix(nodes=(both_ends + 1).tolist(), dof="ALL")
# Central point load on the mid-span bottom line.
x_mid = 0.5 * self.L
load_nodes = np.where((np.abs(pts[:, 0] - x_mid) < 1e-9) & (pts[:, 2] < 1e-9))[0]
fz_per_node = -self.P / len(load_nodes)
for n in load_nodes:
m.apply_force(int(n + 1), fz=fz_per_node)
# Extract from the top-face centerline to avoid the
# local-load indentation.
m._bench_midspan_top_nodes = np.where(
(np.abs(pts[:, 0] - x_mid) < 1e-9) & (pts[:, 2] > self.height - 1e-9)
)[0] # type: ignore[attr-defined]
return m