hermit.csdl_helpers

Native-CSDL preprocessing / postprocessing helpers.

Everything here is ordinary CSDL (auto-differentiated) – gathers, reshapes, the isotropic ABD assembly, the force->pressure L2 solve, stress aggregation, nodal extraction. No FEniCSx, no custom operations.

Functions

aggregate(pnorm_stress, m, rho)

Aggregated stress (1/m) * (pnorm + guard)**(1/rho).

assemble_abd_isotropic(E, nu, thickness)

Isotropic single-layer A, B, D (n,3,3) and As (n,2,2) from E, nu, thickness (n,).

broadcast0(→ csdl_alpha.Variable)

Broadcast var along axis 0 to length n.

build_moment(loads, maps, vf_size)

build_pressure(loads, maps, vf_size)

Combine nodal pressure + (force -> pressure) into the VF-dof RHS vector.

extract_nodal(disp_vec, sparse_map, geom_shape, ...)

(state dof vec) -> (n_nodes, 3) nodal field, reordered back to user node ordering.

orientation_angle(→ numpy.ndarray)

Per-cell angle (rad) from each cell's local e0 to the tangent-plane projection of

reorder(→ csdl_alpha.Variable)

Gather var along axis 0 by integer index array idx (user -> FE ordering).

resolve_mesh_nodes(reference_nodes[, node_disp, ...])

Return (mesh_nodes_variable, geometry_is_differentiable).

rotate_abd(A, B, D, As, theta)

Rotate the ABD stiffness (n,3,3) and transverse shear As (n,2,2) by a

Module Contents

hermit.csdl_helpers.aggregate(pnorm_stress, m, rho)

Aggregated stress (1/m) * (pnorm + guard)**(1/rho).

m is a problem-specific scaling, not a smoothing knob: the aggregate is mathematically invariant under m (it cancels), and only the finite exponent range of float64 breaks that invariance – which it does violently once m*max(vm) strays far from 1. _warn_if_degenerate says so out loud.

hermit.csdl_helpers.assemble_abd_isotropic(E, nu, thickness)

Isotropic single-layer A, B, D (n,3,3) and As (n,2,2) from E, nu, thickness (n,).

Port of MaterialInputFactory.from_isotropic (femo dev_coupling).

hermit.csdl_helpers.broadcast0(var: csdl_alpha.Variable, n: int) → csdl_alpha.Variable

Broadcast var along axis 0 to length n.

A scalar (() / (1,)) or a leading axis of length 1 is expanded to n (differentiably); an axis already of length n is returned unchanged. Used to let material inputs be given as a single value instead of a per-node / per-cell array.

hermit.csdl_helpers.build_moment(loads, maps, vf_size: int)
hermit.csdl_helpers.build_pressure(loads, maps, vf_size: int)

Combine nodal pressure + (force -> pressure) into the VF-dof RHS vector.

hermit.csdl_helpers.extract_nodal(disp_vec, sparse_map, geom_shape, reverse_node_idx)

(state dof vec) -> (n_nodes, 3) nodal field, reordered back to user node ordering.

hermit.csdl_helpers.orientation_angle(direction, frames) → numpy.ndarray

Per-cell angle (rad) from each cell’s local e0 to the tangent-plane projection of direction (a global (3,) vector or a per-cell (n, 3) field). frames: (n, 3, 3) local frames, rows e0, e1, e2. Pure numpy – fibre layout is fixed.

hermit.csdl_helpers.reorder(var: csdl_alpha.Variable, idx) → csdl_alpha.Variable

Gather var along axis 0 by integer index array idx (user -> FE ordering).

hermit.csdl_helpers.resolve_mesh_nodes(reference_nodes, node_disp=None, mesh_nodes=None)

Return (mesh_nodes_variable, geometry_is_differentiable).

hermit.csdl_helpers.rotate_abd(A, B, D, As, theta)

Rotate the ABD stiffness (n,3,3) and transverse shear As (n,2,2) by a per-cell in-plane angle theta (n,). theta may be numpy (fixed fibre layout) or a csdl.Variable (differentiable, e.g. a fibre-angle design field). X_rot = Tsig_inv(theta) @ X @ Teps(theta) – LamAD’s CLT transform.

As is in the shell’s standard (xz, yz) transverse-shear ordering (the CLT seam in hm.laminate permutes it there), so its rotation uses r2t @ As @ r2 == R(theta)^T As R(theta) – the Teps-consistent convention (r2 == R(theta), r2t == R(theta)^T), matching hermit.fenics.kinematics.shear_rotation / congruent_transform. (r2 @ As @ r2t would instead be correct for a swapped (yz, xz) ordering.)