engine: the I-beam cantilever sample (ibeam, #143)

The README video's model as a sample: the corpus cantilever-i-beam design opened as geometry,
swept all-hex, clamped at F2:start, 1 kN on F2:top through a kinematic coupling; static step
Load. It has a Start-card thumbnail and a gallery capture; a test checks its tip deflection
against beam theory with shear.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
jyvikoandClaude Opus 5.5 authored and Irakli Kourtis committed 2026-10-15 11:07:46 -04:00
1 parent 0625125ed7
commit 4711f9474a
7 files changed
+68 -1

No files matched your search

+2
View File
@@ -27,6 +27,8 @@ is committed and listed here with its exports.
| docs/media/examples/flow-past.png | FieldCraft contributors | Original: the FieldCraft window captured (`ui.capture`) with the flow-past sample (its bracket; the viewport does not draw flow results yet) (docs/examples.md) | MIT OR Apache-2.0 |
| docs/media/examples/cantilever.png | FieldCraft contributors | Original: the FieldCraft window captured (`ui.capture`) with the solved cantilever sample (docs/examples.md) | MIT OR Apache-2.0 |
| docs/media/examples/impact.png | FieldCraft contributors | Original: the FieldCraft window captured (`ui.capture`) with the solved impact sample (docs/examples.md) | MIT OR Apache-2.0 |
| docs/media/examples/ibeam.png | FieldCraft contributors | Original: the FieldCraft window captured (`ui.capture`) with the solved ibeam sample (docs/examples.md) | MIT OR Apache-2.0 |
| crates/ui-egui/thumbnails/ibeam.png | FieldCraft contributors | Original: the solved ibeam sample rendered by FieldCraft (`ui.render`, light theme), cropped to the model and scaled to 320 px wide (ImageMagick, 128-colour palette), the ibeam sample's thumbnail on the Start card | MIT OR Apache-2.0 |
| docs/media/examples/parts.png | FieldCraft contributors | Original: the FieldCraft window captured (`ui.capture`) with the parts sample (meshes only, nothing to solve) (docs/examples.md) | MIT OR Apache-2.0 |
| crates/ui-egui/thumbnails/flow-past.png | FieldCraft contributors | Original: the flow-past sample as docs/media/examples/flow-past.png shows it, rendered by FieldCraft (`ui.render`, light theme), cropped to the model and scaled to 320 px wide (ImageMagick, 128-colour palette), the flow-past sample's thumbnail on the Start card | MIT OR Apache-2.0 |
| crates/ui-egui/thumbnails/cantilever.png | FieldCraft contributors | Original: the cantilever sample as docs/media/examples/cantilever.png shows it, rendered by FieldCraft (`ui.render`, light theme), cropped to the model and scaled to 320 px wide (ImageMagick, 128-colour palette), the cantilever sample's thumbnail on the Start card | MIT OR Apache-2.0 |
+1
View File
@@ -1543,6 +1543,7 @@ pub const SAMPLE: &[Field] = &[choice(
("cantilever", "Cantilever"),
("impact", "Impact"),
("parts", "Parts"),
("ibeam", "I-beam cantilever"),
("drop-test", "Drop test"),
("rubber-seal", "Rubber seal"),
("flow-past", "Flow past a bracket"),
+54 -1
View File
@@ -11,7 +11,7 @@
//! explicit dynamic step.
//!
//! The examples gallery (docs/examples.md), each ready to solve with `solve.run`:
//! [`drop_test`], [`rubber_seal`], [`flow_past`], [`heated_bracket`], [`bracket_hex`] and [`crank_arm`]. Their
//! [`ibeam`], [`drop_test`], [`rubber_seal`], [`flow_past`], [`heated_bracket`], [`bracket_hex`] and [`crank_arm`]. Their
//! geometry is a SolidCraft script run when the sample opens ([`Sample::design`]): a corpus
//! recipe (`tests/corpus`) or [`UPRIGHT_BRACKET`].
@@ -60,6 +60,14 @@ pub const SAMPLES: &[Sample] = &[
prepare: None,
script: |_| impact(),
},
Sample {
name: "ibeam",
label: "I-Beam Cantilever",
about: "A 200 mm steel I-beam swept all-hex, clamped at one end with 1 kN on the other through a coupling: static, ready to solve",
design: Some(CANTILEVER_I_BEAM),
prepare: None,
script: |_| ibeam(),
},
Sample {
name: "drop-test",
label: "Drop Test",
@@ -290,6 +298,36 @@ pub fn impact() -> Value {
Value::Array(steps)
}
/// The 200 mm I-beam lying along x, web upright (`tests/corpus/cantilever-i-beam`): 40 × 40 mm,
/// 3 mm flanges, a 2 mm web. Body `Beam`; its end faces are `F2:start` (x = 0) and `F2:top`
/// (x = 200).
pub const CANTILEVER_I_BEAM: &str = include_str!("../../../tests/corpus/cantilever-i-beam/recipe.json");
/// The I-beam's tip load (N).
pub const IBEAM_LOAD: f64 = 1000.0;
/// [`CANTILEVER_I_BEAM`] swept all-hex (hex8 of 4 mm, incompatible modes) in steel, clamped at
/// x = 0 (set `root`), [`IBEAM_LOAD`] down (−z) on a reference point on the beam's axis at the
/// free end, kinematically coupled to the end face (set `tip`) so the section stays plane. Step
/// `Load` (linear static). The README demo's model (tools/demo-video).
pub fn ibeam() -> Value {
let face = |f: &str| json!([{"body": "Beam", "face": f}]);
json!([
{"command": "model.units", "params": {"system": "mm-t-s-N-MPa"}},
{"command": "mesh.hex.sweep", "params": {"body": "Beam", "size": 4}},
{"command": "model.set.create", "params": {"name": "beam", "geometry": [{"body": "Beam"}]}},
{"command": "model.material.create", "params": {"name": "Steel", "elastic": {"type": "isotropic", "young": 210000, "poisson": 0.3}, "density": 7.85e-9}},
{"command": "model.section.create", "params": {"name": "Beam", "set": "beam", "material": "Steel", "kind": {"type": "solid"}, "formulation": {"incompatible_modes": true}}},
{"command": "model.set.create", "params": {"name": "root", "geometry": face("F2:start")}},
{"command": "model.boundary_condition.create", "params": {"name": "Clamp", "type": "fixed", "target": {"set": "root"}}},
{"command": "model.set.create", "params": {"name": "tip", "geometry": face("F2:top")}},
{"command": "model.reference_point.create", "params": {"name": "End", "xyz": [200, 0, 20]}},
{"command": "model.coupling.create", "params": {"name": "EndPlate", "reference_point": "End", "set": "tip", "kind": "kinematic"}},
{"command": "model.load.create", "params": {"name": "Push", "type": "force", "target": {"reference_point": "End"}, "vector": [0, 0, -IBEAM_LOAD]}},
{"command": "model.step.create", "params": {"name": "Load", "procedure": {"type": "static"}, "boundary_conditions": ["Clamp"], "loads": ["Push"]}},
])
}
/// An L-bracket standing on its foot, Z up, as a SolidCraft script: a 60 × 40 mm foot and a 50 mm
/// upright, both 8 mm thick, with an R6 fillet inside the bend (the corpus `filleted-bracket`'s
/// profile without its holes, sketched on XZ so it sweeps along y). Body `Bracket`; its faces
@@ -646,6 +684,21 @@ mod tests {
assert_eq!(f["modes"].as_array().map(Vec::len), Some(6));
}
/// The I-beam opens swept all-hex and ready; solved, its tip deflection is the beam theory's
/// bending plus shear (P L³ / 3 E I + P L / G A_web: 0.149 + 0.035 mm) within 5 %.
#[test]
fn the_ibeam_bends_as_beam_theory() {
let mut s = open("ibeam");
assert!(s.model.elements().values().all(|e| e.kind == fieldcraft_model::ElementKind::Hex8));
s.execute("solve.static", &json!({})).unwrap();
let p = s.execute("solve.probe", &json!({"step": "Load", "at": [[200.0, 0.0, 20.0]]})).unwrap();
let tip = -p["nodes"][0]["displacement"][2].as_f64().unwrap();
let (e, g) = (210000.0, 210000.0 / 2.6);
let i = (40.0 * 40.0f64.powi(3) - 38.0 * 34.0f64.powi(3)) / 12.0;
let theory = IBEAM_LOAD * 200.0f64.powi(3) / (3.0 * e * i) + IBEAM_LOAD * 200.0 / (g * 2.0 * 37.0);
assert!((tip - theory).abs() < 0.05 * theory, "{tip} against {theory}");
}
#[test]
fn the_impact_runs_and_keeps_frames_with_stresses() {
let mut s = Session::default();
+1
View File
@@ -23,6 +23,7 @@ const THUMBNAILS: &[(&str, &[u8])] = &[
("cantilever", include_bytes!("../thumbnails/cantilever.png")),
("impact", include_bytes!("../thumbnails/impact.png")),
("parts", include_bytes!("../thumbnails/parts.png")),
("ibeam", include_bytes!("../thumbnails/ibeam.png")),
];
/// The thumbnails' aspect ratio (height over width).
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

+10
View File
@@ -188,3 +188,13 @@ panel.
A hex8 block beside a tet4 cube: meshes to look at (element types, nodes, picking), nothing to
solve.
## I-beam cantilever (`ibeam`)
![Von Mises stress on the swept hex8 I-beam, clamped at x = 0 with 1 kN on its free end](media/examples/ibeam.png)
The 200 mm steel I-beam of the README video (`tests/corpus/cantilever-i-beam`), opened as
SolidCraft geometry with persistent face names, swept all-hex (hex8 of 4 mm, incompatible modes),
clamped at `F2:start` and loaded with 1000 N down through a kinematic coupling to `F2:top`.
**Look at:** the bending stress in the flanges, highest at the clamp; the tip moves 0.18 mm down
(beam theory with shear: 0.184 mm).
Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB