hermit.fenics.shell_pde

ShellPDE – function spaces + UFL form factories for the R-M shell.

Pure FEniCSx (DOLFINx 0.9 / 0.11), no CSDL. Port of rm_shell_pde_composite.py (femo dev_coupling), unified ABD backend. The argument functions live on the object so the custom operations can update them by array between assemblies.

Classes

ShellPDE

W=, given, is an existing mixed state space to reuse instead of

Module Contents

class hermit.fenics.shell_pde.ShellPDE(mesh, element='CG2CG1', element_wise_material=False, elementwise_pressure=False, W=None, quadrature_degree=4)

W=, given, is an existing mixed state space to reuse instead of building a fresh state_space(mesh, element) – hm.solve (via hermit._solve._pde_for) passes domain.W here so the residual / BCs / ops it builds all share the one FunctionSpace object a BoundaryConditions was compiled against (a strong DirichletBC located against a structurally identical but distinct FunctionSpace is silently dropped, not an error).

area_form(measure=ufl.dx)
cg_forms(thickness=None, density=None)

Mass moments, optionally from arbitrary-space coefficients (else the fixed density / h forms).

coefficient(name, space) → dolfinx.fem.Function

Persistent Function for name on space, memoised by (name, normalized space) – repeated calls with the same arguments return the exact same object. That matters: a residual form built against one Function instance is wrong (or raises, for a strong BC – see ShellDomain’s class docstring) if later evaluated with a different instance, and the custom ops write into the coefficient by array every solve, so it must be the one the compiled form actually references.

General seam for any per-mesh input field a form needs as a UFL coefficient on a space the caller picks (materials, loads, orientation, …) rather than one of the PDE’s fixed-space attributes (self.A / self.h / …).

compliance_form(loads=None)

loads=None (default) – the work conjugate of the fixed self.f/self.m VF-space coefficients. loads, given, is the same (kind, Function) list residual_form takes – the matching compliance term per load kind (inner(u, f) / inner(theta, m) / p * dot(n, u)), so this stays the exact work conjugate of residual_form’s distributed terms (every load kind must contribute both a residual term and a compliance term, or the two drift apart silently). The point_load/load_vector direct-RHS contribution is not included here (it has no distributed form) – callers add csdl.vdot(direct, w).

elastic_energy_form(orientation=None, material=None)
elastic_model(w=None, orientation=None, material=None)

orientation is (name, Function) with name in ("fiber_angle", "fiber_direction"), or None. The no-orientation path reuses the memoised ElasticModel (bit-identical to the unoriented form); an oriented call always builds a fresh one – it is only built once per ShellSolveOp / ShellScalarFormsOp construction (themselves memoised by the structural form cache), so there is nothing to gain by caching it too.

material, given, is a (A, B, D, As) tuple of Functions overriding the PDE’s own fixed-space self.A/self.B/self.D/ self.As: a caller-chosen constitutive_space (any space, via coefficient()) reaches the solve instead of only the fixed VABD/VAs space element_wise_material picks. None (default) keeps the fixed-space attrs – also why the material=None unoriented path is the only one eligible for the self._elastic memo (a material= override is only ever built once per op anyway, same as an oriented call).

force_to_pressure_matrix()

VF consistent mass matrix M such that M @ pressure = consistent nodal force.

mass_form(thickness=None, density=None)

thickness=None/density=None (default) use the fixed-space self.h/self.density. Given, they override with an arbitrary-space Function (from coefficient()).

pnorm_stress_form(dx_measure, m=1e-06, rho=100.0, surface='top', thickness=None, E=None, nu=None)
residual_form(penalty=True, dss=None, dSS=None, bc_dof_mask=None, penalty_terms=None, penalty_targets=None, orientation=None, material=None, loads=None)

penalty_terms, given, is a list of (dss, dSS, bc_dof_mask) triples whose penalty contributions are summed – multi-region penalty (hm.clamp(...) + hm.symmetry(...)); with it omitted (the common case) this is exactly the single (dss, dSS, bc_dof_mask) triple. orientation is (name, Function), material is (A, B, D, As) Functions – see elastic_model().

loads, given, is a list of (kind, Function) pairs (kind in "traction"/"moment"/"pressure"): each term gets its own residual contribution on its own space, via ElasticModel.weak_residual’s load_terms (instead of the fixed self.f/self.m VF-space coefficients this method uses with loads=None, the default). An empty list is valid (e.g. a point-load-only Loads has no distributed term at all) – only None selects the fixed f/m path.

restore_geometry() → None
set_geometry(mesh_nodes) → None

Write mesh.geometry.x from user-ordered node coords (n_nodes, gdim).

von_mises_form(surface='top', thickness=None, E=None, nu=None)
As
E
VABD
VAs
VF
VT
W
X
density
f
g
gdim
h
m
mesh
nu
quadrature_degree = 4
w
x_ref