hermit

Hermit – Reissner-Mindlin shell analysis in FEniCSx, connected to CSDL.

The public API is a ShellDomain (mesh + state space + index maps) plus free functions that build its inputs – Field builders, BoundaryConditions, Geometry, Material / Orientation, Loads – fed to solve(), whose ShellState the postprocess functions (compliance / mass / stress_field / …) consume. The FEniCSx custom operations live in hermit.fenics.ops and are called only from hermit._solve / hermit.outputs / hermit.transfer.

Note on module names: several implementation modules are underscore-prefixed (_solve / _field / _geometry / _laminate) precisely so they cannot shadow the public function of the same name. import hermit.solve would otherwise rebind hermit.solve from the function to the module – silently, with no error.

Submodules

Classes

BoundaryConditions

Located facets, dofs and measures, compiled once against a domain.

Field

A finite-element field on a ShellDomain.

Geometry

The mesh coordinate field, in any configuration solve() accepts.

Layup

A laminate layup.

Loads

A composable bundle of load terms.

Material

The constitutive input bundle consumed by the solve and the outputs.

Orientation

The laminate fibre orientation carried on a Material.

ShellDomain

Mesh, shell state space, index maps and region tags, built once.

ShellState

The solved shell state, and back-references to everything that produced it.

Functions

aggregated_stress(state, *[, rho, m, region, surface, ...])

Smooth aggregate of isotropic von Mises stress, (1/m) * pnorm**(1/rho).

as_field(→ Field)

Coerce a scalar, array or field to a Field.

center_of_gravity(state)

Mass-weighted centre of gravity, in global Cartesian coordinates.

clamp(domain, *, where[, method, penalty_beta, value])

Fix all six generalized dofs on a region.

compliance(state)

Work done by the applied loads on the solution.

composite(→ Material)

Material from pre-computed ABD stiffness fields.

constant(→ Field)

Build a uniform field by broadcasting one value to every dof.

displacement_field(state)

Mid-surface displacement as a field.

edge_moment() → Loads)

Moment per unit length on selected exterior facets.

edge_pressure() → Loads)

Pressure per unit length along the shell normal, on selected exterior facets.

edge_traction() → Loads)

Force per unit length on selected exterior facets.

elastic_energy(state)

Stored shell strain energy.

failure_field(state, *[, sampling])

Per-cell, per-ply-face Tsai-Wu failure indices.

failure_index(state, *[, rho, sampling])

KS-aggregated Tsai-Wu failure index over the whole laminate.

fiber_angle(→ Orientation)

Orientation given as an angle from each element's own in-plane axis.

fiber_direction(→ Orientation)

Orientation given as a global fibre direction.

field_fn(→ Field)

Apply a function to the coefficients of one or more fields.

from_cells(→ Field)

Build a DG0 field from per-cell values in mesh-file order.

from_coeffs(→ Field)

Build a field from raw coefficients in FE dof order.

from_function(→ Field)

Build a field by evaluating a function at the space's dof coordinates.

from_nodal(→ Field)

Build a CG1 field from per-vertex values in mesh-file order.

gauge(domain, *, at, dofs[, value])

Pin selected dofs at a single point, to remove rigid-body modes.

geometry(→ Geometry)

Build the geometry input for a solve.

interpolate(→ hermit._field.Field)

Collocate a field at another space's dof points -- the default transfer.

isotropic(→ Material)

Isotropic single-layer material.

laminate(→ Material)

Composite material from a ply stack, via classical lamination theory.

load_vector(→ Loads)

A generalised right-hand side given directly in state-space dof order.

mass(state)

Structural mass, int density * thickness dx.

moment() → Loads)

Distributed moment per unit area, in global components.

near(axis, value, *[, tol])

Build a where predicate selecting an axis-aligned plane.

nodal_displacement(state)

Displacements at the mesh vertices.

nodal_rotation(state)

Director rotations at the mesh vertices.

on_plane(point, normal, *[, tol])

Build a where predicate selecting an arbitrary plane.

pin(domain, *, where, dofs[, method, penalty_beta, value])

Fix a chosen subset of the six generalized dofs on a region.

pnorm_stress(state, *[, rho, m, region, surface, ...])

Area-normalized p-norm stress integral, (1/A) int (m*vm)**rho dx.

point_load(→ Loads)

A consistent point force and/or moment at a physical coordinate.

pressure() → Loads)

Scalar pressure acting along the shell normal.

project(→ hermit._field.Field)

L2-project a field onto another space, M c = int(phi_target . src) dx.

read_mesh(path[, name])

Read a mesh from an XDMF file.

rotation_field(state)

Director rotation as a field.

solve(→ ShellState)

Solve the linear shell problem.

strain_fields(state, *[, space, method, frame])

Membrane strain, bending curvature and transverse shear.

stress_field(state, *[, space, method, surface])

Isotropic von Mises stress field.

stress_scaling(state, *[, surface, space, method])

Suggest the stress scaling m = 1 / max|von Mises| for the aggregates.

symmetry(domain, *, where, normal[, penalty_beta, value])

Apply a symmetry-plane constraint (penalty enforcement).

thickness_only(→ Material)

Thickness and density only, for a surrogate solve.

traction() → Loads)

Distributed force per unit area, in global components.

Package Contents

class hermit.BoundaryConditions(domain, *, penalty_terms=(), strong=(), penalty_beta=None)

Located facets, dofs and measures, compiled once against a domain.

Build one with clamp(), pin(), symmetry() or gauge() rather than calling this constructor directly. a + b merges two objects on the same domain, with later terms winning on overlapping entities.

Parameters:
domainShellDomain
penalty_termssequence, optional

Compiled penalty regions, each carrying a dof mask, a prescribed target and its facet measures.

strongsequence of dolfinx.fem.DirichletBC, optional

Strong constraints.

penalty_betafloat, optional

Penalty scale for the whole object; None selects the default 1e15.

Attributes:
strong_dofsndarray

Sorted unique dof indices held by the strong constraints.

Notes

Valid only for a solve that shares this domain’s state space; see the module docstring.

to_bc_data()

Pack this object for the FEniCSx solve operation.

Returns:
hermit.fenics.bcs.BCData

Carrying a single masked penalty region when there is at most one, and a list of independently masked regions when several survive a merge.

domain
penalty_beta = None
penalty_terms = []
strong = []
property strong_dofs
class hermit.Field(domain, space, coeffs, *, kind='scalar', frame=None, global_frame=False)

