Geomechanics: elastic strip footing¶
This example adds a small geomechanics boundary-value problem to the solid mechanics example family. A linear-elastic soil block is loaded by a centered strip footing, then solved with TensorMesh’s standard direct linear-elasticity pipeline.
The example is deliberately modest: it demonstrates geotechnical boundary conditions, a footing load patch, settlement reporting, and reaction/load balance without adding a public geomechanics API.
Problem¶
The model is a two-dimensional plane-strain-style soil block with unit out-of-plane thickness. The displacement field satisfies small-strain linear elasticity,
The domain is loaded by a compression-positive footing pressure on a limited
top-surface patch. Internally, the load is applied in the negative
y direction. For geomechanics reporting, settlement is shown as positive
downward,
Boundary conditions¶
The example uses a simple roller setup:
bottom boundary: vertical displacement fixed,
u_y = 0;left and right boundaries: horizontal displacement fixed,
u_x = 0;top boundary: free except for the loaded footing patch.
The footing pressure is lumped over the top-surface nodes inside the footing patch. This keeps the example close to the existing solid-mechanics direct solve examples, while the sanity check verifies that the total reaction balances the total applied load.
Sanity checks¶
The script reports the total applied vertical load, the vertical reaction at fixed vertical degrees of freedom, the reaction/load relative error, and the maximum settlement.
The associated test checks that:
the soil settles downward under the footing;
the vertical reaction balances the applied vertical load;
doubling the footing pressure approximately doubles the settlement.
Fig. 46 Output of elastic_footing.py. The left panel shows the deformed
soil mesh colored by settlement -u_y; red arrows mark the footing
load, blue markers show vertical supports on the base, and purple markers
show horizontal roller constraints on the side boundaries. The right
panel shows the settlement profile along the ground surface. The largest
settlement occurs beneath the centered footing, and the deformation is
exaggerated for visibility.¶
Running it¶
cd examples/solid/geomechanics/elastic_footing
python elastic_footing.py
For a fast numerical-only run without writing the plot:
python elastic_footing.py --no-plot --chara-length 0.5
Core implementation¶
The full driver is in
examples/solid/geomechanics/elastic_footing/elastic_footing.py. It reuses
the same direct-solve components as the existing linear-elastic solid examples:
LinearElasticityElementAssembler, Condenser, and SparseMatrix.solve.
problem: FootingProblem,
dtype: torch.dtype = torch.float64,
device: str | torch.device = "cpu",
) -> Dict[str, Any]:
"""Assemble and solve the elastic footing boundary-value problem."""
mesh = build_mesh(problem, dtype=dtype, device=device)
assembler = LinearElasticityElementAssembler.from_mesh(
mesh,
E=problem.E,
nu=problem.nu,
)
K = assembler()
fixed = make_boundary_mask(mesh, problem)
rhs, loaded_nodes, total_vertical_load = make_load_vector(mesh, problem)
rhs_flat = rhs.flatten()
condenser = Condenser(fixed.flatten())
K_cond, rhs_cond = condenser(K, rhs_flat)
u_cond = K_cond.solve(rhs_cond)
u_flat = condenser.recover(u_cond).to(dtype=rhs_flat.dtype)
u = u_flat.reshape(mesh.n_points, mesh.dim)
# Reactions at constrained DOFs: r = K u - f.
# SparseMatrix inherits @ from torch-sla SparseTensor.
residual = K @ u_flat - rhs_flat
reactions = residual.reshape(mesh.n_points, mesh.dim)
vertical_fixed = fixed[:, 1]
vertical_reaction = reactions[vertical_fixed, 1].sum()
uy = u[:, 1]
settlement = -uy
footing_settlement = settlement[loaded_nodes].mean()
max_settlement = settlement.max()
min_uy = uy.min()
load_balance_error = torch.abs(
vertical_reaction + torch.as_tensor(
total_vertical_load,
dtype=vertical_reaction.dtype,
device=vertical_reaction.device,
)
) / max(abs(total_vertical_load), 1.0)
result = {
"mesh": mesh,
"K": K,
"rhs": rhs,
"u": u,
"fixed": fixed,
"loaded_nodes": loaded_nodes,
"total_vertical_load_N_per_m": total_vertical_load,
"vertical_reaction_N_per_m": float(vertical_reaction.detach().cpu()),
"load_balance_relative_error": float(load_balance_error.detach().cpu()),
"max_settlement_m": float(max_settlement.detach().cpu()),
"footing_settlement_m": float(footing_settlement.detach().cpu()),
"min_vertical_displacement_m": float(min_uy.detach().cpu()),
"n_nodes": int(mesh.n_points),
"n_loaded_nodes": int(loaded_nodes.numel()),
"n_fixed_dofs": int(fixed.sum().detach().cpu()),
"n_free_dofs": int(fixed.numel() - fixed.sum().detach().cpu()),
}
return result
What’s next¶
This example is elastic and intentionally small. A later follow-up could reuse
the same footing geometry with the example-local Drucker-Prager material model,
or promote a stabilized geomechanics assembler into tensormesh/assemble/ if
the API direction becomes clear.