From 65066b84511492323be900ee9637da0e8b1123bb Mon Sep 17 00:00:00 2001 From: Christian Svensson Date: Sun, 18 Oct 2026 12:11:04 +0200 Subject: [PATCH] tests: add the NAFEMS LE5 Z-section oracle case The Z-section cantilever under an end torque as five hex20 blocks (two flanges, two corner blocks, the web; two elements through every wall): axial stress at A, on the mid-surface of the lower flange at its free edge, -109.45 against -108 (2%). The solid model converges to about -109.7, 1.6% beyond the thin-walled reference, as ESRD's converged p-version solid (-109.5) does. The README also records the MITC4 mid-surface shell model of the same problem, which converges to about -111.3 (3.1%), and why the case is the solid. Part of #97. --- docs/oracle.md | 4 +- tests/oracle/nafems-le5/README.md | 100 ++++++++++++++++++++++++++ tests/oracle/nafems-le5/expected.json | 15 ++++ tests/oracle/nafems-le5/fixture.json | 9 +++ tests/oracle/nafems-le5/recipe.json | 18 +++++ 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 tests/oracle/nafems-le5/README.md create mode 100644 tests/oracle/nafems-le5/expected.json create mode 100644 tests/oracle/nafems-le5/fixture.json create mode 100644 tests/oracle/nafems-le5/recipe.json diff --git a/docs/oracle.md b/docs/oracle.md index f4bf3c0..ee66cf5 100644 --- a/docs/oracle.md +++ b/docs/oracle.md @@ -2,7 +2,7 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the policy is [verification.md](verification.md). Every case is our own model, built with FieldCraft commands from the problem's published description, and compared with the reference value its `expected.json` cites, within the stated tolerance and for the stated reason. Accepted results are frozen in each case's `fixture.json`: a later change that moves one beyond its band (10⁻⁹ of its size) fails CI even while the reference still passes. No reference value was produced with a commercial product. -**280 of 286 quantities pass** (6 known failure(s)) in 75 active cases: 62 theory, 13 published benchmark, 0 experiment; 8 pending cases wait for capabilities still being built. +**281 of 287 quantities pass** (6 known failure(s)) in 76 active cases: 62 theory, 14 published benchmark, 0 experiment; 8 pending cases wait for capabilities still being built. ## Theory: closed-form solutions, patch tests and mesh invariants @@ -296,6 +296,7 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the | [nafems-le2](../tests/oracle/nafems-le2/) | tangential stress at E, outer surface, load case 1 (edge moment) | 60 | 59.936 | -0.107% | 1% | pass | matches | | [nafems-le2](../tests/oracle/nafems-le2/) | tangential stress at E, outer surface, load case 2 (pressure and edge traction) | 60 | 59.904 | -0.161% | 1% | pass | matches | | [nafems-le3](../tests/oracle/nafems-le3/) | radial displacement at A (10, 0, 0), m | 0.185 | 0.18418 | -0.446% | 2% | pass | matches | +| [nafems-le5](../tests/oracle/nafems-le5/) | σ_xx at A (2.5, −1, −1), mid-surface of the lower flange at its free edge | -108 | -109.45 | -1.35% | 2% | pass | matches | | [pinched-cylinder](../tests/oracle/pinched-cylinder/) | radial displacement under the load, mid-surface | -1.8248e-5 | -1.8038e-5 | 1.15% | 2% | pass | matches | | [scordelis-lo-roof-shell](../tests/oracle/scordelis-lo-roof-shell/) | vertical displacement at the midpoint of the free edge, mid-surface | -0.3024 | -0.29901 | 1.12% | 2% | pass | matches | | [scordelis-lo-roof-solid](../tests/oracle/scordelis-lo-roof-solid/) | vertical displacement at the midpoint of the free edge, mid-surface | -0.3024 | -0.3014 | 0.33% | 2% | pass | matches | @@ -340,6 +341,7 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the - **[nafems-le10](../tests/oracle/nafems-le10/)** (benchmark): NAFEMS LE10 thick plate under pressure (hex20). Source: NAFEMS, The Standard NAFEMS Benchmarks, TNSB Rev. 3, October 1990, test LE10 (target σ_yy = −5.38 MPa at D); as reproduced in the Abaqus 2017 Benchmarks Guide, 'LE10: Thick plate under pressure'. Tolerances — syy_D: the target is published to three figures; at D the stress is extrapolated to a corner where the hole edge meets the loaded face, and published converged solutions scatter by 1–2% (Abaqus Benchmarks Guide fine meshes: −5.51 to −5.89) - **[nafems-le2](../tests/oracle/nafems-le2/)** (benchmark): NAFEMS LE2 cylindrical shell bending patch test (MITC4 shells). Source: NAFEMS, The Standard NAFEMS Benchmarks, TNSB Rev. 3, October 1990, test LE2 (target: tangential stress 60 MPa on the outer surface at E, both load cases); the problem and target as reproduced in the Abaqus 2016 Benchmarks Guide, section 4.2.2 'LE2: Cylindrical shell bending patch test', and in the ESRD StressCheck Benchmarks Guide 'The Standard NAFEMS Benchmarks: Linear Elastic Tests' (2018), p. 8. Tolerances — stt_E_moment: the target is the exact thin-shell value (6M/t²) and the stress is uniform over the patch; the 8 × 8 flat facets tilt the element axes by ±1.875° from the hoop direction, which moves 0.1% of it into σ_zz, and 16 × 16 is within 0.03% (see README); stt_E_pressure: the target is the exact membrane value (pR/t) and the stress is uniform over the patch; as in load case 1 the facets' tilt and the pressure on flat facets keep 8 × 8 within 0.2%, 16 × 16 within 0.05% (see README) - **[nafems-le3](../tests/oracle/nafems-le3/)** (benchmark): NAFEMS LE3 hemispherical shell with point loads (MITC4 shells). Source: NAFEMS, The Standard NAFEMS Benchmarks, TNSB Rev. 3, October 1990, test LE3 (target: radial displacement 0.185 m at A); the problem and target as reproduced in the Abaqus 2016 Benchmarks Guide, section 4.2.3 'LE3: Hemispherical shell with point loads', and in the ESRD StressCheck Benchmarks Guide 'The Standard NAFEMS Benchmarks: Linear Elastic Tests' (2018), p. 12. Tolerances — ur_A: the target is published to three figures; the displacement under a point load on a shear-deformable shell keeps growing slowly as the mesh is refined (32 × 32 is 0.44% low, 64 × 64 0.16%; see README) +- **[nafems-le5](../tests/oracle/nafems-le5/)** (benchmark): NAFEMS LE5 Z-section cantilever (hex20). Source: NAFEMS, The Standard NAFEMS Benchmarks, TNSB Rev. 3, October 1990, test LE5 (target: axial stress −108 MPa at A, mid-surface); the problem and target as reproduced in the Abaqus 2016 Benchmarks Guide, section 4.2.5 'LE5: Z-section cantilever', and in the ESRD StressCheck Benchmarks Guide 'The Standard NAFEMS Benchmarks: Linear Elastic Tests' (2018), p. 15. Tolerances — sxx_A: the target is published to three figures and comes from a thin-walled idealisation; a 3D solid model of the section converges about 1.5% beyond it (ours: −109.46 here, −109.60 on the next mesh; ESRD's p-version solid: −109.5, see README) - **[nl-contact-patch](../tests/oracle/nl-contact-patch/)** (theory): Contact patch test: two blocks with non-matching meshes in uniaxial compression (implicit mortar contact). Source: The contact patch test: a contact discretisation must transmit a uniform pressure across a non-matching interface exactly; Taylor & Papadopoulos, On a patch test for contact problems in two dimensions, Nonlinear Computational Mechanics, Springer 1991, 690–702; Crisfield, Re-visiting the contact patch test, IJNME 48:435–449, 2000; passed by mortar methods: Puso & Laursen, CMAME 193:601–629, 2004. Uniaxial stress between frictionless symmetry planes is uniform in both blocks. Tolerances — small_lower_corner: small sliding is linear and its mortar integrals exact on these rectangular faces: a uniform stress is the discrete solution up to the convergence tolerance (10⁻¹⁰); small_lower_centre: as for the corner element; small_lower_base: as for the corner element; small_upper_top: as for the corner element; small_lateral: frictionless symmetry planes and a frictionless interface leave the blocks free to expand sideways; the non-matching interface must not hold them; small_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; small_force: the pressure carries the whole load across the interface; finite_lower_corner: finite sliding is exact up to the geometric terms of the gaps the augmentation leaves (10⁻⁵ of the element thickness) and the default convergence tolerances; finite_lower_centre: as for the corner element; finite_lower_base: as for the corner element; finite_upper_top: as for the corner element; finite_lateral: frictionless symmetry planes and a frictionless interface leave the blocks free to expand sideways; the non-matching interface must not hold them; finite_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; finite_force: the pressure carries the whole load across the interface; small_stress: the squeeze less the penetration the augmented Lagrangian leaves (up to 10⁻⁵ of the element thickness, a few 10⁻⁵ of the squeeze) - **[nl-contact-patch-axisymmetric](../tests/oracle/nl-contact-patch-axisymmetric/)** (theory): Contact patch test, axisymmetric: two rings with non-matching edges in axial compression (implicit mortar contact). Source: The contact patch test: a contact discretisation must transmit a uniform pressure across a non-matching interface exactly; Taylor & Papadopoulos, On a patch test for contact problems in two dimensions, Nonlinear Computational Mechanics, Springer 1991, 690–702; passed by 2D mortar methods: Yang, Laursen & Meng, IJNME 62:1183–1225, 2005. Uniaxial stress between frictionless supports is uniform in both blocks. Tolerances — small_lower_corner: small sliding is linear and its edge mortar integrals exact (degree-3 Gauss on the overlaps): the uniform stress is the discrete solution up to the convergence tolerance; small_lower_far: as for the corner element; small_lower_base: as for the corner element; small_upper_top: as for the corner element; small_lateral: a frictionless interface (and symmetry plane) leaves the blocks free to expand sideways; the non-matching interface must not hold them; small_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; small_force: the pressure carries the whole load across the interface (both are totals over the circumference); finite_lower_corner: finite sliding is exact up to the geometric terms of the gaps the augmentation leaves (10⁻⁵ of the element thickness) and the default convergence tolerances; finite_lower_far: as for the corner element; finite_lower_base: as for the corner element; finite_upper_top: as for the corner element; finite_lateral: a frictionless interface (and symmetry plane) leaves the blocks free to expand sideways; the non-matching interface must not hold them; finite_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; finite_force: the pressure carries the whole load across the interface (both are totals over the circumference); small_stress: the squeeze less the penetration the augmented Lagrangian leaves (up to 10⁻⁵ of the element thickness) - **[nl-contact-patch-plane-strain](../tests/oracle/nl-contact-patch-plane-strain/)** (theory): Contact patch test in plane strain: two blocks with non-matching edges in uniaxial compression (implicit mortar contact). Source: The contact patch test: a contact discretisation must transmit a uniform pressure across a non-matching interface exactly; Taylor & Papadopoulos, On a patch test for contact problems in two dimensions, Nonlinear Computational Mechanics, Springer 1991, 690–702; passed by 2D mortar methods: Yang, Laursen & Meng, IJNME 62:1183–1225, 2005. Uniaxial stress between frictionless supports is uniform in both blocks. Tolerances — small_lower_corner: small sliding is linear and its edge mortar integrals exact (degree-3 Gauss on the overlaps): the uniform stress is the discrete solution up to the convergence tolerance; small_lower_far: as for the corner element; small_lower_base: as for the corner element; small_upper_top: as for the corner element; small_lateral: a frictionless interface (and symmetry plane) leaves the blocks free to expand sideways; the non-matching interface must not hold them; small_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; small_force: the pressure carries the whole load across the interface (both are totals over the thickness); finite_lower_corner: finite sliding is exact up to the geometric terms of the gaps the augmentation leaves (10⁻⁵ of the element thickness) and the default convergence tolerances; finite_lower_far: as for the corner element; finite_lower_base: as for the corner element; finite_upper_top: as for the corner element; finite_lateral: a frictionless interface (and symmetry plane) leaves the blocks free to expand sideways; the non-matching interface must not hold them; finite_pressure: the mortar multipliers are the nodal values of the contact pressure, uniform and equal to the stress across the interface; finite_force: the pressure carries the whole load across the interface (both are totals over the thickness); small_stress: the squeeze less the penetration the augmented Lagrangian leaves (up to 10⁻⁵ of the element thickness) diff --git a/tests/oracle/nafems-le5/README.md b/tests/oracle/nafems-le5/README.md new file mode 100644 index 0000000..153afc9 --- /dev/null +++ b/tests/oracle/nafems-le5/README.md @@ -0,0 +1,100 @@ +# NAFEMS LE5 Z-section cantilever (hex20) + +Our model of the published problem: a cantilever 10 m long (x) whose cross-section is a Z of +0.1 m thick walls, E = 210 GPa, ν = 0.3 (m, MN, MPa). On the mid-surface the web is 2 m wide, in +the plane z = 0 from y = −1 to y = 1, and the two flanges are 1 m high: the upper one at y = 1 +rises to z = 1, the lower one at y = −1 drops to z = −1. Every displacement is zero at x = 0. A +torque of 1.2 MN·m acts at x = 10 as two shear forces of 0.6 MN, one along each flange's end +(+z on the upper flange, −z on the lower), 2 m apart. The target is the axial stress σ_xx at A, +x = 2.5 on the mid-surface of the lower flange at its free edge (2.5, −1, −1), where the restrained +warping of the section puts its largest axial stress. + +## The mesh + +The walls are solid hex20 blocks (`mesh.block`), the route LE1 and LE10 take: the issue allows +quad4 shells or hex20, and the shell model reaches a different limit (below). Five blocks, +2 elements through every wall: the two flanges (0.95 m outside the web, k = 4 elements), the two +corner blocks where a flange meets the web (2 × 2), and the web between them (8 elements); 32 +elements along the length. The blocks share their faces, so `mesh.block` joins them. The shear +force on each flange is a total over the flange block's end face (z from ±0.05 to ±1), split by +area. A is a node: the mid-plane of the lower flange (y = −1) is a node plane of its 2 elements +through the thickness. + +The recipe was generated with this script (`python3 le5.py 4 > recipe.json`): + +```python +import json, sys + +def le5_solid(k): + """NAFEMS LE5 Z-section as hex20 solid blocks (m, MN, MPa): mid-surface web 2 wide in z = 0, + flanges at y = ±1 reaching z = ±1, all walls 0.1 thick, length 10 along x; k sets the density + (8k cells along the length, k per 0.95 m across the walls, 2 through the thickness).""" + h = 0.05 + blocks = { # name: (y0, y1, z0, z1, ny, nz) + "lower_flange": (-1 - h, -1 + h, -1.0, -h, 2, k), + "lower_corner": (-1 - h, -1 + h, -h, h, 2, 2), + "web": (-1 + h, 1 - h, -h, h, 2 * k, 2), + "upper_corner": (1 - h, 1 + h, -h, h, 2, 2), + "upper_flange": (1 - h, 1 + h, h, 1.0, 2, k), + } + commands = [] + for name, (y0, y1, z0, z1, ny, nz) in blocks.items(): + base = [[0.0, y0, z0], [0.0, y1, z0], [0.0, y1, z1], [0.0, y0, z1]] + corners = base + [[10.0, y, z] for _, y, z in base] + commands.append({"command": "mesh.block", "params": {"body": name, "kind": "hex20", "corners": corners, "divisions": [ny, nz, 8 * k], "faces": {"1": "root", "2": "tip"}}}) + return commands + [ + {"command": "model.set.create", "params": {"name": "all", "geometry": [{"body": b} for b in blocks]}}, + {"command": "model.set.create", "params": {"name": "root", "geometry": [{"body": b, "face": "root"} for b in blocks]}}, + {"command": "model.set.create", "params": {"name": "lower_tip", "geometry": [{"body": "lower_flange", "face": "tip"}]}}, + {"command": "model.set.create", "params": {"name": "upper_tip", "geometry": [{"body": "upper_flange", "face": "tip"}]}}, + {"command": "model.material.create", "params": {"name": "Steel", "elastic": {"type": "isotropic", "young": 210000.0, "poisson": 0.3}}}, + {"command": "model.section.create", "params": {"name": "z_section", "set": "all", "material": "Steel", "kind": {"type": "solid"}}}, + {"command": "model.boundary_condition.create", "params": {"name": "root", "type": "fixed", "target": {"set": "root"}}}, + # the 1.2 MN·m torque: 0.6 MN along each flange's end face, 2 m apart + {"command": "model.load.create", "params": {"name": "lower_shear", "type": "force", "target": {"set": "lower_tip"}, "vector": [0, 0, -0.6]}}, + {"command": "model.load.create", "params": {"name": "upper_shear", "type": "force", "target": {"set": "upper_tip"}, "vector": [0, 0, 0.6]}}, + {"command": "model.step.create", "params": {"name": "Torque", "procedure": {"type": "static"}, "boundary_conditions": ["root"], "loads": ["lower_shear", "upper_shear"]}}, + {"command": "solve.static", "params": {}, "id": "solve"}, + ] + +k = int(sys.argv[1]) if len(sys.argv) > 1 else 4 +print("[\n " + ",\n ".join(json.dumps(c) for c in le5_solid(k)) + "\n]") +``` + +## Refinement + +Every row is the listing above with the row's k, solved and probed at A as the case does; the +case mesh is k = 4: + +| k | elements | σ_xx at A | error | +|---|---|---|---| +| 1 | 128 | −108.93 | +0.86% | +| 2 | 384 | −109.27 | +1.17% | +| 4 (this case) | 1280 | −109.46 | +1.35% | +| 8 | 4608 | −109.60 | +1.48% | + +The result converges to about −109.7, 1.6% beyond the target. The published value comes from a +thin-walled idealisation of the section; a solid model of the same section differs from it by +about that much (ESRD's guide reports −109.5 for its converged p-version solid model on two +meshes, p. 16). The next refinement of the case mesh moves the value by 0.13%, under a tenth of +the tolerance. + +A model of quad4 MITC4 shells on the mid-surface (walls meeting at the junction lines, the edge +shears as consistent nodal forces) converges further away, to about −111.3 (320 elements along the +length and 16 per metre across the walls: −111.65 and −111.06 at A from the two elements that meet +there), 3.1% beyond the target (the Abaqus Benchmarks Guide, Table 4.2.5–1, reports −110.38 for its +S4 shell on the refined mesh); we have not separated how much of the difference is the shell +idealisation of the junctions. That is why this case is the solid model. + +## Tolerance + +2%: the target is published to three figures, and the solid model's converged value differs from +the thin-walled reference by about 1.5% (above). `fixture.json` holds the k = 4 value: the mesh is +built by `mesh.block` from the recipe's corners, so it is the same on every platform. + +## Running it + +```sh +cargo xtask oracle --case nafems-le5 +cargo xtask oracle --bless nafems-le5 # only after the case passes +``` diff --git a/tests/oracle/nafems-le5/expected.json b/tests/oracle/nafems-le5/expected.json new file mode 100644 index 0000000..f507bb9 --- /dev/null +++ b/tests/oracle/nafems-le5/expected.json @@ -0,0 +1,15 @@ +{ + "title": "NAFEMS LE5 Z-section cantilever (hex20)", + "kind": "benchmark", + "source": "NAFEMS, The Standard NAFEMS Benchmarks, TNSB Rev. 3, October 1990, test LE5 (target: axial stress −108 MPa at A, mid-surface); the problem and target as reproduced in the Abaqus 2016 Benchmarks Guide, section 4.2.5 'LE5: Z-section cantilever', and in the ESRD StressCheck Benchmarks Guide 'The Standard NAFEMS Benchmarks: Linear Elastic Tests' (2018), p. 15", + "quantities": [ + { + "id": "sxx_A", + "quantity": "σ_xx at A (2.5, −1, −1), mid-surface of the lower flange at its free edge", + "measure": {"command": "solve.probe", "params": {"step": "Torque", "at": [[2.5, -1, -1]]}, "pointer": "/nodes/0/stress/0"}, + "value": -108.0, + "tolerance": {"relative": 0.02}, + "reason": "the target is published to three figures and comes from a thin-walled idealisation; a 3D solid model of the section converges about 1.5% beyond it (ours: −109.46 here, −109.60 on the next mesh; ESRD's p-version solid: −109.5, see README)" + } + ] +} diff --git a/tests/oracle/nafems-le5/fixture.json b/tests/oracle/nafems-le5/fixture.json new file mode 100644 index 0000000..a142132 --- /dev/null +++ b/tests/oracle/nafems-le5/fixture.json @@ -0,0 +1,9 @@ +{ + "note": "FieldCraft's accepted results for this case, written by `cargo xtask oracle --bless`. A result outside its band fails CI.", + "values": { + "sxx_A": { + "band": 1.0945472240551884e-7, + "value": -109.45472240551884 + } + } +} diff --git a/tests/oracle/nafems-le5/recipe.json b/tests/oracle/nafems-le5/recipe.json new file mode 100644 index 0000000..4be4f58 --- /dev/null +++ b/tests/oracle/nafems-le5/recipe.json @@ -0,0 +1,18 @@ +[ + {"command": "mesh.block", "params": {"body": "lower_flange", "kind": "hex20", "corners": [[0.0, -1.05, -1.0], [0.0, -0.95, -1.0], [0.0, -0.95, -0.05], [0.0, -1.05, -0.05], [10.0, -1.05, -1.0], [10.0, -0.95, -1.0], [10.0, -0.95, -0.05], [10.0, -1.05, -0.05]], "divisions": [2, 4, 32], "faces": {"1": "root", "2": "tip"}}}, + {"command": "mesh.block", "params": {"body": "lower_corner", "kind": "hex20", "corners": [[0.0, -1.05, -0.05], [0.0, -0.95, -0.05], [0.0, -0.95, 0.05], [0.0, -1.05, 0.05], [10.0, -1.05, -0.05], [10.0, -0.95, -0.05], [10.0, -0.95, 0.05], [10.0, -1.05, 0.05]], "divisions": [2, 2, 32], "faces": {"1": "root", "2": "tip"}}}, + {"command": "mesh.block", "params": {"body": "web", "kind": "hex20", "corners": [[0.0, -0.95, -0.05], [0.0, 0.95, -0.05], [0.0, 0.95, 0.05], [0.0, -0.95, 0.05], [10.0, -0.95, -0.05], [10.0, 0.95, -0.05], [10.0, 0.95, 0.05], [10.0, -0.95, 0.05]], "divisions": [8, 2, 32], "faces": {"1": "root", "2": "tip"}}}, + {"command": "mesh.block", "params": {"body": "upper_corner", "kind": "hex20", "corners": [[0.0, 0.95, -0.05], [0.0, 1.05, -0.05], [0.0, 1.05, 0.05], [0.0, 0.95, 0.05], [10.0, 0.95, -0.05], [10.0, 1.05, -0.05], [10.0, 1.05, 0.05], [10.0, 0.95, 0.05]], "divisions": [2, 2, 32], "faces": {"1": "root", "2": "tip"}}}, + {"command": "mesh.block", "params": {"body": "upper_flange", "kind": "hex20", "corners": [[0.0, 0.95, 0.05], [0.0, 1.05, 0.05], [0.0, 1.05, 1.0], [0.0, 0.95, 1.0], [10.0, 0.95, 0.05], [10.0, 1.05, 0.05], [10.0, 1.05, 1.0], [10.0, 0.95, 1.0]], "divisions": [2, 4, 32], "faces": {"1": "root", "2": "tip"}}}, + {"command": "model.set.create", "params": {"name": "all", "geometry": [{"body": "lower_flange"}, {"body": "lower_corner"}, {"body": "web"}, {"body": "upper_corner"}, {"body": "upper_flange"}]}}, + {"command": "model.set.create", "params": {"name": "root", "geometry": [{"body": "lower_flange", "face": "root"}, {"body": "lower_corner", "face": "root"}, {"body": "web", "face": "root"}, {"body": "upper_corner", "face": "root"}, {"body": "upper_flange", "face": "root"}]}}, + {"command": "model.set.create", "params": {"name": "lower_tip", "geometry": [{"body": "lower_flange", "face": "tip"}]}}, + {"command": "model.set.create", "params": {"name": "upper_tip", "geometry": [{"body": "upper_flange", "face": "tip"}]}}, + {"command": "model.material.create", "params": {"name": "Steel", "elastic": {"type": "isotropic", "young": 210000.0, "poisson": 0.3}}}, + {"command": "model.section.create", "params": {"name": "z_section", "set": "all", "material": "Steel", "kind": {"type": "solid"}}}, + {"command": "model.boundary_condition.create", "params": {"name": "root", "type": "fixed", "target": {"set": "root"}}}, + {"command": "model.load.create", "params": {"name": "lower_shear", "type": "force", "target": {"set": "lower_tip"}, "vector": [0, 0, -0.6]}}, + {"command": "model.load.create", "params": {"name": "upper_shear", "type": "force", "target": {"set": "upper_tip"}, "vector": [0, 0, 0.6]}}, + {"command": "model.step.create", "params": {"name": "Torque", "procedure": {"type": "static"}, "boundary_conditions": ["root"], "loads": ["lower_shear", "upper_shear"]}}, + {"command": "solve.static", "params": {}, "id": "solve"} +]