A finite-element field on a ShellDomain.

The input/output primitive of the API: every material, load, geometry and output field is a Field. Build one with constant(), from_nodal(), from_cells(), from_function() or from_coeffs() rather than calling this constructor directly.

Parameters:
domainShellDomain

The domain the field lives on.

spacetuple

FE space descriptor, (family, degree) or (family, degree, value_shape), e.g. ("Lagrange", 1) or ("DG", 0, (3,)).

coeffscsdl.Variable or array_like

Coefficients in FE dof order, either flat (n_scalar_dofs * bs,) or (n_scalar_dofs, *value_shape). Stored flat, in the interleaved block layout Function(V).x.array uses.

kind{‘scalar’, ‘vector3’, ‘strain2’, ‘shear2’, ‘tensor3’}, optional

Component layout, which fixes how the components transform under a change of frame. Default 'scalar' (frame-invariant).

framendarray, optional

The frame coeffs are expressed in: (3, 3) when global_frame is True, otherwise (n_cells, 3, 3) in FE cell order. None means frame-free.

global_framebool, optional

True for a single Cartesian frame shared by every dof, False for a per-cell local frame. Default False.

Attributes:
coeffscsdl.Variable

Flat coefficients, FE dof order.

spacetuple

Normalized (family, degree, value_shape).

valuesndarray

Centroid value per cell, file cell order (see values).

See also

hermit.interpolate, hermit.project

move a field between spaces.

Notes

Two fields combine elementwise (+ - * /) only when they already share a space; a plain scalar also works, but a csdl.Variable scalar must go on the right (fld * var, not var * fld).

cell_values()

Centroid value of every cell.

Returns:
csdl.Variable

Shape (n_cells, bs), in file cell order.

eval(cells, ref_points)

Evaluate the field at reference points inside given cells.

Parameters:
cellsarray_like of int

Cell ids in file order, one per query point.

ref_pointsarray_like

Reference-cell coordinates: one point broadcast to every query, or one point per query.

Returns:
csdl.Variable

Shape (n_query, bs), or (n_query,) for a scalar field.

Raises:
ValueError

If the field is on a Quadrature space, which has no basis to tabulate.

to_frame(target) → Field

Re-express a frame-relative field in another frame.

Parameters:
targetarray_like

For a global-frame field, a full (3, 3) Cartesian frame whose rows are the new basis axes in world coordinates; every dof is rotated by the same transform.

For a per-cell field, the direction the in-plane e0 is aligned to: a (3,) global direction, a (3, 3) frame (row 0 is the direction), or a per-cell (n_cells, 3) / (n_cells, 3, 3) array in file cell order. The tangent plane (e2) is unchanged.

Returns:
Field

The same field in the new frame. A scalar or frame-free field is returned unchanged.

Raises:
ValueError

If a per-cell field is on a continuous (CG) space, where per-cell frames are ambiguous at shared nodes, or if a global-frame field is given anything but a full (3, 3) frame.

See also

to_global

re-express in world x/y/z.

to_global() → Field

Re-express the field in the world x/y/z frame.

Returns:
Field

Equivalent to to_frame(np.eye(3)). A scalar or frame-free field is returned unchanged.

property V
property block_size
coeffs
domain
global_frame = False
property is_dg
kind = 'scalar'
property n_dofs
property n_scalar_dofs
space
property value_shape
property values: numpy.ndarray

ndarray: cell_values() as plain numpy – for inspection and plotting.

class hermit.Geometry(domain, *, node_disp=None, nodes=None)

The mesh coordinate field, in any configuration solve() accepts.

Build one with geometry(). Giving neither node_disp nor nodes selects the reference configuration and no mesh-derivative forms are built, so leaving it out costs nothing.

Parameters:
domainShellDomain
node_dispcsdl.Variable or array_like, optional

(n_nodes, 3) displacement added to the reference coordinates, in file vertex order. The usual shape design variable.

nodescsdl.Variable or array_like, optional

(n_nodes, 3) absolute coordinates, in file vertex order.

Attributes:
fieldField

The coordinates as a ("Lagrange", 1, (3,)) field, for uniformity with every other input.

nodescsdl.Variable

(n_nodes, 3) file-order coordinates, the layout the FEniCSx operations consume.

is_differentiablebool

True when node_disp or nodes was supplied.

Raises:
ValueError

If both node_disp and nodes are given, or if the mesh has a higher-order (curved) coordinate element.

domain
field
class hermit.Layup

Bases: csdl_alpha.VariableGroup

A laminate layup.

Parameters:
materialsMaterial | list[Material]

The ply materials. A single Material is broadcast to every ply.

anglescsdl.Variable | np.ndarray | float

Ply orientation angles in radians. A scalar is broadcast to every ply.

heightscsdl.Variable | np.ndarray | float

Ply thicknesses. A scalar is broadcast to every ply.

num_pliesint, optional

Number of plies. Inferred from the other arguments when omitted.

angles: csdl_alpha.Variable | numpy.ndarray | float
heights: csdl_alpha.Variable | numpy.ndarray | float
materials: caddee_materials.Material | list
num_plies: int = None
class hermit.Loads(domain, *, traction_terms=(), moment_terms=(), pressure_terms=(), edge_traction_terms=(), edge_moment_terms=(), edge_pressure_terms=(), direct=None)

A composable bundle of load terms.

Build one with pressure(), traction(), moment(), edge_pressure(), edge_traction(), edge_moment(), point_load() or load_vector() rather than calling this constructor directly. a + b merges two Loads on the same domain, concatenating the term lists and summing the direct right-hand sides.

Parameters:
domainShellDomain
traction_terms, moment_termssequence of Field, optional

Distributed (3,) vector fields.

pressure_termssequence of Field, optional

Distributed scalar fields, acting along the shell normal.

edge_traction_terms, edge_moment_terms, edge_pressure_termssequence, optional

Edge terms, each pairing a field with the exterior-facet measure it acts on.

directcsdl.Variable, optional

A generalised right-hand side in domain.W dof ordering.

Examples

>>> loads = hm.pressure(domain, 2.0) + hm.point_load(domain, at=tip, force=[0, 0, -1])
combined_moment(space=('Lagrange', 1, (3,))) → hermit._field.Field | None

Sum the distributed moment terms onto one space.

Parameters:
spacetuple, optional

Target space. Exact when every term is already on it.

Returns:
Field or None

None when there are no moment terms.

