Mass and centre of gravity of a plate with uniform and graded thickness
The rectangular plate has L = 10, W = 2, density rho = 3, and base
thickness t0 = 0.2. It checks a uniform thickness and the field
t(x) = t0 (1 + x/L), constructed with :func:hm.from_function.
The closed forms are m_uniform = rho W t0 L and cg_uniform = (L/2, W/2, 0);
for the graded field they are m = 3 rho W t0 L / 2 and
cg = (5 L / 9, W/2, 0). The residual gate is zero-reference and prints mass
and every centre-of-gravity component for both cases. Its tight tolerance also
guards the user-node to FE-degree-of-freedom ordering path used by the field.
"""Mass and centre of gravity of a plate with uniform and graded thickness
The rectangular plate has ``L = 10``, ``W = 2``, density ``rho = 3``, and base
thickness ``t0 = 0.2``. It checks a uniform thickness and the field
``t(x) = t0 (1 + x/L)``, constructed with :func:`hm.from_function`.
The closed forms are ``m_uniform = rho W t0 L`` and ``cg_uniform = (L/2, W/2, 0)``;
for the graded field they are ``m = 3 rho W t0 L / 2`` and
``cg = (5 L / 9, W/2, 0)``. The residual gate is zero-reference and prints mass
and every centre-of-gravity component for both cases. Its tight tolerance also
guards the user-node to FE-degree-of-freedom ordering path used by the field.
"""
import pathlib
import sys
import csdl_alpha as csdl
import numpy as np
import hermit as hm
sys.path.insert(0, str(pathlib.Path(__file__).parent))
from _geometry import rect_plate # noqa: E402
from _harness import Case, main # noqa: E402
L, W, T0, RHO = 10.0, 2.0, 0.2, 3.0
UNIFORM_MASS = RHO * W * T0 * L
GRADED_MASS = 1.5 * UNIFORM_MASS
UNIFORM_CG = np.array([L / 2.0, W / 2.0, 0.0])
GRADED_CG = np.array([5.0 * L / 9.0, W / 2.0, 0.0])
def _outputs(domain, thickness):
material = hm.isotropic(domain, E=1.0, nu=0.0, thickness=thickness, density=RHO)
state = hm.solve(domain, material, hm.traction(domain, [0.0, 0.0, 0.0]),
hm.clamp(domain, where=lambda x: np.isclose(x[0], 0.0)))
return float(hm.mass(state).value[0]), np.asarray(hm.center_of_gravity(state).value)
def solve_at(n):
"""Maximum absolute residual for both exact mass/CG cases on an ``n x n/2`` mesh."""
mesh = rect_plate(L, W, nx=n, ny=n // 2, cell="quad")
rec = csdl.Recorder(inline=True)
rec.start()
domain = hm.ShellDomain(mesh, element="CG2CG1")
uniform = hm.constant(domain, ("Lagrange", 1), T0)
graded = hm.from_function(domain, ("Lagrange", 1),
lambda xyz: T0 * (1.0 + xyz[:, 0] / L))
mass_u, cg_u = _outputs(domain, uniform)
mass_g, cg_g = _outputs(domain, graded)
rec.stop()
print(f" uniform: mass={mass_u:.12e}, cg={cg_u}")
print(f" graded : mass={mass_g:.12e}, cg={cg_g}")
return max(abs(mass_u - UNIFORM_MASS), abs(mass_g - GRADED_MASS),
float(np.max(abs(cg_u - UNIFORM_CG))), float(np.max(abs(cg_g - GRADED_CG))))
CASE = Case(
name="Plate mass and centre of gravity",
quantity="maximum absolute residual across uniform and graded exact mass/CG",
reference=0.0,
tolerance=1e-10,
citation="Closed-form area integrals computed in this file",
levels=(2, 4, 8),
quick_level=2,
solve=solve_at,
monotone=False,
notes="zero reference means every reported mass and CG component matches its integral",
)
if __name__ == "__main__":
main(CASE)