diff --git a/docs/oracle.md b/docs/oracle.md index 7a8d69b..03516a6 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. -**163 of 169 quantities pass** (6 known failure(s)) in 49 active cases: 40 theory, 9 published benchmark, 0 experiment; 9 pending cases wait for capabilities still being built. +**170 of 176 quantities pass** (6 known failure(s)) in 50 active cases: 41 theory, 9 published benchmark, 0 experiment; 9 pending cases wait for capabilities still being built. ## Theory: closed-form solutions, patch tests and mesh invariants @@ -90,6 +90,13 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the | [nl-thermal-expansion](../tests/oracle/nl-thermal-expansion/) | stress of the held block, σ_xx (hydrostatic) | -3.5108 | -3.5108 | < 1e-7% | 1e-8 rel. | pass | matches | | [nl-yeoh-uniaxial](../tests/oracle/nl-yeoh-uniaxial/) | axial Cauchy stress, stretch λ = 2 | 3.1743 | 3.1743 | < 1e-7% | 1e-6 rel. | pass | matches | | [nl-yeoh-uniaxial](../tests/oracle/nl-yeoh-uniaxial/) | lateral displacement at (1, 1, 1): l − 1 | -0.28545 | -0.28545 | < 1e-7% | 1e-6 rel. | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | x reaction on the clamped face (5 mm mesh) | -2000 | -2000 | < 1e-7% | 1e-6 rel. | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | elements the certified Jacobian check does not prove valid (5 mm mesh) | 0 | 0 | ≈ 0 | exact | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | mesh volume against the CAD volume (5 mm mesh) | 32145 | 32177 | 0.1% | 0.3% | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | σ_yy on the outer face of the upright leg at y = 19, z = 6 (5 mm mesh) | 145.31 | 149.26 | 2.72% | 5% | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | σ_yy on the inner face of the upright leg at y = 19, z = 6 (5 mm mesh) | -145.31 | -147.28 | -1.35% | 5% | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | von Mises stress at the middle of the fillet, 7 mm mesh | 143.21 | 138.53 | -3.27% | 5% | pass | matches | +| [octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/) | von Mises stress at the middle of the fillet, 5 mm mesh | 143.21 | 144.53 | 0.921% | 2% | pass | matches | | [patch-hex20](../tests/oracle/patch-hex20/) | u_x at the corner (1, 1, 1) | -2.5000e-4 | -2.5000e-4 | < 1e-7% | 1e-9 rel. | pass | matches | | [patch-hex20](../tests/oracle/patch-hex20/) | u_y at the corner (1, 1, 1) | 0.001 | 0.001 | < 1e-7% | 1e-9 rel. | pass | matches | | [patch-hex20](../tests/oracle/patch-hex20/) | u_z at the corner (1, 1, 1) | 0.00225 | 0.00225 | < 1e-7% | 1e-9 rel. | pass | matches | @@ -220,6 +227,7 @@ Generated by `cargo xtask oracle` from [`tests/oracle/`](../tests/oracle/); the - **[nl-rigid-rotation](../tests/oracle/nl-rigid-rotation/)** (theory): A quarter turn strains and stresses nothing (objectivity). Source: Objectivity of the total Lagrangian formulation and of the hyperelastic stress: a rigid motion x = R X + c gives C = I and σ = 0 whatever the rotation (Bonet & Wood 2008, §3.4 and §5.2; T. Belytschko, W. K. Liu, B. Moran, Nonlinear Finite Elements for Continua and Structures, 2nd ed., Wiley 2014, §5.5). Tolerances — ux: the inner nodes follow the rigid rotation exactly; uy: the inner nodes follow the rigid rotation exactly; uz: the inner nodes follow the rigid rotation exactly; von_mises: zero up to rounding of stresses of order E = 1000: a 90° turn leaves no strain - **[nl-thermal-expansion](../tests/oracle/nl-thermal-expansion/)** (theory): Free and fully constrained thermal expansion at finite strain. Source: The multiplicative split F = F_m F_θ with F_θ = (1 + α ΔT) I (V. A. Lubarda, Appl. Mech. Rev. 57(2):95–108, 2004): a free body follows F = F_θ stress-free; held on every face, F = I and F_m = ϑ⁻¹ I, so the compressible neo-Hookean material carries the pressure of its volume ratio, σ = κ (ϑ⁻³ − 1). Tolerances — free_u: a homogeneous stretch every element represents exactly; free_stress: rounding only; held_stress: a homogeneous state; E = 10, ν = 0.3 (κ = 8.3333), ϑ = 1.2 - **[nl-yeoh-uniaxial](../tests/oracle/nl-yeoh-uniaxial/)** (theory): Yeoh block in uniaxial tension. Source: Uniaxial tension of the decoupled compressible Yeoh energy W = C₁₀ (Ī₁ − 3) + C₂₀ (Ī₁ − 3)² + C₃₀ (Ī₁ − 3)³ + κ/2 (J − 1)² (O. H. Yeoh, Rubber Chem. Technol. 66(5):754–771, 1993): σ = (λ/J) ∂W/∂λ with the lateral stretch found so σ_yy = 0 (bisection; E = 3, ν = 0.49, C₁₀ = μ/2, C₂₀ = −0.02, C₃₀ = 0.004). Tolerances — sigma: a homogeneous deformation the hybrid hex20 represents exactly: the tolerance admits the Newton tolerance and rounding; lateral: a homogeneous deformation the hybrid hex20 represents exactly: the tolerance admits the Newton tolerance and rounding +- **[octree-filleted-bracket](../tests/oracle/octree-filleted-bracket/)** (theory): Filleted L-bracket on the octree all-hex mesh: equilibrium, beam bending, fillet stress convergence. Source: equilibrium; Euler–Bernoulli bending of the 8 × 40 mm leg, σ = M c / I with M = F (50 − y) (S. P. Timoshenko, J. N. Goodier, Theory of Elasticity, 3rd ed., 1970, §102); grid convergence of the fillet stress against the 4 mm mesh of this case (P. J. Roache, Verification and Validation in Computational Science and Engineering, 1998, ch. 5). Tolerances — reaction_x: the reactions balance the 2 kN load to solver precision; invalid: the octree mesher returns only elements proven valid; volume: bilinear boundary faces on the R6 fillet and the two Ø8 holes, and the 1e-3 chordal tessellation they are fitted to; syy_outer: beam theory ignores the corner 5 mm below and the hole 9 mm above; the probe takes the nearest node's extrapolated stress; syy_inner: as for the outer face; fillet_7: the coarsest mesh, within 5% of the 4 mm mesh; fillet_5: converging: within 2% of the 4 mm mesh - **[patch-hex20](../tests/oracle/patch-hex20/)** (theory): Constant-stress patch test, hex20. Source: Irons and Razzaque, 'Experience with the patch test for convergence of finite elements', in Aziz (ed.), The Mathematical Foundations of the Finite Element Method, Academic Press 1972, pp. 557–587; MacNeal and Harder, Finite Elements in Analysis and Design 1 (1985) 3–20, constant-stress loading, p. 14 Table 7. Tolerances — ux: a patch test is passed exactly: the tolerance only admits rounding; uy: a patch test is passed exactly: the tolerance only admits rounding; uz: a patch test is passed exactly: the tolerance only admits rounding; sxx: a patch test is passed exactly: the tolerance only admits rounding; syy: a patch test is passed exactly: the tolerance only admits rounding; szz: a patch test is passed exactly: the tolerance only admits rounding; sxy: a patch test is passed exactly: the tolerance only admits rounding (stresses of order 1) - **[patch-hex20-reduced](../tests/oracle/patch-hex20-reduced/)** (theory): Constant-stress patch test, hex20-reduced. Source: Irons and Razzaque, 'Experience with the patch test for convergence of finite elements', in Aziz (ed.), The Mathematical Foundations of the Finite Element Method, Academic Press 1972, pp. 557–587; MacNeal and Harder, Finite Elements in Analysis and Design 1 (1985) 3–20, constant-stress loading, p. 14 Table 7. Tolerances — ux: a patch test is passed exactly: the tolerance only admits rounding; uy: a patch test is passed exactly: the tolerance only admits rounding; uz: a patch test is passed exactly: the tolerance only admits rounding; sxx: a patch test is passed exactly: the tolerance only admits rounding; syy: a patch test is passed exactly: the tolerance only admits rounding; szz: a patch test is passed exactly: the tolerance only admits rounding; sxy: a patch test is passed exactly: the tolerance only admits rounding (stresses of order 1) - **[patch-hex8](../tests/oracle/patch-hex8/)** (theory): Constant-stress patch test, hex8. Source: Irons and Razzaque, 'Experience with the patch test for convergence of finite elements', in Aziz (ed.), The Mathematical Foundations of the Finite Element Method, Academic Press 1972, pp. 557–587; MacNeal and Harder, Finite Elements in Analysis and Design 1 (1985) 3–20, constant-stress loading, p. 14 Table 7. Tolerances — ux: a patch test is passed exactly: the tolerance only admits rounding; uy: a patch test is passed exactly: the tolerance only admits rounding; uz: a patch test is passed exactly: the tolerance only admits rounding; sxx: a patch test is passed exactly: the tolerance only admits rounding; syy: a patch test is passed exactly: the tolerance only admits rounding; szz: a patch test is passed exactly: the tolerance only admits rounding; sxy: a patch test is passed exactly: the tolerance only admits rounding (stresses of order 1) diff --git a/tests/oracle/octree-filleted-bracket/README.md b/tests/oracle/octree-filleted-bracket/README.md new file mode 100644 index 0000000..b883d76 --- /dev/null +++ b/tests/oracle/octree-filleted-bracket/README.md @@ -0,0 +1,21 @@ +# Filleted L-bracket on the octree all-hex mesh + +The corpus part `filleted-bracket` (an 8 mm thick L, 60 × 50 × 40 mm, an R6 fillet inside the +corner and a Ø8 hole through each leg), built with SolidCraft from `design.json` and meshed by +`mesh.hex.octree` (hex8 with incompatible modes, steel E = 210 GPa, ν = 0.3). The bottom face of +the horizontal leg (`F2:side:l1`) is clamped and the top face of the upright leg (`F2:side:l5`) +carries 2 kN along +x. + +Checked on the 5 mm mesh: equilibrium; the certified validity of every element; the mesh volume +against the CAD volume; the bending stress of the upright leg at y = 19 on both faces, +σ_yy = M c / I = 2000 · 31 · 4 / 1706.7 = 145.3 MPa (M is exact by statics, the plane-section +stress is the reference, the corner and a hole are near). The von Mises stress at the middle of +the fillet, which has no closed form, is checked for convergence against the 4 mm mesh: + +| mesh size | hexahedra | fillet von Mises (MPa) | σ_yy outer / inner at y = 19 (MPa) | +|---|---|---|---| +| 7 mm | 4291 | 138.5 | 149.4 / −146.9 | +| 5 mm | 13273 | 144.5 | 149.3 / −147.3 | +| 4 mm (reference) | 21508 | 143.2 | 147.4 / −145.5 | + +A 3 mm mesh needs more than the direct solver's factor cap. diff --git a/tests/oracle/octree-filleted-bracket/design.json b/tests/oracle/octree-filleted-bracket/design.json new file mode 100644 index 0000000..488b840 --- /dev/null +++ b/tests/oracle/octree-filleted-bracket/design.json @@ -0,0 +1,89 @@ +{ + "commands": [ + { + "command": "sketch.create", + "params": { + "plane": "XY", + "name": "Profile" + } + }, + { + "command": "sketch.line", + "params": { + "points": [ + [ + 0, + 0 + ], + [ + 60, + 0 + ], + [ + 60, + 8 + ], + [ + 8, + 8 + ], + [ + 8, + 50 + ], + [ + 0, + 50 + ] + ], + "closed": true + } + }, + { + "command": "sketch.finish", + "params": {} + }, + { + "command": "solid.extrude", + "params": { + "distance": 40, + "body_name": "Bracket" + } + }, + { + "command": "solid.fillet", + "params": { + "edges": [ + [ + 8, + 8, + 20 + ] + ], + "radius": 6 + } + }, + { + "command": "solid.hole", + "params": { + "position": [ + 40, + 8, + 20 + ], + "diameter": 8 + } + }, + { + "command": "solid.hole", + "params": { + "position": [ + 8, + 32, + 20 + ], + "diameter": 8 + } + } + ] +} \ No newline at end of file diff --git a/tests/oracle/octree-filleted-bracket/expected.json b/tests/oracle/octree-filleted-bracket/expected.json new file mode 100644 index 0000000..f3920d8 --- /dev/null +++ b/tests/oracle/octree-filleted-bracket/expected.json @@ -0,0 +1,99 @@ +{ + "title": "Filleted L-bracket on the octree all-hex mesh: equilibrium, beam bending, fillet stress convergence", + "kind": "theory", + "source": "equilibrium; Euler–Bernoulli bending of the 8 × 40 mm leg, σ = M c / I with M = F (50 − y) (S. P. Timoshenko, J. N. Goodier, Theory of Elasticity, 3rd ed., 1970, §102); grid convergence of the fillet stress against the 4 mm mesh of this case (P. J. Roache, Verification and Validation in Computational Science and Engineering, 1998, ch. 5)", + "slow": true, + "quantities": [ + { + "id": "reaction_x", + "quantity": "x reaction on the clamped face (5 mm mesh)", + "measure": { + "from": "solve5", + "pointer": "/reaction_total/0" + }, + "value": -2000.0, + "tolerance": { + "relative": 1e-06 + }, + "reason": "the reactions balance the 2 kN load to solver precision" + }, + { + "id": "invalid", + "quantity": "elements the certified Jacobian check does not prove valid (5 mm mesh)", + "measure": { + "from": "quality5", + "pointer": "/invalid/count" + }, + "value": 0.0, + "tolerance": { + "exact": true + }, + "reason": "the octree mesher returns only elements proven valid" + }, + { + "id": "volume", + "quantity": "mesh volume against the CAD volume (5 mm mesh)", + "measure": { + "from": "mesh5", + "pointer": "/bodies/0/volume" + }, + "value": 32144.778925, + "tolerance": { + "relative": 0.003 + }, + "reason": "bilinear boundary faces on the R6 fillet and the two Ø8 holes, and the 1e-3 chordal tessellation they are fitted to" + }, + { + "id": "syy_outer", + "quantity": "σ_yy on the outer face of the upright leg at y = 19, z = 6 (5 mm mesh)", + "measure": { + "from": "probe5", + "pointer": "/nodes/1/stress/1" + }, + "value": 145.3125, + "tolerance": { + "relative": 0.05 + }, + "reason": "beam theory ignores the corner 5 mm below and the hole 9 mm above; the probe takes the nearest node's extrapolated stress" + }, + { + "id": "syy_inner", + "quantity": "σ_yy on the inner face of the upright leg at y = 19, z = 6 (5 mm mesh)", + "measure": { + "from": "probe5", + "pointer": "/nodes/2/stress/1" + }, + "value": -145.3125, + "tolerance": { + "relative": 0.05 + }, + "reason": "as for the outer face" + }, + { + "id": "fillet_7", + "quantity": "von Mises stress at the middle of the fillet, 7 mm mesh", + "measure": { + "from": "probe7", + "pointer": "/nodes/0/von_mises" + }, + "value": 143.21, + "tolerance": { + "relative": 0.05 + }, + "reason": "the coarsest mesh, within 5% of the 4 mm mesh" + }, + { + "id": "fillet_5", + "quantity": "von Mises stress at the middle of the fillet, 5 mm mesh", + "measure": { + "from": "probe5", + "pointer": "/nodes/0/von_mises" + }, + "value": 143.21, + "tolerance": { + "relative": 0.02 + }, + "reason": "converging: within 2% of the 4 mm mesh" + } + ] +} \ No newline at end of file diff --git a/tests/oracle/octree-filleted-bracket/fixture.json b/tests/oracle/octree-filleted-bracket/fixture.json new file mode 100644 index 0000000..9cd226b --- /dev/null +++ b/tests/oracle/octree-filleted-bracket/fixture.json @@ -0,0 +1,33 @@ +{ + "note": "FieldCraft's accepted results for this case, written by `cargo xtask oracle --bless`. A result outside its band fails CI.", + "values": { + "fillet_5": { + "band": 1.4452906175564113e-7, + "value": 144.52906175564112 + }, + "fillet_7": { + "band": 1.4321000000000002e-7, + "value": 138.5286423070889 + }, + "invalid": { + "band": 0.0, + "value": 0.0 + }, + "reaction_x": { + "band": 2.0000000000000003e-6, + "value": -1999.999999998785 + }, + "syy_inner": { + "band": 1.472793063397077e-7, + "value": -147.2793063397077 + }, + "syy_outer": { + "band": 1.4926027331710374e-7, + "value": 149.26027331710372 + }, + "volume": { + "band": 0.00003217691091849411, + "value": 32176.910918494108 + } + } +} diff --git a/tests/oracle/octree-filleted-bracket/recipe.json b/tests/oracle/octree-filleted-bracket/recipe.json new file mode 100644 index 0000000..a637497 --- /dev/null +++ b/tests/oracle/octree-filleted-bracket/recipe.json @@ -0,0 +1,228 @@ +[ + { + "command": "model.material.create", + "params": { + "name": "Steel", + "elastic": { + "type": "isotropic", + "young": 210000.0, + "poisson": 0.3 + } + } + }, + { + "command": "mesh.hex.octree", + "params": { + "body": "Bracket", + "size": 7.0 + }, + "id": "mesh7" + }, + { + "command": "model.set.create", + "params": { + "name": "all", + "geometry": [ + { + "body": "Bracket" + } + ] + } + }, + { + "command": "model.set.create", + "params": { + "name": "base", + "geometry": [ + { + "body": "Bracket", + "face": "F2:side:l1" + } + ] + } + }, + { + "command": "model.set.create", + "params": { + "name": "top", + "geometry": [ + { + "body": "Bracket", + "face": "F2:side:l5" + } + ] + } + }, + { + "command": "model.section.create", + "params": { + "name": "solid", + "set": "all", + "material": "Steel", + "kind": { + "type": "solid" + }, + "formulation": { + "incompatible_modes": true + } + } + }, + { + "command": "model.boundary_condition.create", + "params": { + "name": "clamp", + "type": "fixed", + "target": { + "set": "base" + } + } + }, + { + "command": "model.load.create", + "params": { + "name": "F", + "type": "force", + "target": { + "set": "top" + }, + "vector": [ + 2000.0, + 0, + 0 + ] + } + }, + { + "command": "model.step.create", + "params": { + "name": "Load", + "procedure": { + "type": "static" + }, + "boundary_conditions": [ + "clamp" + ], + "loads": [ + "F" + ] + } + }, + { + "command": "mesh.quality", + "params": {}, + "id": "quality7" + }, + { + "command": "solve.static", + "params": {}, + "id": "solve7" + }, + { + "command": "solve.probe", + "params": { + "step": "Load", + "at": [ + [ + 9.757359312880714, + 9.757359312880714, + 10.0 + ], + [ + 0.0, + 19.0, + 6.0 + ], + [ + 8.0, + 19.0, + 6.0 + ] + ] + }, + "id": "probe7" + }, + { + "command": "mesh.hex.octree", + "params": { + "body": "Bracket", + "size": 5.0 + }, + "id": "mesh5" + }, + { + "command": "mesh.quality", + "params": {}, + "id": "quality5" + }, + { + "command": "solve.static", + "params": {}, + "id": "solve5" + }, + { + "command": "solve.probe", + "params": { + "step": "Load", + "at": [ + [ + 9.757359312880714, + 9.757359312880714, + 10.0 + ], + [ + 0.0, + 19.0, + 6.0 + ], + [ + 8.0, + 19.0, + 6.0 + ] + ] + }, + "id": "probe5" + }, + { + "command": "mesh.hex.octree", + "params": { + "body": "Bracket", + "size": 4.0 + }, + "id": "mesh4" + }, + { + "command": "mesh.quality", + "params": {}, + "id": "quality4" + }, + { + "command": "solve.static", + "params": {}, + "id": "solve4" + }, + { + "command": "solve.probe", + "params": { + "step": "Load", + "at": [ + [ + 9.757359312880714, + 9.757359312880714, + 10.0 + ], + [ + 0.0, + 19.0, + 6.0 + ], + [ + 8.0, + 19.0, + 6.0 + ] + ] + }, + "id": "probe4" + } +] \ No newline at end of file