combined_traction(space=('Lagrange', 1, (3,))) → hermit._field.Field | None

Sum the traction and pressure terms onto one space.

Pressure is converted to a traction with a numpy snapshot of the reference normal. solve() does not use this – it takes each term directly, with the live normal.

Parameters:
spacetuple, optional

Target space. Exact when every term is already on it.

Returns:
Field or None

None when there are no such terms.

direct_vector(ndof=None) → csdl_alpha.Variable

The combined point-load and load-vector right-hand side.

Parameters:
ndofint, optional

Length to use when there is no direct term. Defaults to the number of domain.W dofs.

Returns:
csdl.Variable

Shape (ndof,) in domain.W dof order; zeros when there is no point load or load vector.

direct = None
domain
edge_moment_terms = []
edge_pressure_terms = []
edge_traction_terms = []
moment_terms = []
pressure_terms = []
traction_terms = []
class hermit.Material(A, B, D, As, thickness, density, *, orientation=None, E=None, nu=None, layup=None)

The constitutive input bundle consumed by the solve and the outputs.

Build one with isotropic(), laminate(), composite() or thickness_only() rather than calling this constructor directly.

Parameters:
A, B, DField or None

Membrane, coupling and bending stiffness, each a (3, 3) field in the laminate’s own material axes, unrotated. None only for a thickness_only() material.

AsField or None

Transverse-shear stiffness, a (2, 2) field in the (xz, yz) ordering the shell form uses.

thickness, densityField

Scalar fields, on any space.

orientationOrientation, optional

Applied to A/B/D/As inside the shell form. None means no rotation, which is correct for an isotropic material.

E, nuField, optional

Isotropic constants, needed by the von Mises stress outputs.

layupLayup, optional

The ply stack, needed by the Tsai-Wu failure outputs.

Attributes:
domainShellDomain

Taken from thickness.

E = None
density
property domain
layup = None
nu = None
orientation = None
thickness
class hermit.Orientation(domain, *, kind, value)

The laminate fibre orientation carried on a Material.

Build one with fiber_angle() or fiber_direction(). It does not rotate anything at construction time: the rotation into each element’s frame happens inside the shell form during solve(), and again in the Tsai-Wu failure recovery.

Parameters:
domainShellDomain
kind{‘angle’, ‘direction’}

Whether value is an in-plane angle or a global direction vector.

valueField

Scalar field of angles (rad) for kind='angle', or a (3,) vector field for kind='direction'.

domain
kind
value
class hermit.ShellDomain(mesh, *, element='CG2CG1', cell_tags=None, regions=None, quadrature_degree=4)

Mesh, shell state space, index maps and region tags, built once.

The first object in every Hermit workflow. It carries no CSDL and no per-solve cost, so build it once and reuse it across every load case, sweep or optimizer iteration.

Parameters:
meshdolfinx.mesh.Mesh

A serial, straight-sided (P1 geometry) surface mesh of triangles or quadrilaterals.

element{‘CG2CG1’, ‘CG1CG1’, ‘CG2CR1’}, optional

Displacement x rotation element pair. Default 'CG2CG1'. 'CG2CR1' uses a Crouzeix-Raviart rotation that resists shear locking on thin shells, and is triangle meshes only.

cell_tagsdolfinx.mesh.MeshTags, optional

Cell tags, for per-region output integrals.

regionsdict, optional

Maps region names to tag values, so outputs can take region="skin".

quadrature_degreeint, optional

Degree for every integral this package pins by hand: the penalty-BC facet measures, the stress measures, the DG0 'average' field method and the oriented elastic energy. Default 4, which is deep in the converged regime for the shell integrand.

Attributes:
Wdolfinx.fem.FunctionSpace

The mixed state space for this domain, shared by everything downstream.

n_nodes, n_cellsint
node_coordsndarray

(n_nodes, 3) vertex coordinates in file order.

cell_centroidsndarray

(n_cells, 3) centroids in file order.

node_input_idx, cell_input_idxndarray

File-to-FE gather indices, fe_order = file_order[node_input_idx].

reverse_node_idx, reverse_cell_idxndarray

Their inverses, file_order = fe_order[reverse_node_idx].

Raises:
ValueError

If the mesh is not serial (its input_global_indices must form a node permutation), or if CG1 / DG0 dof order does not coincide with the mesh’s own vertex / cell order.

Notes

Serial meshes only. This is a requirement of every use, not only of the shape-derivative path: an MPI-partitioned mesh raises here, before any solve.

Three coefficient orderings coexist on any mesh: file (the mesh-file vertex and cell numbering, which external arrays use), local (DOLFINx’s post-read reordering) and FE dof (per function space, indexing Function.x.array). from_nodal() and from_cells() take file order and permute; from_coeffs() takes FE dof order directly.

One state space per domain. DOLFINx does not treat two independently built but structurally identical function spaces as interchangeable for boundary conditions: a strong BC located against a second instance is silently dropped, with no exception and a wrong answer. Everything downstream therefore shares domain.W.

Examples

>>> domain = hm.ShellDomain(hm.read_mesh("plate.xdmf"), element="CG2CG1")
check_cell_orientation_consistency(tol=0.0)

Verify that cell normals do not flip across shared interior facets.

Called by pressure() and edge_pressure(), whose sign convention is only well defined on a consistently wound mesh.

Parameters:
tolfloat, optional

Two adjacent normals are inconsistent when their dot product is at or below this. Default 0.

Raises:
ValueError

Naming the offending cell pair. Fix the mesh winding upstream, or use traction(), which takes an explicit global vector.

Notes

Cheap and memoized; a no-op after the first successful call.

dof_coords(space)

Coordinates of a space’s scalar dofs, in FE dof order.

Parameters:
spacetuple

(family, degree) or (family, degree, value_shape).

Returns:
ndarray

Shape (n_scalar_dofs, 3). For a blocked (vector or tensor) space these are per scalar dof, not per component: all components of one dof share a coordinate.

See also

hermit.from_coeffs, hermit.from_function

build coefficients on this space.

function_space(space)

The DOLFINx function space for a space descriptor.

Parameters:
spacetuple

(family, degree) or (family, degree, value_shape).

Returns:
dolfinx.fem.FunctionSpace

Memoized, one per normalized descriptor.

local_frames()

The orthonormal local shell frame of every cell.

Returns:
ndarray

Shape (n_cells, 3, 3), rows e0, e1 in-plane and e2 the element normal, at the reference geometry, in FE cell order. Memoized.

