forked from 1b-agent/fieldcraft-upstream
demo video: a horizontal cantilever, checked against beam theory
The demo used the corpus I-beam, which is extruded along z and so stood upright. A new corpus part, cantilever-i-beam, is the same section on the YZ plane extruded along x, web upright: a sweep along a horizontal axis for the meshers, and a cantilever that bends downward on screen. The i-beam part is unchanged (its corpus row and the meshing tests keep it). The demo clamps it at x = 0 and loads the free end with 1000 N in -z through a kinematic coupling, and now checks the solution against beam theory and stops when it is off: tip deflection (bending plus shear) within 3 %, root bending stress (stress_xx along the top flange, extrapolated to the root, nodes from the Abaqus deck) within 2 %. docs/meshing/corpus.md regenerated with the new part. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01NmAttHhUZTokgPeEJia8sm
This commit is contained in:
1 parent
025c826dd4
commit
357de3f7a5
5 files changed
+121
-40
No files matched your search
+25
-23
@@ -10,40 +10,41 @@ clock on the machine that ran it; every other column is deterministic.
|
|||||||
|
|
||||||
| part | categories | bodies | faces | triangles | tol. mm | build ms | enclosed mm³ | SolidCraft mm³ | rel. diff | exact mm³ | boundary | genus | feature edges | check ms | CDT tets | Steiner | cavities | tet gate | CDT ms | hex | hex elements | hex8 min SJ | hex20 min SJ | hex ms |
|
| part | categories | bodies | faces | triangles | tol. mm | build ms | enclosed mm³ | SolidCraft mm³ | rel. diff | exact mm³ | boundary | genus | feature edges | check ms | CDT tets | Steiner | cavities | tet gate | CDT ms | hex | hex elements | hex8 min SJ | hex20 min SJ | hex ms |
|
||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||
| bent-tube | sweepable, thin-wall, genus | 1 | 18 | 4296 | 0.121 | 454 | 4213.1 | 4159.4 | 1.3e-2 | 4159.8 | ok, 2148 vertices | 1 | 268 | 11.5 | 15550 | 2338 | 0 | pass | 606 | sweep | 192 | 0.859 | 0.997 | 63 |
|
| bent-tube | sweepable, thin-wall, genus | 1 | 18 | 4296 | 0.121 | 2726 | 4213.1 | 4159.4 | 1.3e-2 | 4159.8 | ok, 2148 vertices | 1 | 268 | 19.8 | 15550 | 2338 | 0 | pass | 4070 | sweep | 192 | 0.859 | 0.997 | 535 |
|
||||||
| bolted-assembly | assembly, blocky | 3 | 22 | 284 | 0.117 | 206 | 33004.7 | 32999.0 | 1.7e-4 | 32998.9 | ok, 144 vertices | 0, 1, 1 | 104 | 0.7 | 690 | 181 | 0 | pass | 34 | extrude | 357 | 0.398 | 0.222 | 40 |
|
| bolted-assembly | assembly, blocky | 3 | 22 | 284 | 0.117 | 1141 | 33004.7 | 32999.0 | 1.7e-4 | 32998.9 | ok, 144 vertices | 0, 1, 1 | 104 | 1.6 | 690 | 181 | 0 | pass | 211 | extrude | 357 | 0.398 | 0.222 | 139 |
|
||||||
| cross-cylinders | boolean, sharp | 1 | 10 | 1395 | 0.087 | 7920 | 32197.4 | 32357.8 | -5.0e-3 | 32365.8 | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | |
|
| cantilever-i-beam | sweepable, thin-wall | 1 | 14 | 44 | 0.208 | 483 | 61600.0 | 61600.0 | 0.0e0 | 61600.0 | ok, 24 vertices | 0 | 36 | 0.2 | 90 | 24 | 0 | pass | 5 | extrude | 336 | 0.707 | 0.707 | 47 |
|
||||||
| filleted-bracket | fillet, genus, sweepable | 1 | 13 | 540 | 0.088 | 388 | 32162.3 | 32145.0 | 5.4e-4 | 32144.8 | ok, 268 vertices | 2 | 135 | 1.4 | 5582 | 1613 | 0 | pass | 208 | no: Bracket: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 1: linking face 6 has 2 boundary loops (periodic or holed linking faces are not swept); from face 6: | | | | 5 |
|
| cross-cylinders | boolean, sharp | 1 | 10 | 1395 | 0.087 | 59348 | 32197.4 | 32357.8 | -5.0e-3 | 32365.8 | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | | error: Cross: invalid boundary surface: 28 defects; triangles 142 and 184 are oriented inconsistently across edge 0–396 at (-15.488656021315903, 4.577998041166734, 7.853092194999726); triangles 184 and 239 are oriented inconsistently across edge 0–554 at (-18.75000000000002, 5.634596901641276, 7.632255089815496); edge 103–108 at (-0.16007784063084018, 0.03984236936862052, 9.998639270596865) is shared by 3 triangles; … | | | | |
|
||||||
| i-beam | sweepable, thin-wall | 1 | 14 | 44 | 0.208 | 3 | 61600.0 | 61600.0 | 0.0e0 | 61600.0 | ok, 24 vertices | 0 | 36 | 0.1 | 94 | 24 | 0 | pass | 4 | extrude | 336 | 0.707 | 0.707 | 34 |
|
| filleted-bracket | fillet, genus, sweepable | 1 | 13 | 540 | 0.088 | 3706 | 32162.3 | 32145.0 | 5.4e-4 | 32144.8 | ok, 268 vertices | 2 | 135 | 2.1 | 5582 | 1613 | 0 | pass | 1769 | no: Bracket: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 1: linking face 6 has 2 boundary loops (periodic or holed linking faces are not swept); from face 6: | | | | 10 |
|
||||||
| loft-transition | organic | 1 | 6 | 292 | 0.075 | 57 | 56390.2 | 56450.6 | -1.1e-3 | – | ok, 148 vertices | 0 | 40 | 0.7 | 1329 | 256 | 1 | pass | 38 | sweep | 2873 | 0.189 | 0.189 | 328 |
|
| i-beam | sweepable, thin-wall | 1 | 14 | 44 | 0.208 | 280 | 61600.0 | 61600.0 | 0.0e0 | 61600.0 | ok, 24 vertices | 0 | 36 | 0.3 | 94 | 24 | 0 | pass | 7 | extrude | 336 | 0.707 | 0.707 | 52 |
|
||||||
| perforated-plate | genus, thin-wall, sweepable | 1 | 54 | 1644 | 0.144 | 273 | 26822.2 | 26764.6 | 2.2e-3 | 26764.2 | ok, 776 vertices | 24 | 828 | 4.8 | 33611 | 9798 | 0 | pass | 1345 | extrude | 467 | 0.304 | 0.033 | 98 |
|
| loft-transition | organic | 1 | 6 | 292 | 0.075 | 397 | 56390.2 | 56450.6 | -1.1e-3 | – | ok, 148 vertices | 0 | 40 | 1.0 | 1329 | 256 | 1 | pass | 172 | sweep | 2873 | 0.189 | 0.189 | 2056 |
|
||||||
| pinhole-block | small-feature, genus | 1 | 38 | 4044 | 0.073 | 201 | 49919.4 | 49909.6 | 2.0e-4 | 49909.5 | ok, 1992 vertices | 16 | 1548 | 12.9 | 96411 | 30221 | 0 | pass | 8142 | extrude | 5236 | 0.408 | 0.392 | 735 |
|
| perforated-plate | genus, thin-wall, sweepable | 1 | 54 | 1644 | 0.144 | 2864 | 26822.2 | 26764.6 | 2.2e-3 | 26764.2 | ok, 776 vertices | 24 | 828 | 10.6 | 33611 | 9798 | 0 | pass | 6513 | extrude | 467 | 0.304 | 0.033 | 480 |
|
||||||
| rounded-box | fillet | 1 | 26 | 2540 | 0.054 | 235 | 23325.7 | 23340.5 | -6.3e-4 | 23340.8 | ok, 1272 vertices | 0 | 216 | 8.4 | 11663 | 1916 | 0 | pass | 469 | no: Box: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 0: 12 faces are not on the sweep path; from face 5: 12 faces are not on the sweep path; revolve: plan | | | | 15 |
|
| pinhole-block | small-feature, genus | 1 | 38 | 4044 | 0.073 | 2053 | 49919.4 | 49909.6 | 2.0e-4 | 49909.5 | ok, 1992 vertices | 16 | 1548 | 41.8 | 96411 | 30221 | 0 | pass | 28180 | extrude | 5236 | 0.408 | 0.392 | 3838 |
|
||||||
| shallow-groove | near-tangent, boolean | 1 | 8 | 62 | 0.075 | 11 | 47795.2 | 47782.3 | 2.7e-4 | 47781.5 | ok, 33 vertices | 0 | 24 | 0.2 | 267 | 87 | 0 | pass | 13 | extrude | 1960 | 0.991 | 0.986 | 201 |
|
| rounded-box | fillet | 1 | 26 | 2540 | 0.054 | 1069 | 23325.7 | 23340.5 | -6.3e-4 | 23340.8 | ok, 1272 vertices | 0 | 216 | 11.5 | 11653 | 1914 | 0 | pass | 2265 | no: Box: not supported yet: no structured hex method fits this body (extrude: cylindrical face 8 is not parallel to the direction; sweep: from face 0: 12 faces are not on the sweep path; from face 5: 12 faces are not on the sweep path; revolve: plan | | | | 67 |
|
||||||
| sharp-wedge | sharp, thin-wall, sweepable | 1 | 5 | 8 | 0.083 | 2 | 3200.0 | 3200.0 | 0.0e0 | 3200.0 | ok, 6 vertices | 0 | 9 | 0.0 | 11 | 3 | 0 | pass | 0 | extrude | 294 | 0.050 | 0.050 | 30 |
|
| shallow-groove | near-tangent, boolean | 1 | 8 | 62 | 0.075 | 218 | 47795.2 | 47782.3 | 2.7e-4 | 47781.5 | ok, 33 vertices | 0 | 24 | 0.2 | 267 | 87 | 0 | pass | 24 | extrude | 1960 | 0.991 | 0.986 | 1078 |
|
||||||
| sphere-on-plate | near-tangent, boolean | 1 | 12 | 2826 | 0.098 | 974 | 69353.3 | 69509.0 | -2.2e-3 | 69509.7 | ok, 1415 vertices | 0 | 124 | 7.2 | 4898 | 309 | 9 | pass | 260 | no: Plate: not supported yet: no structured hex method fits this body (extrude: face 6 is curved across the direction; sweep: from face 0: 6 faces are not on the sweep path; from face 5: linking face 11 has 2 boundary loops (periodic or holed linking | | | | 11 |
|
| sharp-wedge | sharp, thin-wall, sweepable | 1 | 5 | 8 | 0.083 | 72 | 3200.0 | 3200.0 | 0.0e0 | 3200.0 | ok, 6 vertices | 0 | 9 | 0.0 | 11 | 3 | 0 | pass | 1 | extrude | 294 | 0.050 | 0.050 | 87 |
|
||||||
| spline-vase | organic | 1 | 11 | 3890 | 0.106 | 226 | 94891.2 | 94948.5 | -6.0e-4 | – | ok, 1947 vertices | 0 | 243 | 9.0 | 12461 | 1512 | 0 | pass | 456 | sweep | 4554 | 0.649 | 0.649 | 547 |
|
| sphere-on-plate | near-tangent, boolean | 1 | 12 | 2826 | 0.098 | 3775 | 69353.3 | 69509.0 | -2.2e-3 | 69509.7 | ok, 1415 vertices | 0 | 124 | 8.3 | 4898 | 309 | 9 | pass | 1265 | no: Plate: not supported yet: no structured hex method fits this body (extrude: face 6 is curved across the direction; sweep: from face 0: 6 faces are not on the sweep path; from face 5: linking face 11 has 2 boundary loops (periodic or holed linking | | | | 75 |
|
||||||
| stepped-block | blocky, sharp | 1 | 14 | 96 | 0.078 | 264 | 47000.0 | 47000.0 | 0.0e0 | 47000.0 | ok, 48 vertices | 1 | 60 | 0.4 | 223 | 41 | 0 | pass | 7 | extrude | 1416 | 0.999 | 0.999 | 142 |
|
| spline-vase | organic | 1 | 11 | 3890 | 0.106 | 1097 | 94891.2 | 94948.5 | -6.0e-4 | – | ok, 1947 vertices | 0 | 243 | 23.5 | 12461 | 1512 | 0 | pass | 2487 | sweep | 4554 | 0.649 | 0.649 | 3030 |
|
||||||
| thin-ribs | thin-wall, blocky | 1 | 26 | 92 | 0.119 | 1004 | 32000.0 | 32000.0 | -4.5e-16 | 32000.0 | ok, 48 vertices | 0 | 72 | 0.6 | 171 | 33 | 0 | pass | 8 | extrude | 777 | 0.844 | 0.844 | 100 |
|
| stepped-block | blocky, sharp | 1 | 14 | 96 | 0.078 | 438 | 47000.0 | 47000.0 | 0.0e0 | 47000.0 | ok, 48 vertices | 1 | 60 | 0.6 | 223 | 41 | 0 | pass | 11 | extrude | 1416 | 0.999 | 0.999 | 906 |
|
||||||
| thin-wall-enclosure | thin-wall, blocky | 1 | 11 | 36 | 0.108 | 340 | 15564.0 | 15564.0 | 0.0e0 | 15564.0 | ok, 20 vertices | 0 | 28 | 0.2 | 191 | 47 | 2 | pass | 11 | extrude | 894 | 0.297 | 0.297 | 112 |
|
| thin-ribs | thin-wall, blocky | 1 | 26 | 92 | 0.119 | 1085 | 32000.0 | 32000.0 | -4.5e-16 | 32000.0 | ok, 48 vertices | 0 | 72 | 0.7 | 171 | 33 | 0 | pass | 10 | extrude | 777 | 0.844 | 0.844 | 236 |
|
||||||
| analytic-sphere | analytic | 1 | 6 | 2784 | 0.069 | 719 | 33353.4 | 33507.9 | -4.6e-3 | 33510.3 | ok, 1394 vertices | 0 | 96 | 12.6 | 4211 | 58 | 0 | pass | 440 | no: Sphere: not supported yet: no structured hex method fits this body (extrude: no direction to try; sweep: no planar face to sweep from; revolve: no axis to try; mapped: meshing failed: meshing failed: mesh validity gate: 8 inverted or degenerate ele | | | | 31 |
|
| thin-wall-enclosure | thin-wall, blocky | 1 | 11 | 36 | 0.108 | 189 | 15564.0 | 15564.0 | 0.0e0 | 15564.0 | ok, 20 vertices | 0 | 28 | 0.2 | 191 | 47 | 2 | pass | 8 | extrude | 894 | 0.297 | 0.297 | 349 |
|
||||||
| analytic-torus | analytic, genus | 1 | 6 | 7230 | 0.109 | 944 | 37644.1 | 37893.7 | -6.6e-3 | 37899.3 | ok, 3615 vertices | 1 | 180 | 25.6 | 19790 | 466 | 0 | pass | 1412 | revolve | 1134 | 0.706 | 0.706 | 280 |
|
| analytic-sphere | analytic | 1 | 6 | 2784 | 0.069 | 1340 | 33353.4 | 33507.9 | -4.6e-3 | 33510.3 | ok, 1394 vertices | 0 | 96 | 8.9 | 4211 | 58 | 0 | pass | 1430 | no: Sphere: not supported yet: no structured hex method fits this body (extrude: no direction to try; sweep: no planar face to sweep from; revolve: no axis to try; mapped: meshing failed: meshing failed: mesh validity gate: 8 inverted or degenerate ele | | | | 159 |
|
||||||
| analytic-cylinder | analytic, sweepable | 1 | 4 | 1004 | 0.049 | 190 | 12533.1 | 12564.7 | -2.5e-3 | 12566.4 | ok, 504 vertices | 0 | 106 | 3.4 | 7437 | 1568 | 0 | pass | 452 | extrude | 3200 | 0.707 | 0.707 | 585 |
|
| analytic-torus | analytic, genus | 1 | 6 | 7230 | 0.109 | 1725 | 37644.1 | 37893.7 | -6.6e-3 | 37899.3 | ok, 3615 vertices | 1 | 180 | 31.7 | 19790 | 466 | 0 | pass | 4946 | revolve | 1134 | 0.706 | 0.706 | 1155 |
|
||||||
| analytic-pipe | analytic, genus, sweepable | 1 | 6 | 1448 | 0.104 | 336 | 94150.9 | 94238.0 | -9.3e-4 | 94247.8 | ok, 724 vertices | 1 | 220 | 9.0 | 6238 | 1340 | 0 | pass | 479 | extrude | 1288 | 0.998 | 0.998 | 290 |
|
| analytic-cylinder | analytic, sweepable | 1 | 4 | 1004 | 0.049 | 368 | 12533.1 | 12564.7 | -2.5e-3 | 12566.4 | ok, 504 vertices | 0 | 106 | 9.3 | 7437 | 1568 | 0 | pass | 1797 | extrude | 3200 | 0.707 | 0.707 | 2316 |
|
||||||
| analytic-plate-hole | analytic, genus, sweepable | 1 | 8 | 112 | 0.224 | 231 | 98449.1 | 98429.5 | 2.0e-4 | 98429.2 | ok, 56 vertices | 1 | 62 | 0.9 | 1868 | 543 | 0 | pass | 72 | extrude | 321 | 0.320 | 0.142 | 55 |
|
| analytic-pipe | analytic, genus, sweepable | 1 | 6 | 1448 | 0.104 | 506 | 94150.9 | 94238.0 | -9.3e-4 | 94247.8 | ok, 724 vertices | 1 | 220 | 9.5 | 6238 | 1340 | 0 | pass | 1734 | extrude | 1288 | 0.998 | 0.998 | 1059 |
|
||||||
|
| analytic-plate-hole | analytic, genus, sweepable | 1 | 8 | 112 | 0.224 | 524 | 98449.1 | 98429.5 | 2.0e-4 | 98429.2 | ok, 56 vertices | 1 | 62 | 0.6 | 1868 | 543 | 0 | pass | 78 | extrude | 321 | 0.320 | 0.142 | 248 |
|
||||||
|
|
||||||
## Categories
|
## Categories
|
||||||
|
|
||||||
| category | stresses | parts |
|
| category | stresses | parts |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| thin-wall | walls thin against the part's size | 6 |
|
| thin-wall | walls thin against the part's size | 7 |
|
||||||
| small-feature | features much smaller than the part | 1 |
|
| small-feature | features much smaller than the part | 1 |
|
||||||
| fillet | blends between faces | 2 |
|
| fillet | blends between faces | 2 |
|
||||||
| genus | through holes (genus > 0) | 7 |
|
| genus | through holes (genus > 0) | 7 |
|
||||||
| sharp | sharp edges and acute dihedral angles | 3 |
|
| sharp | sharp edges and acute dihedral angles | 3 |
|
||||||
| near-tangent | faces meeting at a shallow angle | 2 |
|
| near-tangent | faces meeting at a shallow angle | 2 |
|
||||||
| boolean | intersection curves from booleans | 3 |
|
| boolean | intersection curves from booleans | 3 |
|
||||||
| sweepable | extrusions and sweeps (one-to-one sweep meshing) | 8 |
|
| sweepable | extrusions and sweeps (one-to-one sweep meshing) | 9 |
|
||||||
| blocky | axis-aligned blocks (polycube-friendly) | 4 |
|
| blocky | axis-aligned blocks (polycube-friendly) | 4 |
|
||||||
| organic | free-form, lofted or revolved spline surfaces | 2 |
|
| organic | free-form, lofted or revolved spline surfaces | 2 |
|
||||||
| assembly | several bodies placed by component transforms | 1 |
|
| assembly | several bodies placed by component transforms | 1 |
|
||||||
@@ -53,6 +54,7 @@ clock on the machine that ran it; every other column is deterministic.
|
|||||||
|
|
||||||
- `bent-tube`: A 10 mm tube with a 1 mm wall, bent through R30: a thin-walled sweep along a line-arc-line path.
|
- `bent-tube`: A 10 mm tube with a 1 mm wall, bent through R30: a thin-walled sweep along a line-arc-line path.
|
||||||
- `bolted-assembly`: A base plate with a clamp block component placed twice, once turned 90 degrees: bodies placed by component transforms.
|
- `bolted-assembly`: A base plate with a clamp block component placed twice, once turned 90 degrees: bodies placed by component transforms.
|
||||||
|
- `cantilever-i-beam`: The 200 mm I-beam of `i-beam` lying along X with its web upright (Z up): a sweep along a horizontal axis, and the cantilever of the README demo.
|
||||||
- `cross-cylinders`: Two orthogonal R10 cylinders joined: equal radii, so the intersection curves cross at two singular points.
|
- `cross-cylinders`: Two orthogonal R10 cylinders joined: equal radii, so the intersection curves cross at two singular points.
|
||||||
- `filleted-bracket`: An 8 mm L-bracket with an R6 inside fillet and a hole through each leg: a blend beside holes.
|
- `filleted-bracket`: An 8 mm L-bracket with an R6 inside fillet and a hole through each leg: a blend beside holes.
|
||||||
- `i-beam`: A 200 mm I-beam with 3 mm flanges and a 2 mm web: a long sweepable prism with thin walls.
|
- `i-beam`: A 200 mm I-beam with 3 mm flanges and a 2 mm web: a long sweepable prism with thin walls.
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
The 200 mm I-beam of `i-beam` lying along X with its web upright (Z up): a sweep along a horizontal axis, and the cantilever of the README demo.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{"corpus": {"categories": ["sweepable", "thin-wall"], "volume_mm3": 61600},
|
||||||
|
"commands": [
|
||||||
|
{"command": "sketch.create", "params": {"plane": "YZ", "name": "Section"}},
|
||||||
|
{"command": "sketch.line", "params": {"points": [[-20, 0], [20, 0], [20, 3], [1, 3], [1, 37], [20, 37], [20, 40], [-20, 40], [-20, 37], [-1, 37], [-1, 3], [-20, 3]], "closed": true}},
|
||||||
|
{"command": "sketch.finish", "params": {}},
|
||||||
|
{"command": "solid.extrude", "params": {"distance": 200, "body_name": "Beam"}}
|
||||||
|
]}
|
||||||
@@ -17,20 +17,27 @@ It honours `CARGO_TARGET_DIR`.
|
|||||||
What it does:
|
What it does:
|
||||||
|
|
||||||
1. Builds `fieldcraft`, `fieldcraft-cli` and the testkit's `corpus` tool (release).
|
1. Builds `fieldcraft`, `fieldcraft-cli` and the testkit's `corpus` tool (release).
|
||||||
2. Writes the corpus I-beam (`tests/corpus/i-beam`) as a `.solidcraft` design to a temporary
|
2. Writes the corpus cantilever I-beam (`tests/corpus/cantilever-i-beam`: 40 x 40 mm, 3 mm
|
||||||
folder with `corpus --part i-beam --design`.
|
flanges, a 2 mm web upright, 200 mm long along x) as a `.solidcraft` design to a temporary
|
||||||
|
folder with `corpus --part cantilever-i-beam --design`.
|
||||||
3. Starts the app with `--control PORT` and `fieldcraft-cli mcp --connect 127.0.0.1:PORT`, and
|
3. Starts the app with `--control PORT` and `fieldcraft-cli mcp --connect 127.0.0.1:PORT`, and
|
||||||
speaks MCP (JSON-RPC over stdio) to it. Every step is an MCP tool call: `open` the design,
|
speaks MCP (JSON-RPC over stdio) to it. Every step is an MCP tool call: `open` the design,
|
||||||
`mesh` (`hex_sweep`, 4 mm), `batch` (steel, a solid section, the root clamp, a kinematic
|
`mesh` (`hex_sweep`, 4 mm), `batch` (steel, a solid section, the root clamp, a kinematic
|
||||||
coupling of the free end to a reference point loaded with 1000 N), `check_model`, `solve`
|
coupling of the free end at x = 200 mm to a reference point loaded with 1000 N down, −z), `check_model`, `solve`
|
||||||
(`static`), and `show_results` for the von Mises contours and the deformed shape, its `scale`
|
(`static`), and `show_results` for the von Mises contours and the deformed shape, its `scale`
|
||||||
stepped from 1 to 150 and back. Every picture is the MCP `screenshot` tool with
|
stepped from 1 to 150 and back. Every picture is the MCP `screenshot` tool with
|
||||||
`source: window` (the whole window drawn offscreen, `ui.capture`), turned to the `iso` or
|
`source: window` (the whole window drawn offscreen, `ui.capture`), turned to the `iso` or
|
||||||
`right` view by its `view` argument.
|
`front` view by its `view` argument.
|
||||||
4. Encodes the pictures at 10 frames per second, each held for its stage, into the MP4 (H.264,
|
4. Encodes the pictures at 10 frames per second, each held for its stage, into the MP4 (H.264,
|
||||||
1280 px) and the GIF (960 px, its own palette), and prints the tip deflection and the peak
|
1280 px) and the GIF (960 px, its own palette), and prints the tip deflection and the peak
|
||||||
von Mises stress.
|
von Mises stress.
|
||||||
|
|
||||||
The theme is the app's default (dark). Beam theory for the same beam gives a tip deflection of
|
The theme is the app's default (dark).
|
||||||
0.143 mm in bending plus about 0.034 mm in shear (0.177 mm) and a root bending stress of 45 MPa;
|
|
||||||
the clamped root adds a local peak above that.
|
The script checks the solution against beam theory and stops when it is off. Tip deflection:
|
||||||
|
P L³ / 3 E I = 0.1429 mm in bending plus P L / G A_web = 0.0335 mm in shear, 0.1763 mm; the
|
||||||
|
solution gives 0.1764 mm (within 3 %). Root bending stress: P L c / I = 45.0 MPa
|
||||||
|
(I = 88 871 mm⁴); the solution's stress_xx on the top flange, fitted along the beam between one
|
||||||
|
depth from either end and extrapolated to the root, gives 44.8 MPa (within 2 %). At the root
|
||||||
|
itself the clamp, which stops the section's Poisson contraction, adds a local peak (62 MPa
|
||||||
|
stress_xx, 56 MPa von Mises) that beam theory does not have.
|
||||||
+74
-10
@@ -22,13 +22,20 @@ from pathlib import Path
|
|||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
|
|
||||||
# The analysis: the corpus I-beam (tests/corpus/i-beam, 40 x 40 mm, 3 mm flanges, 2 mm web,
|
# The analysis: a cantilever I-beam lying along x (tests/corpus/cantilever-i-beam: 40 x 40 mm,
|
||||||
# 200 mm long along z) clamped at z = 0 with 1000 N down (-y) on its free end.
|
# 3 mm flanges, 2 mm web upright, 200 mm long), clamped at x = 0 with 1000 N down (-z) on its free
|
||||||
PART = "i-beam"
|
# end, so it bends downward on screen (z is up).
|
||||||
|
PART = "cantilever-i-beam"
|
||||||
BODY = "Beam"
|
BODY = "Beam"
|
||||||
ROOT_FACE = "F2:start"
|
ROOT_FACE = "F2:start"
|
||||||
TIP_FACE = "F2:top"
|
TIP_FACE = "F2:top"
|
||||||
LOAD_N = 1000.0
|
LOAD_N = 1000.0
|
||||||
|
LENGTH_MM = 200.0
|
||||||
|
DEPTH_MM = 40.0 # overall; the flanges are 40 wide and 3 thick, the web 2 thick
|
||||||
|
FLANGE_MM = 3.0
|
||||||
|
WEB_MM = 2.0
|
||||||
|
YOUNG_MPA = 210000.0
|
||||||
|
POISSON = 0.3
|
||||||
SIZE_MM = 4.0
|
SIZE_MM = 4.0
|
||||||
STEP = "Load"
|
STEP = "Load"
|
||||||
|
|
||||||
@@ -159,13 +166,69 @@ class Film:
|
|||||||
return str(folder / "%05d.png"), n
|
return str(folder / "%05d.png"), n
|
||||||
|
|
||||||
|
|
||||||
|
def hand_calculation():
|
||||||
|
"""Beam theory for the cantilever: tip deflection in bending (P L^3 / 3 E I) plus shear
|
||||||
|
(P L / G A_web, the web carrying the shear) and the bending stress at the root (P L c / I)."""
|
||||||
|
b, h, tf, tw = 40.0, DEPTH_MM, FLANGE_MM, WEB_MM
|
||||||
|
inertia = (b * h**3 - (b - tw) * (h - 2 * tf) ** 3) / 12.0
|
||||||
|
shear_modulus = YOUNG_MPA / (2.0 * (1.0 + POISSON))
|
||||||
|
bending = LOAD_N * LENGTH_MM**3 / (3.0 * YOUNG_MPA * inertia)
|
||||||
|
shear = LOAD_N * LENGTH_MM / (shear_modulus * (h - tf) * tw)
|
||||||
|
return {"bending_mm": bending, "shear_mm": shear, "tip_mm": bending + shear, "root_stress_mpa": LOAD_N * LENGTH_MM * (h / 2) / inertia}
|
||||||
|
|
||||||
|
|
||||||
|
def root_bending_stress(mcp, work):
|
||||||
|
"""The bending stress at the root from the solution: stress_xx along the top flange's upper
|
||||||
|
face, averaged across it at each station, fitted by a line between one beam depth from either
|
||||||
|
end (where beam theory holds) and extrapolated to x = 0. At the root itself the clamp, which
|
||||||
|
stops the section's Poisson contraction, adds a local peak beam theory does not have.
|
||||||
|
|
||||||
|
The node coordinates come from the Abaqus deck (`write_deck`); the stresses from `probe`."""
|
||||||
|
deck = work / "ibeam.inp"
|
||||||
|
mcp.call("write_deck", {"format": "abaqus", "path": str(deck)})
|
||||||
|
stations = {}
|
||||||
|
in_nodes = False
|
||||||
|
for line in deck.read_text().splitlines():
|
||||||
|
if line.startswith("*"):
|
||||||
|
in_nodes = line.upper().split(",")[0].strip() == "*NODE"
|
||||||
|
continue
|
||||||
|
fields = [f.strip() for f in line.split(",")]
|
||||||
|
if in_nodes and len(fields) >= 4:
|
||||||
|
label, x, z = int(fields[0]), float(fields[1]), float(fields[3])
|
||||||
|
if abs(z - DEPTH_MM) < 1e-6 and DEPTH_MM <= x <= LENGTH_MM - DEPTH_MM:
|
||||||
|
stations.setdefault(x, []).append(label)
|
||||||
|
if len(stations) < 2:
|
||||||
|
raise SystemExit(f"root stress: {len(stations)} stations on the top flange in {deck}")
|
||||||
|
probed = mcp.call("probe", {"step": STEP, "nodes": [n for nodes in stations.values() for n in nodes]})
|
||||||
|
sxx = {r["node"]: r["stress"][0] for r in probed["nodes"]}
|
||||||
|
xs = sorted(stations)
|
||||||
|
ys = [sum(sxx[n] for n in stations[x]) / len(stations[x]) for x in xs]
|
||||||
|
mx, my = sum(xs) / len(xs), sum(ys) / len(ys)
|
||||||
|
slope = sum((x - mx) * (y - my) for x, y in zip(xs, ys)) / sum((x - mx) ** 2 for x in xs)
|
||||||
|
return my - slope * mx
|
||||||
|
|
||||||
|
|
||||||
|
def against_beam_theory(result):
|
||||||
|
"""The solution against beam theory: tip deflection within 3 %, root stress within 2 %."""
|
||||||
|
hand = hand_calculation()
|
||||||
|
result["hand_calculation"] = hand
|
||||||
|
for got, want, tol, what in (
|
||||||
|
(result["tip_deflection_mm"], hand["tip_mm"], 0.03, "tip deflection (mm)"),
|
||||||
|
(result["root_stress_mpa"], hand["root_stress_mpa"], 0.02, "root bending stress (MPa)"),
|
||||||
|
):
|
||||||
|
log(f"{what}: {got:.4f}, beam theory {want:.4f} ({(got - want) / want:+.1%})")
|
||||||
|
if abs(got - want) > tol * want:
|
||||||
|
raise SystemExit(f"{what} {got:.4f} is more than {tol:.0%} from beam theory's {want:.4f}")
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def batch(mcp, commands):
|
def batch(mcp, commands):
|
||||||
reply = mcp.call("batch", {"commands": commands})
|
reply = mcp.call("batch", {"commands": commands})
|
||||||
if not reply.get("ok"):
|
if not reply.get("ok"):
|
||||||
raise SystemExit(f"batch failed: {reply.get('failed')}")
|
raise SystemExit(f"batch failed: {reply.get('failed')}")
|
||||||
|
|
||||||
|
|
||||||
def analyse(mcp, film, design):
|
def analyse(mcp, film, design, work):
|
||||||
"""The analysis, every step an MCP tool call, with the pictures a viewer follows."""
|
"""The analysis, every step an MCP tool call, with the pictures a viewer follows."""
|
||||||
# 1. Open the part.
|
# 1. Open the part.
|
||||||
mcp.call("open", {"path": str(design)})
|
mcp.call("open", {"path": str(design)})
|
||||||
@@ -181,7 +244,7 @@ def analyse(mcp, film, design):
|
|||||||
batch(mcp, [
|
batch(mcp, [
|
||||||
{"command": "model.units", "params": {"system": "mm-t-s-N-MPa"}},
|
{"command": "model.units", "params": {"system": "mm-t-s-N-MPa"}},
|
||||||
{"command": "model.set.create", "params": {"name": "beam", "geometry": [{"body": BODY}]}},
|
{"command": "model.set.create", "params": {"name": "beam", "geometry": [{"body": BODY}]}},
|
||||||
{"command": "model.material.create", "params": {"name": "Steel", "elastic": {"type": "isotropic", "young": 210000, "poisson": 0.3}, "density": 7.85e-9}},
|
{"command": "model.material.create", "params": {"name": "Steel", "elastic": {"type": "isotropic", "young": YOUNG_MPA, "poisson": POISSON}, "density": 7.85e-9}},
|
||||||
{"command": "model.section.create", "params": {"name": "solid", "set": "beam", "material": "Steel", "kind": {"type": "solid"}, "formulation": {"incompatible_modes": True}}},
|
{"command": "model.section.create", "params": {"name": "solid", "set": "beam", "material": "Steel", "kind": {"type": "solid"}, "formulation": {"incompatible_modes": True}}},
|
||||||
])
|
])
|
||||||
film.shoot(HOLD_SHORT)
|
film.shoot(HOLD_SHORT)
|
||||||
@@ -194,9 +257,9 @@ def analyse(mcp, film, design):
|
|||||||
# The end section stays plane: a kinematic coupling to a point on the beam's axis carries
|
# The end section stays plane: a kinematic coupling to a point on the beam's axis carries
|
||||||
# the load, so no load-introduction peak hides the root stress.
|
# the load, so no load-introduction peak hides the root stress.
|
||||||
{"command": "model.set.create", "params": {"name": "tip", "geometry": [{"body": BODY, "face": TIP_FACE}]}},
|
{"command": "model.set.create", "params": {"name": "tip", "geometry": [{"body": BODY, "face": TIP_FACE}]}},
|
||||||
{"command": "model.reference_point.create", "params": {"name": "end", "xyz": [0, 20, 200]}},
|
{"command": "model.reference_point.create", "params": {"name": "end", "xyz": [LENGTH_MM, 0, DEPTH_MM / 2]}},
|
||||||
{"command": "model.coupling.create", "params": {"name": "end_plate", "reference_point": "end", "set": "tip", "kind": "kinematic"}},
|
{"command": "model.coupling.create", "params": {"name": "end_plate", "reference_point": "end", "set": "tip", "kind": "kinematic"}},
|
||||||
{"command": "model.load.create", "params": {"name": "tip_load", "type": "force", "target": {"reference_point": "end"}, "vector": [0, -LOAD_N, 0]}},
|
{"command": "model.load.create", "params": {"name": "tip_load", "type": "force", "target": {"reference_point": "end"}, "vector": [0, 0, -LOAD_N]}},
|
||||||
{"command": "model.step.create", "params": {"name": STEP, "procedure": {"type": "static"}, "boundary_conditions": ["clamp"], "loads": ["tip_load"]}},
|
{"command": "model.step.create", "params": {"name": STEP, "procedure": {"type": "static"}, "boundary_conditions": ["clamp"], "loads": ["tip_load"]}},
|
||||||
])
|
])
|
||||||
check = mcp.call("check_model")
|
check = mcp.call("check_model")
|
||||||
@@ -211,6 +274,7 @@ def analyse(mcp, film, design):
|
|||||||
# 5. Results: the tip deflection, then von Mises contours on the true (scale 1) shape.
|
# 5. Results: the tip deflection, then von Mises contours on the true (scale 1) shape.
|
||||||
disp = mcp.call("show_results", {"step": STEP, "field": "displacement"})
|
disp = mcp.call("show_results", {"step": STEP, "field": "displacement"})
|
||||||
tip = disp["contour"]["range"]["max"]["value"]
|
tip = disp["contour"]["range"]["max"]["value"]
|
||||||
|
root_stress = root_bending_stress(mcp, work)
|
||||||
vm = mcp.call("show_results", {"step": STEP, "field": "von_mises", "bands": 12, "deformed": True, "scale": 1})
|
vm = mcp.call("show_results", {"step": STEP, "field": "von_mises", "bands": 12, "deformed": True, "scale": 1})
|
||||||
peak = vm["contour"]["range"]["max"]["value"]
|
peak = vm["contour"]["range"]["max"]["value"]
|
||||||
log(f"tip deflection {tip:.4f} mm, peak von Mises {peak:.1f} MPa")
|
log(f"tip deflection {tip:.4f} mm, peak von Mises {peak:.1f} MPa")
|
||||||
@@ -219,12 +283,12 @@ def analyse(mcp, film, design):
|
|||||||
# 6. The deformed shape, swung from the true shape to an exaggeration and back, in the iso
|
# 6. The deformed shape, swung from the true shape to an exaggeration and back, in the iso
|
||||||
# view and then from the side, ending held at the exaggeration.
|
# view and then from the side, ending held at the exaggeration.
|
||||||
ramp = [max(1.0, MAX_SCALE * i / SWING) for i in range(SWING + 1)]
|
ramp = [max(1.0, MAX_SCALE * i / SWING) for i in range(SWING + 1)]
|
||||||
for view, scales in (("iso", ramp[1:] + ramp[-2::-1]), ("right", ramp[1:])):
|
for view, scales in (("iso", ramp[1:] + ramp[-2::-1]), ("front", ramp[1:])):
|
||||||
for i, scale in enumerate(scales):
|
for i, scale in enumerate(scales):
|
||||||
mcp.call("show_results", {"step": STEP, "deformed": True, "scale": scale})
|
mcp.call("show_results", {"step": STEP, "deformed": True, "scale": scale})
|
||||||
film.shoot(1, view=view if i == 0 else None)
|
film.shoot(1, view=view if i == 0 else None)
|
||||||
film.shoot(HOLD_LONG)
|
film.shoot(HOLD_LONG)
|
||||||
return {"tip_deflection_mm": tip, "peak_von_mises_mpa": peak, "elements": mesh["mesh"]["elements"], "nodes": mesh["mesh"]["nodes"]}
|
return against_beam_theory({"tip_deflection_mm": tip, "root_stress_mpa": root_stress, "peak_von_mises_mpa": peak, "elements": mesh["mesh"]["elements"], "nodes": mesh["mesh"]["nodes"]})
|
||||||
|
|
||||||
|
|
||||||
def encode(film, frames_dir, mp4, gif):
|
def encode(film, frames_dir, mp4, gif):
|
||||||
@@ -272,7 +336,7 @@ def main():
|
|||||||
wait_for_port(port, app)
|
wait_for_port(port, app)
|
||||||
mcp = Mcp(bin_dir / "fieldcraft-cli", port)
|
mcp = Mcp(bin_dir / "fieldcraft-cli", port)
|
||||||
film = Film(mcp, frames_dir)
|
film = Film(mcp, frames_dir)
|
||||||
result = analyse(mcp, film, design)
|
result = analyse(mcp, film, design, work)
|
||||||
encode(film, frames_dir, args.out / "ibeam-analysis.mp4", args.out / "ibeam-analysis.gif")
|
encode(film, frames_dir, args.out / "ibeam-analysis.mp4", args.out / "ibeam-analysis.gif")
|
||||||
print(json.dumps(result, indent=2))
|
print(json.dumps(result, indent=2))
|
||||||
finally:
|
finally:
|
||||||
|
|||||||
Reference in new issue
Block a user