RasmahRasmah

Design Loop: Sizing a Part and Printing It

using Rasmah
using LinearAlgebra

Overview

A real design is not one analysis — it is a loop: build the part, check it against the requirements, change it, and check again until it passes, then hand the result to manufacturing. This tutorial walks the full loop end to end:

  1. size a steel strap by running the finite-element check at several thicknesses until the factor of safety meets the requirement;
  2. verify the chosen design and its mass;
  3. slice it and emit the G-code a 3D printer executes.

The part is the same tension strap as the linear elasticity tutorial: 100 mm long, 30 mm wide, pulled by a 30 kN service load at its ends, made of A36 structural steel. The requirement: a factor of safety of at least 1.5 against yield, meaning the working stress must stay below 250 / 1.5 ≈ 167 MPa. The question: how thick does the strap need to be — and no thicker, because every extra millimetre is material, mass, and cost?

set_unitsystem!(MM_TONNE_S)
UnitSystem(mm, tonne, s, A, K, mol, cd, deg, N, MPa, MPa, mJ, W, mm/s, tonne/mm^3, tonne/(mm·s), Hz)

The sizing loop

For a strap in pure tension the working stress is σ = F/(W·t), so the required thickness is t = F/(W·σ_allow) = 30000/(30·167) = 6 mm. But that hand formula assumes uniform stress; the point of the loop is to prove it with the finite-element model — the same check you would run on a part without a closed-form answer. We solve the full model at t = 3, 4, 5, 6 mm and report the mid-span von Mises stress and the resulting factor of safety:

# 1. the design space — a strap of variable thickness, 30 kN at the ends
for t in (3.0, 4.0, 5.0, 6.0)

    # 2. geometry + boundary sets (the workflow order never changes)
    part = box(100u"mm", 30u"mm", t * u"mm")
    b    = brep(part)
    left  = faces_in_box(b, [-55u"mm", -20u"mm", -10u"mm"], [-45u"mm", 20u"mm", 10u"mm"])
    right = faces_in_box(b, [ 45u"mm", -20u"mm", -10u"mm"], [ 55u"mm", 20u"mm", 10u"mm"])

    # 3. mesh, 4. boundary conditions (30 kN spread over the 30 × t mm² face), 5. solve
    mesh = tetrahedralize(b, 10)
    u = solve(Elasticity(), mesh, steel;
              fixed = left => [0.0, 0.0, 0.0],
              loads = right => [(30000.0 / (30.0 * t))u"MPa", 0.0u"MPa", 0.0u"MPa"])

    # post-process: mid-span von Mises stress and the factor of safety
    σv  = von_mises_stress(mesh, ustrip(u), stiffness(steel))
    mid = [sum(mesh.nodes[1, mesh.elements[:, e]]) / 4 for e in axes(mesh.elements, 2)]
    σmid = maximum(σv[i] for i in eachindex(mid) if 0.04 < mid[i] < 0.06)
    fos  = yield_strength(steel) / σmid
    mass = density(steel) * ustrip(volume(part)) * 1e3          # grams

    println("t = ", t, " mm:  σ = ", round(σmid / 1e6; digits = 1), " MPa,  FoS = ",
            round(fos; digits = 2), ",  mass = ", round(mass; digits = 1), " g")
end
t = 3.0 mm:  σ = 333.3 MPa,  FoS = 0.75,  mass = 70.6 g
t = 4.0 mm:  σ = 250.0 MPa,  FoS = 1.0,  mass = 94.2 g
t = 5.0 mm:  σ = 200.0 MPa,  FoS = 1.25,  mass = 117.8 g
t = 6.0 mm:  σ = 166.7 MPa,  FoS = 1.5,  mass = 141.3 g

The table tells the story:

  • t = 3, 4 mm fail: factors of safety 0.75 and 1.0 are at or below the yield point — the strap would plastically deform in service.
  • t = 5 mm yields, but the safety factor 1.25 is below the required 1.5.
  • t = 6 mm gives FoS = 1.5 — exactly the requirement.

So the lightest strap that passes is 6 mm thick, at 141 g. The finite element model confirms the hand formula element-by-element; the loop is what automates the search over the design.

Verify the chosen design

Lock in the winner and look at the full stress picture — the mid-span field is uniform at the design stress, with the familiar corner concentrations at the clamped face:

part  = box(100u"mm", 30u"mm", 6u"mm")
b     = brep(part)
left  = faces_in_box(b, [-55u"mm", -20u"mm", -10u"mm"], [-45u"mm", 20u"mm", 10u"mm"])
right = faces_in_box(b, [ 45u"mm", -20u"mm", -10u"mm"], [ 55u"mm", 20u"mm", 10u"mm"])
mesh  = tetrahedralize(b, 10)
u     = solve(Elasticity(), mesh, steel;
              fixed = left => [0.0, 0.0, 0.0],
              loads = right => [(30000.0 / (30.0 * 6.0))u"MPa", 0.0u"MPa", 0.0u"MPa"])
σv    = von_mises_stress(mesh, ustrip(u), stiffness(steel))

println("max stretch = ", round(uconvert(u"mm", maximum(abs, u)); digits = 4), " mm")
println("mass        = ", round(density(steel) * ustrip(volume(part)) * 1e3; digits = 1), " g")
max stretch = 0.0835 mm
mass        = 141.3 g
vtk(mesh; field = σv, fieldname = "von Mises stress", colormap = :inferno, scalarbar = true)

From analysis to G-code

The design now goes to manufacturing. Rasmah’s additive pipeline takes the part from mesh to machine instructions: tessellate the surface, slice it into layers, and turn the layers into extruder moves:

# tessellate the strap to a triangle mesh, then slice it for 0.2 mm layers
sm     = surface_mesh(part; n = 20)
layers = slice(sm; layer_height = ustrip(0.2u"mm"))

println("triangles:  ", size(sm.faces, 2))
println("layers:     ", length(layers))
triangles:  4328
layers:     31
vtk(sm)

A print plan records the slicing plus the extrusion settings — perimeters, infill density, line width, filament diameter:

pp = print_plan(sm; layer_height = ustrip(0.2u"mm"))
println("perimeters: ", pp.perimeters, "   infill: ", pp.infill_density * 100, "%")
perimeters: 3   infill: 20.0%

print_gcode turns the plan into the Marlin-flavoured G-code a typical FDM printer executes — heating, per-layer perimeter loops, and infill with extrusion:

gcode = print_gcode(pp)
lines = split(gcode, '\n')
println(length(lines), " lines of G-code, starting:")
for l in lines[1:4]
    println("  ", l)
end
14731 lines of G-code, starting:
  ; Rasmah print plan
  M104 S210 ; set extruder temp
  M140 S60 ; set bed temp
  M190 S60 ; wait bed

That is the whole loop, closed: a load requirement became a thickness, the thickness passed a factor-of-safety check, and the same model became printer code. Every step ran on the same geometry — change the load, re-run the loop, and the design, the check, and the G-code all update together.

Next steps