local_frames_at(points, cells)

The local shell frame at chosen points inside chosen cells.

Parameters:
pointsarray_like

Reference-cell coordinates: one point broadcast to every query, or one point per query.

cellsarray_like of int

Cell ids in file order.

Returns:
ndarray

Shape (n, 3, 3); see local_frames() for the row convention.

W
cell_centroids
cell_input_idx
cell_tags = None
element = 'CG2CG1'
mesh
n_cells
n_nodes
node_coords
node_input_idx
quadrature_degree = 4
regions
reverse_cell_idx
reverse_node_idx
class hermit.ShellState(domain, geometry, material, loads, bcs, *, disp_solid)

The solved shell state, and back-references to everything that produced it.

Returned by solve(). Because it carries its own inputs, every output function in hermit.outputs needs only the state. A surrogate solve should return one of these to be a drop-in replacement.

Parameters:
domainShellDomain
geometryGeometry or None

None is normalised to the reference configuration, so state.geometry is never None.

materialMaterial
loadsLoads
bcsBoundaryConditions
disp_solidcsdl.Variable

The raw mixed-space dof vector, in domain.W dof order.

bcs
disp_solid
domain
geometry
loads
material
hermit.aggregated_stress(state, *, rho=100, m=1e-06, region=None, surface='top', quadrature_degree=None)

Smooth aggregate of isotropic von Mises stress, (1/m) * pnorm**(1/rho).

A differentiable stand-in for the peak stress, suitable as an optimizer constraint.

Parameters:
stateShellState
rhofloat, optional

Aggregation exponent. Default 100.

mfloat, optional

Stress scaling; see pnorm_stress(). Default 1e-6.

regionstr or int, optional

Restrict to a mesh-tagged region.

surface{‘top’, ‘bottom’, ‘mid’}, optional

Through-thickness station. Default 'top'.

quadrature_degreeint, optional

Overrides the domain’s degree for this integral.

Returns:
csdl.Variable

Scalar, in stress units.

Warns:
UserWarning

If m is degenerate for this problem, which would otherwise return a number that barely depends on the solution.

Notes

Approaches max(vm) from below as rho grows: this is the area-averaged integral p-norm, bounded above by the pointwise maximum, unlike the discrete log-sum-exp aggregate failure_index() uses.

Examples

>>> m = hm.stress_scaling(state)
>>> peak = hm.aggregated_stress(state, m=m)
hermit.as_field(domain, value, *, space=None) → Field

Coerce a scalar, array or field to a Field.

This is how the rest of the API accepts a bare number or array wherever a Field is expected.

Parameters:
domainShellDomain
valueField, float, array_like or csdl.Variable

The value to resolve.

spacetuple, optional

Space to use for a broadcast scalar, and to test an array’s leading axis against before falling back to the per-vertex / per-cell orderings.

Returns:
Field
Raises:
ValueError

If an array’s leading axis matches neither domain.n_nodes, domain.n_cells, nor the given space’s scalar-dof count.

Notes

Resolution order:

  1. A Field is returned unchanged (space is ignored – reconcile a mismatch yourself with interpolate() or project()).

  2. A scalar, or a length-1 array / csdl.Variable, is broadcast with constant() on space (default ("Lagrange", 1)).

  3. An array with a leading axis longer than 1 is read as FE dof order when it matches space’s scalar-dof count, else as file order via from_nodal() (n_nodes) or from_cells() (n_cells).

hermit.center_of_gravity(state)

Mass-weighted centre of gravity, in global Cartesian coordinates.

Parameters:
stateShellState
Returns:
csdl.Variable

Shape (3,).

hermit.clamp(domain, *, where, method='penalty', penalty_beta=None, value=0.0)

Fix all six generalized dofs on a region.

Parameters:
domainShellDomain
wherecallable

Coordinate predicate; receives a (3, N) array and returns an (N,) boolean mask. near() and on_plane() build the common cases.

method{‘penalty’, ‘strong’}, optional

Enforcement path. Default 'penalty'.

penalty_betafloat, optional

Penalty scale, 'penalty' only. Default 1e15.

valuefloat, array_like or callable, optional

Prescribed value: a scalar, a (6,) vector over (ux, uy, uz, rx, ry, rz), or a callable returning (N, 6) for its (N, 3) coordinates. Default 0. Not differentiable – see the module docstring.

Returns:
BoundaryConditions
Raises:
ValueError

If method is not recognised, or if penalty_beta is given with method='strong'.

See also

pin

constrain a subset of the six dofs.

Examples

>>> bcs = hm.clamp(domain, where=hm.near("x", 0.0))
hermit.compliance(state)

Work done by the applied loads on the solution.

Parameters:
stateShellState
Returns:
csdl.Variable

Scalar. Conjugate to the exact Loads object that was solved, including edge and point terms.

hermit.composite(domain, *, A, B, D, As, thickness, density, orientation=None, E=None, nu=None) → Material

Material from pre-computed ABD stiffness fields.

Nothing is assembled here, so there is no constitutive_space to choose: each field keeps whatever space it was built on.

Parameters:
domainShellDomain
A, B, DField or array_like

(3, 3) membrane, coupling and bending stiffness, in laminate axes.

AsField or array_like

(2, 2) transverse-shear stiffness, (xz, yz) ordering.

thickness, densityField, float or array_like
orientationOrientation, optional

Rotates the supplied ABD into each element’s frame inside the shell form.

E, nuField, float or array_like, optional

Supply these to enable the isotropic von Mises stress outputs.

Returns:
Material

Notes

The result carries no layup, so the Tsai-Wu outputs (failure_index(), failure_field()) do not work on it. Those need laminate(), which knows the ply stack the criterion is evaluated over.

hermit.constant(domain, space, value) → Field

Build a uniform field by broadcasting one value to every dof.

Parameters:
domainShellDomain
spacetuple

(family, degree) or (family, degree, value_shape).

valuefloat, array_like or csdl.Variable

A scalar, or a value of the space’s value_shape. Stays differentiable when it is a csdl.Variable.

Returns:
Field
hermit.displacement_field(state)

Mid-surface displacement as a field.

Parameters:
stateShellState
Returns:
Field

(3,) vector field on the displacement subspace of the element, in global Cartesian components.

See also

nodal_displacement

the same quantity at mesh vertices, in file order.

hermit.edge_moment(domain, m, *, where, space=('Lagrange', 1)) → Loads

Moment per unit length on selected exterior facets.

Parameters:
domainShellDomain
mField or array_like

A (3,) global vector, or a Field of (3,) vectors.

wherecallable

Coordinate predicate selecting the loaded edge; see edge_traction().

spacetuple, optional

Space for a non-Field value. Default ("Lagrange", 1).

Returns:
Loads
Raises:
ValueError

If m does not resolve to a (3,) vector field.

hermit.edge_pressure(domain, p, *, where, space=('DG', 0)) → Loads

Pressure per unit length along the shell normal, on selected exterior facets.

Parameters:
domainShellDomain
pField, float or array_like

Scalar pressure; positive acts along +n.

wherecallable

Coordinate predicate selecting the loaded edge; see edge_traction().

spacetuple, optional

Space for a non-Field value. Default ("DG", 0).

Returns:
Loads
Raises:
ValueError

If space is not scalar, or if the domain’s cell normals are not consistently oriented.

hermit.edge_traction(domain, t, *, where, space=('Lagrange', 1)) → Loads

Force per unit length on selected exterior facets.

Parameters:
domainShellDomain
tField or array_like

A (3,) global vector, or a Field of (3,) vectors.

wherecallable

Coordinate predicate selecting the loaded edge, with the same contract as clamp(): it receives a (3, N) coordinate array and returns an (N,) boolean mask. Only exterior facets are selected.

spacetuple, optional

Space for a non-Field value. Default ("Lagrange", 1).

Returns:
Loads
Raises:
ValueError

If t does not resolve to a (3,) vector field.

Examples

>>> tip = hm.edge_traction(domain, [0.0, 0.0, -1.0], where=hm.near("x", 10.0))
hermit.elastic_energy(state)

Stored shell strain energy.

Parameters:
stateShellState
Returns:
csdl.Variable

Scalar. For a linear solve this is half the compliance, up to the round-off of the direct solve.

Raises:
ValueError

If the material carries no A/B/D/As (a thickness_only() material).

hermit.failure_field(state, *, sampling='average')

Per-cell, per-ply-face Tsai-Wu failure indices.

Parameters:
stateShellState
sampling{‘average’, ‘midpoint’}, optional

How the DG0 strain measures are sampled per cell. Default 'average'.

Returns:
Field

DG0 field with one component per ply face.

Raises:
ValueError

If the material carries no layup. Only laminate() sets one.

hermit.failure_index(state, *, rho=100, sampling='average')

KS-aggregated Tsai-Wu failure index over the whole laminate.

Parameters:
stateShellState
rhofloat, optional

KS aggregation exponent. Default 100.

sampling{‘average’, ‘midpoint’}, optional

How the DG0 strain measures are sampled per cell. Default 'average'.

Returns:
csdl.Variable

Scalar. Below 1 means no ply face has failed; the log-sum-exp KS aggregate bounds the true maximum from above.

Raises:
ValueError

If the material carries no layup. Only laminate() sets one.

hermit.fiber_angle(domain, angle) → Orientation

Orientation given as an angle from each element’s own in-plane axis.

Parameters:
domainShellDomain
angleField, float or array_like

Angle in radians from the element’s e0 to the fibre direction, on any space. A csdl.Variable makes it a differentiable design field.

Returns:
Orientation
Raises:
ValueError

If angle is not a scalar field.

See also

fiber_direction

orientation given as a global direction.

Notes

The angle is measured from e0, which follows the mesh parametrisation. On a mesh where e0 is not uniform, a continuous (CG) angle field makes the physical fibre direction kink slightly at element interfaces – fine on a flat or structured mesh, otherwise prefer fiber_direction().

hermit.fiber_direction(domain, direction) → Orientation

Orientation given as a global fibre direction.

Parameters:
domainShellDomain
directionField or array_like

A (3,) global vector, or a Field of (3,) vectors on any space (e.g. a per-cell curvilinear fibre path).

Returns:
Orientation
Raises:
ValueError

If direction is not a (3,) vector field.

See also

fiber_angle

orientation given as an element-relative angle.

Notes

The shell form projects the direction into each element’s tangent plane and never forms the angle explicitly, so there is no branch cut. The projection is degenerate only where direction is parallel to the shell normal, which is detected when concrete values reach the form.

hermit.field_fn(*args, fn=None) → Field

Apply a function to the coefficients of one or more fields.

Parameters:
*argsField

Operands, all on the same domain and the same space. fn may be passed as the last positional argument instead of as a keyword.

fncallable

Applied to the operands’ flat coeffs, one argument per field.

Returns:
Field

On the shared space. kind / frame / global_frame carry through only when every operand agrees, otherwise the result is scalar and frame-free.

Raises:
ValueError

If no operand is given, or if the operands are on different domains or different spaces. Use interpolate() or project() to reconcile spaces first; there is no implicit coercion.

TypeError

If an operand is not a Field.

Examples

>>> total = field_fn(a, b, fn=lambda x, y: x + y)
hermit.from_cells(domain, values) → Field

Build a DG0 field from per-cell values in mesh-file order.

Parameters:
domainShellDomain
valuescsdl.Variable or array_like

Shape (n_cells,) or (n_cells, k), in file cell order.

Returns:
Field

On ("DG", 0) or ("DG", 0, (k,)). The file-to-FE permutation is applied as a CSDL gather, so a csdl.Variable input stays differentiable.

Raises:
ValueError

If the leading axis is not domain.n_cells.

See also

from_nodal

the per-vertex (CG1) counterpart.

hermit.from_coeffs(domain, space, coeffs, *, kind='scalar', frame=None, global_frame=False) → Field

Build a field from raw coefficients in FE dof order.

The explicit way to say “these are FE dof coefficients”; pair it with dof_coords() to know where each dof lives.

Parameters:
domainShellDomain
spacetuple

(family, degree) or (family, degree, value_shape).

coeffscsdl.Variable or array_like

FE dof order, flat or (n_scalar_dofs, *value_shape).

kind, frame, global_frame

Orientation bookkeeping; see Field.

Returns:
Field
hermit.from_function(domain, space, fn) → Field

Build a field by evaluating a function at the space’s dof coordinates.

Parameters:
domainShellDomain
spacetuple

(family, degree) or (family, degree, value_shape).

fncallable

Called once with the (n_scalar_dofs, 3) dof coordinates and returning the coefficients. Returning numpy gives a constant field; returning a csdl.Variable (e.g. closing over a design variable) is passed through undisturbed and stays differentiable.

Returns:
Field

Notes

The sample points are the reference dof coordinates, so the result is not differentiable with respect to the geometry.

Examples

>>> taper = from_function(domain, ("Lagrange", 1), lambda x: 0.2 - 0.01 * x[:, 0])
hermit.from_nodal(domain, values) → Field

Build a CG1 field from per-vertex values in mesh-file order.

Parameters:
domainShellDomain
valuescsdl.Variable or array_like

Shape (n_nodes,) or (n_nodes, k), in file vertex order (the ordering an external geometry or CADDEE-side array uses).

Returns:
Field

On ("Lagrange", 1) or ("Lagrange", 1, (k,)). The file-to-FE permutation is applied as a CSDL gather, so a csdl.Variable input stays differentiable.

Raises:
ValueError

If the leading axis is not domain.n_nodes.

See also

from_cells

the per-cell (DG0) counterpart.

from_coeffs

when the array is already in FE dof order.

hermit.gauge(domain, *, at, dofs, value=0.0)

Pin selected dofs at a single point, to remove rigid-body modes.

Always enforced strongly. Use it to fix the residual null space of an otherwise softly supported model.

Parameters:
domainShellDomain
atarray_like

A (3,) point; the nearest dof of each selected component is used.

dofssequence of str

Any subset of ('ux', 'uy', 'uz', 'rx', 'ry', 'rz').

valuefloat, array_like or callable, optional

Prescribed value; see clamp(). Default 0.

Returns:
BoundaryConditions
Raises:
ValueError

If a dof name is not one of the six.

Examples

>>> bcs = supports + hm.gauge(domain, at=[0.0, 0.0, 0.0], dofs=("ux", "uy"))
hermit.geometry(domain, *, node_disp=None, nodes=None) → Geometry

Build the geometry input for a solve.

Parameters:
domainShellDomain
node_dispcsdl.Variable or array_like, optional

(n_nodes, 3) displacement from the reference coordinates, file order.

nodescsdl.Variable or array_like, optional

(n_nodes, 3) absolute coordinates, file order.

Returns:
Geometry

The reference configuration when neither argument is given.

Examples

>>> nd = csdl.Variable(value=np.zeros((domain.n_nodes, 3)), name="node_disp")
>>> state = hm.solve(domain, mat, loads, bcs,
...                  geometry=hm.geometry(domain, node_disp=nd))
hermit.interpolate(src_field, space) → hermit._field.Field

Collocate a field at another space’s dof points – the default transfer.

Parameters:
src_fieldField
spacetuple

Target space; its value shape must match the source’s.

Returns:
Field

On the target space, or src_field itself if already there.

Raises:
ValueError

If the value shapes differ, or if either space is not point-evaluation (Lagrange, DG, DQ or Quadrature). Piola-mapped families such as RT and Nedelec drag in the geometric Jacobian and are rejected, as does Crouzeix-Raviart. A Quadrature source has no basis to tabulate – use project().

See also

project

the geometry-dependent L2 alternative.

Notes

A constant sparse matrix, memoized per source/target pair, so this is a plain csdl.sparse.matvec: fully differentiable in the coefficients and independent of the geometry, because the target dofs’ reference coordinates never move.

hermit.isotropic(domain, *, E, nu, thickness, density, constitutive_space=None, orientation=None) → Material

Isotropic single-layer material.

A/B/D/As are assembled from the closed form in plain CSDL, with a 0.833 shear-correction factor.

Parameters:
domainShellDomain
E, nu, thickness, densityField, float or array_like

Young’s modulus, Poisson’s ratio, shell thickness and mass density. Each may be a scalar (broadcast), a per-vertex / per-cell array, or a Field on its own space. A csdl.Variable makes it a design variable.

constitutive_spacetuple, optional

Space the ABD fields are evaluated on. Defaults to ("DG", d) where d is the highest degree among E, nu and thickness.

orientationOrientation, optional

Accepted and ignored – an isotropic in-plane stiffness has no preferred axis, so rotating it is a no-op. The parameter exists so that one orientation object can be passed uniformly to any material constructor.

Returns:
Material

Notes

The ABD fields are an interpolant of the closed form at the constitutive_space dofs, not an exact representation of it: E*t and t**3 have higher polynomial degree than any fixed space carries. Raise constitutive_space if that matters for a strongly graded thickness.

Examples

>>> mat = hm.isotropic(domain, E=4.32e8, nu=0.0, thickness=0.2, density=1.0)
hermit.laminate(domain, *, layup, density, orientation=None, shear_correction=0.833, constitutive_space=None) → Material

Composite material from a ply stack, via classical lamination theory.

Parameters:
domainShellDomain
layupLayup

The ply stack (materials, angles, heights). Its angles and heights may be csdl.Variables, making the ABD differentiable in the layup.

densityField, float or array_like

Mass density.

orientationOrientation, optional

Rotates the laminate axes into each element’s frame inside the shell form. Without it the laminate 0-degree axis is taken to be the element e0.

shear_correctionfloat, optional

Multiplies the CLT transverse-shear stiffness. Default 0.833, the same factor isotropic() applies.

constitutive_spacetuple, optional

Space the ABD fields are broadcast onto. Defaults to ("DG", d) where d is the highest degree among density and orientation.

Returns:
Material

Carrying layup, so failure_index() and failure_field() work on the resulting state.

Notes

One layup is broadcast uniformly to every cell; spatial variation of the stack itself is not part of this API. Use composite() with per-point ABD fields for that.

Examples

>>> layup = hm.Layup(ud, np.radians([0.0, 90.0, 0.0]), np.full(3, 0.002))
>>> mat = hm.laminate(domain, layup=layup, density=1.6e3,
...                   orientation=hm.fiber_direction(domain, [1.0, 0.0, 0.0]))
hermit.load_vector(domain, vec) → Loads

A generalised right-hand side given directly in state-space dof order.

Parameters:
domainShellDomain
veccsdl.Variable or array_like

One entry per domain.W dof, in that space’s dof ordering.

Returns:
Loads
Raises:
ValueError

If vec does not have exactly domain.W’s dof count.

hermit.mass(state)

Structural mass, int density * thickness dx.

Parameters:
stateShellState
Returns:
csdl.Variable

Scalar.

hermit.moment(domain, m, *, space=('Lagrange', 1)) → Loads

Distributed moment per unit area, in global components.

Contributes int m . dtheta dx to the residual.

Parameters:
domainShellDomain
mField or array_like

A (3,) global vector, or a Field of (3,) vectors.

spacetuple, optional

Space for a non-Field value. Default ("Lagrange", 1).

Returns:
Loads
Raises:
ValueError

If m does not resolve to a (3,) vector field.

hermit.near(axis, value, *, tol=1e-12)

Build a where predicate selecting an axis-aligned plane.

Parameters:
axis{0, 1, 2, ‘x’, ‘y’, ‘z’}

Coordinate axis to test.

valuefloat

Coordinate value the plane sits at.

tolfloat, optional

Absolute tolerance. Default 1e-12.

Returns:
callable

Suitable as where= for any BC or edge-load builder.

Raises:
ValueError

If axis is not one of the accepted names.

Notes

The comparison is purely absolute. A relative tolerance would swamp tol far from the origin – near("x", 10.0) would select everything within 1e-4 of the plane.

Examples

>>> bcs = hm.clamp(domain, where=hm.near("x", 0.0))
hermit.nodal_displacement(state)

Displacements at the mesh vertices.

Parameters:
stateShellState
Returns:
csdl.Variable

Shape (n_nodes, 3), in file vertex order – the ordering an external geometry pipeline uses.

hermit.nodal_rotation(state)

Director rotations at the mesh vertices.

Parameters:
stateShellState
Returns:
csdl.Variable

Shape (n_nodes, 3), in file vertex order.

hermit.on_plane(point, normal, *, tol=1e-12)

Build a where predicate selecting an arbitrary plane.

Parameters:
pointarray_like

A (3,) point on the plane.

normalarray_like

A (3,) plane normal; need not be unit or axis-aligned.

tolfloat, optional

Absolute distance tolerance. Default 1e-12.

Returns:
callable

Suitable as where= for any BC or edge-load builder.

See also

near

the axis-aligned shorthand.

hermit.pin(domain, *, where, dofs, method='penalty', penalty_beta=None, value=0.0)

Fix a chosen subset of the six generalized dofs on a region.

Parameters:
domainShellDomain
wherecallable

Coordinate predicate; see clamp().

dofssequence of str

Any subset of ('ux', 'uy', 'uz', 'rx', 'ry', 'rz').

method{‘penalty’, ‘strong’}, optional

Enforcement path. Default 'penalty'.

penalty_betafloat, optional

Penalty scale, 'penalty' only.

valuefloat, array_like or callable, optional

Prescribed value; see clamp(). Default 0.

Returns:
BoundaryConditions
Raises:
ValueError

If a dof name is unknown, if method is not recognised, or if penalty_beta is given with method='strong'.

Examples

>>> diaphragm = hm.pin(domain, where=hm.near("x", 0.0), dofs=("uy", "uz"))
hermit.pnorm_stress(state, *, rho=100, m=1e-06, region=None, surface='top', quadrature_degree=None)

Area-normalized p-norm stress integral, (1/A) int (m*vm)**rho dx.

Parameters:
stateShellState
rhofloat, optional

Aggregation exponent. Default 100.

mfloat, optional

Stress scaling; see Notes. Default 1e-6.

regionstr or int, optional

Restrict the integral to a mesh-tagged region. Needs a domain built with cell_tags= and regions=.

surface{‘top’, ‘bottom’, ‘mid’}, optional

Through-thickness station the stress is recovered at. Default 'top'.

quadrature_degreeint, optional

Overrides the domain’s degree for this integral.

Returns:
csdl.Variable

Scalar.

Raises:
ValueError

If the material carries no E and nu (stress recovery is isotropic-only – use failure_index() for laminates), or if region= is given without mesh tags.

KeyError

If region is not among the mesh tags.

See also

aggregated_stress

the same quantity reduced back to stress units.

Notes

m is a problem-specific scaling, not a smoothing knob. It enters as (m*vm)**rho, so it must put m*max(vm) near 1 or the integrand runs off the end of double precision: with the default m and a peak von Mises of 1.4e4, (m*vm)**100 is 1e-186. Use stress_scaling() to compute one.

m must also be constant with respect to the design variables. It is not differentiated and cancels out of the aggregate exactly; recomputing it inside the recorded graph each optimizer iteration silently makes the reported derivative wrong.

hermit.point_load(domain, *, at, force=None, moment=None) → Loads

A consistent point force and/or moment at a physical coordinate.

The cell containing at is located, the state-space basis is evaluated there, and the result is scattered into the direct right-hand side – the weak form of a Dirac delta, int F . delta(x - at) . du dx = F . du(at). The point need not be a mesh vertex.

Parameters:
domainShellDomain
atarray_like

Physical coordinate, shape (3,).

forcecsdl.Variable or array_like, optional

(3,) point force.

momentcsdl.Variable or array_like, optional

(3,) point moment.

Returns:
Loads
Raises:
ValueError

If neither force nor moment is given, or if at lies in no cell.

Notes

Differentiable in force and moment, but not in at or the mesh coordinates: the containing cell is fixed at construction.

hermit.pressure(domain, p, *, space=('DG', 0)) → Loads

Scalar pressure acting along the shell normal.

Positive p acts along +n. The residual uses the live ufl.CellNormal, so the load is differentiable with respect to the mesh coordinates.

Parameters:
domainShellDomain
pField, float or array_like

Scalar pressure.

spacetuple, optional

Space for a non-Field value. Default ("DG", 0).

Returns:
Loads
Raises:
ValueError

If space is not scalar, or if the domain’s cell normals are not consistently oriented. On a badly-wound imported mesh a uniform pressure would otherwise become a sign-alternating load with no other symptom.

See also

traction

an explicit global vector, independent of the normal.

hermit.project(src_field, space, *, geometry=None) → hermit._field.Field

L2-project a field onto another space, M c = int(phi_target . src) dx.

Parameters:
src_fieldField
spacetuple

Target space; its value shape must match the source’s.

geometryGeometry, ndarray or csdl.Variable, optional

Defaults to the domain’s reference coordinates, which lets the mass-matrix factorization be cached across calls. Passing a live geometry enables the shape-derivative branch.

Returns:
Field

On the target space. Projecting onto the same space is the identity up to solver round-off.

Raises:
ValueError

If the value shapes differ, if geometry belongs to another domain, or if the source and target Quadrature degrees disagree.

Notes

Both sides carry dx, so unlike interpolate() this depends on the geometry. It is a custom operation with a mass-matrix quotient-rule VJP, reverse mode only: one cotangent solve per call, never a dense Jacobian.

hermit.read_mesh(path, name='Grid')

Read a mesh from an XDMF file.

Parameters:
pathstr or pathlib.Path
namestr, optional

Grid name inside the file. Default "Grid".

Returns:
dolfinx.mesh.Mesh

Read on MPI.COMM_WORLD. Run serially – ShellDomain requires a serial mesh.

Examples

>>> domain = hm.ShellDomain(hm.read_mesh("plate.xdmf"))
hermit.rotation_field(state)

Director rotation as a field.

Parameters:
stateShellState
Returns:
Field

(3,) vector field on the rotation subspace of the element, in global Cartesian components.

hermit.solve(domain, material, loads, bcs, *, geometry=None) → ShellState

Solve the linear shell problem.

Parameters:
domainShellDomain
materialMaterial

Must carry A/B/D/As; a thickness_only() material is surrogate-only.

loadsLoads
bcsBoundaryConditions
geometryGeometry, optional

Defaults to the reference configuration. Pass hm.geometry(domain, node_disp=...) to make the mesh coordinates a differentiable input.

Returns:
ShellState
Raises:
ValueError

If material, loads or bcs was built against a different ShellDomain, or if the material has no ABD stiffness.

Notes

The formulation is linear and the solve is a direct MUMPS factorization, wrapped as a CSDL implicit custom operation. Because the shell tangent is symmetric, one factorization serves both the forward solve and the adjoint.

Every input must be built against this exact domain. DOLFINx silently drops a boundary condition located against a structurally identical but distinct function space, so this is checked rather than assumed.

solve is swappable: any Callable[(domain, material, loads, bcs), ShellState] is a drop-in surrogate, and the output functions consume its result unchanged.

Examples

>>> state = hm.solve(domain, material, hm.pressure(domain, 2.0),
...                  hm.clamp(domain, where=hm.near("x", 0.0)))
hermit.strain_fields(state, *, space=('DG', 2), method='project', frame=None)

Membrane strain, bending curvature and transverse shear.

Parameters:
stateShellState
spacetuple, optional

Target space for all three fields. Default ("DG", 2).

method{‘project’, ‘interpolate’, ‘average’}, optional

Recovery method. 'average' is the DG0 cell average. Default 'project'.

frame{‘local’, ‘global’}, optional

Component frame. Defaults to the element-local in-plane frame on a DG space, and to global Cartesian on a CG space, where per-cell frames would be ambiguous at shared nodes.

Returns:
tuple of Field

(mid_strain, curvature, shear_strain). The component layout follows the frame: in the element-local frame the first two are in-plane engineering Voigt [xx, yy, 2xy] and the third is [xz, yz]; in the global frame they become full symmetric tensors, [xx, yy, zz, 2yz, 2xz, 2xy] and a 3-vector.

Raises:
ValueError

If method or frame is not one of the accepted values, or if a local frame is requested on a continuous (CG) space.

See also

hermit.Field.to_frame, hermit.Field.to_global

re-express the components.

Examples

>>> eps, kappa, gamma = hm.strain_fields(state)
>>> kappa_global = kappa.to_global().values
hermit.stress_field(state, *, space=('DG', 2), method='project', surface='top')

Isotropic von Mises stress field.

Parameters:
stateShellState
spacetuple, optional

Target space. Default ("DG", 2).

method{‘project’, ‘interpolate’, ‘average’}, optional

Recovery method. Default 'project'.

surface{‘top’, ‘bottom’, ‘mid’}, optional

Through-thickness station. Default 'top'.

Returns:
Field

Scalar field.

Raises:
ValueError

If the material carries no E and nu; von Mises recovery is isotropic-only. composite() accepts E= / nu= for this.

hermit.stress_scaling(state, *, surface='top', space=('DG', 2), method='project')

Suggest the stress scaling m = 1 / max|von Mises| for the aggregates.

Parameters:
stateShellState
surface{‘top’, ‘bottom’, ‘mid’}, optional

Through-thickness station. Default 'top'.

spacetuple, optional

Space the stress field is recovered on. Default ("DG", 2).

method{‘project’, ‘interpolate’, ‘average’}, optional

Field recovery method. Default 'project'.

Returns:
float

A plain Python float, deliberately: it is a snapshot taken outside the differentiated path, so it cannot contaminate the derivative of aggregated_stress().

Raises:
ValueError

If the stress field has no value (run under an inline csdl.Recorder), or if the peak is not finite and positive.

Notes

Costs one extra stress-field assembly. Call it once when setting a problem up, not inside an optimizer loop – a design-dependent m makes the reported gradient wrong even though the value looks better scaled.

hermit.symmetry(domain, *, where, normal, penalty_beta=None, value=0.0)

Apply a symmetry-plane constraint (penalty enforcement).

Constrains the displacement along normal and the two rotations about the in-plane axes, leaving the rotation about normal free.

Parameters:
domainShellDomain
wherecallable

Coordinate predicate selecting the plane; see clamp().

normalarray_like

A (3,) plane normal, which must be (close to) axis-aligned.

penalty_betafloat, optional

Penalty scale. Default 1e15.

valuefloat, array_like or callable, optional

Prescribed value; see clamp(). Default 0.

Returns:
BoundaryConditions
Raises:
ValueError

If normal is zero or not axis-aligned. The penalty form carries a 6-component mask in global x/y/z, with no rotated-frame projection.

Examples

>>> half = hm.symmetry(domain, where=hm.near("y", 0.0), normal=[0, 1, 0])
hermit.thickness_only(domain, *, thickness, density) → Material

Thickness and density only, for a surrogate solve.

A through As stay None, so solve() rejects this material: it is for a surrogate that carries its own constitutive model but still needs mass() and center_of_gravity().

Parameters:
domainShellDomain
thickness, densityField, float or array_like
Returns:
Material
hermit.traction(domain, t, *, space=('Lagrange', 1)) → Loads

Distributed force per unit area, in global components.

Contributes int t . du dx to the residual. Unlike pressure() this never touches the shell normal, so it is the right choice for a body force such as self weight on a curved surface.

Parameters:
domainShellDomain
tField or array_like

A (3,) global vector, or a Field of (3,) vectors.

spacetuple, optional

Space for a non-Field value. Default ("Lagrange", 1).

Returns:
Loads
Raises:
ValueError

If t does not resolve to a (3,) vector field.

See also

pressure

a scalar load following the shell normal.

edge_traction

the same load on selected exterior facets.