# Non-Wood Materials

*Ring stock, shrink-fit pre-stress, and external, bought-in parts*

**Names:** `stock` `ring` `constraint` `to` `substance` `tempered`

`status: partial`. Built: `stock shape=ring`, `constraint shrink`, non-timber sheet goods
(`type=sheet substance=acrylic|solid_surface|glass|laminate`, `tempered=#true`), the
material-pair joint allow-list, and the SUPPLY section of the cut list (ADR-0033, ADR-0076).
**`external_part` (RFC-0121 Level 3) is rejected permanently** — do not write it; it does not
parse. For a bought assembly use a `hardware` catalogue entry and `hardware_mount`, or a proxy
`component` (ADR-0017, ADR-0076, `docs/KDL_AUTHORING.md` §5). Stress, force and pull-out are
not modelled; the shrink report is *geometric only, not a structural check*.

## When to use

- A part that is a **rolled or forged band**, not a sawn or turned one: a wagon wheel's iron
  tyre, a barrel hoop, a steel collar on a post. Material is `type=metal`.
- A band **fitted by shrinking**: made a little too small, heated, dropped on, left to cool.
  `constraint shrink` states the fit and the compiler checks it, so the deliberate overlap is
  not reported as a clash.
- Any part that is **bought or made to order** rather than cut from timber: a `type=metal`
  part is listed under SUPPLY in the cut list and left out of timber volumes, boards and
  blank sizes.

## Syntax

```kdl grammar
material Steel type=metal thermal_expansion=0.000012      // per kelvin

stock <metal> shape=ring diameter=<outer> section_w=<axial width> section_h=<radial thickness>

constraint shrink from=<ring part> to=<part> radial_interference=<mm> temperature=<degC> [ambient=<degC>]
```

- `diameter` is the **outer** diameter; the bore is `diameter / 2 - section_h`. The ring sits
  in its circumscribing square with its top face at `z = 0` (D4), like every other part.
- `shape=ring` takes the material and **no other positional argument**: no `x y z`, no grain
  word. It needs all three of `diameter=`, `section_w=`, `section_h=`. Only a `type=metal`
  material may be a ring.
- `constraint` is an assembly-scope node, may sit inside a `repeat`, and `to=` takes the same
  `{i}` interpolation as `part id=`. `ambient=` defaults to 20 degC. The material of `from`
  must declare `thermal_expansion=`.
- What it checks: (1) the built overlap is the declared `radial_interference` (within
  0.1 mm); (2) at `temperature=` the bore `r (1 + alpha (T - T0))` clears `to`; it reports the
  lowest temperature that does, and the hoop strain `interference / r`.

## Example

```kdl
project name="Banded Plug" units=mm schema_version=1 {
    params {
        plug_diameter  300.0 min=200.0 max=500.0 label="Plug Diameter"
        plug_len        20.0 min=10.0 max=60.0  label="Plug Length"
        band_th          6.0 min=3.0  max=12.0  label="Band Thickness"
        interference     0.5 min=0.3  max=1.5   label="Band Interference (radial)"
        heat_c         400.0 min=150.0 max=600.0 label="Band Heat (degC)"
        // Outer diameter of the band: the plug's diameter plus the band's wall, less what the
        // band is made too small by.
        band_diameter  "plug_diameter + 2.0 * (band_th - interference)"
        band_depth     "band_th"
        plug_r         "plug_diameter / 2.0"
    }

    materials {
        material Oak   type=hardwood
        // `thermal_expansion` is per kelvin; the shrink fit's heating arithmetic needs it.
        material Steel type=metal thermal_expansion=0.000012
    }

    component Plug role=plug {
        stock Oak shape=round diameter=plug_diameter z=plug_len
        // Round stock is only round once it is turned: a plain cylinder is two stations.
        section axis=z interp=linear {
            at z=0.0 { circle r=plug_r }
            at z="0.0 - plug_len" { circle r=plug_r }
        }
    }

    // A rolled band: outer diameter, axial width, radial thickness. No grain word.
    component Band role=band {
        stock Steel shape=ring diameter=band_diameter section_w=plug_len section_h=band_depth
    }

    assembly Banded {
        part Plug instance=Plug {
            by top_center
            pos x=0.0 y=0.0 z=0.0
        }
        part Band instance=Band {
            by top_center
            pos x=0.0 y=0.0 z=0.0
        }
        constraint shrink from=Band to=Plug radial_interference=interference temperature=heat_c
    }
}
```

`ww clash` on it reports 0 clashes, the overlap as design interference, and the fit: 0.500 mm
interference (bore r = 149.500, fit r = 150.000); at 400 degC the bore clears by 0.182 mm; it
fits from 299 degC; the shrink asks 0.00334 of strain and heating to 400 degC supplies 0.00456 (labelled *geometric only, not a structural check*). A worked example with six felloes and six spokes is
`examples/wagon_wheel.kdl`.

## Pitfalls and diagnostics

- **Round stock without a `section` is a square block.** The plug above needs its two-station
  `section` to be a cylinder; without it the ring "reaches" the block's corners and the fit
  fails with an interference of several millimetres.
- **`declared radial_interference=… but the built solids interfere by …`**: the ring's `stock`
  and the constraint disagree. The ring's bore is `diameter / 2 - section_h`; make the
  constraint say what the geometry says, or the geometry what the constraint says.
- **`… does not clear … the ring cannot be fitted; heat it to at least N degC`**: a steel ring
  grows about 1.2e-5 per kelvin, so 1 mm on a 49 mm bore needs about 1,700 degC. Interference is
  small relative to the radius for a reason: raise the radius, lower the interference or raise
  `temperature=`.
- **`… declares no thermal_expansion=`**: add `thermal_expansion=0.000012` (steel) to the
  `material`.
- **`` `X` is a wood joint, but the male part `Y` is Steel ``**: wood joinery (`mortise_and_tenon`,
  `dowel`, `lap`, …) is refused on a non-wood part. Only `butt` and `hardware_mount` may touch
  one; hold a band with `constraint shrink`.
- **A ring overlapping a part it has no constraint to** stays an ordinary clash. So does one
  whose constraint failed.
- The tolerance is 0.1 mm. A flat tang end 0.083 mm proud of a curved tread passes, a 1 mm
  discrepancy does not. A constraint that only passes within the tolerance is a modelling
  artefact worth a comment, as in the wagon wheel.
- **Not modelled, and not claimed**: hoop stress, clamping force on the joints, wood crushing,
  yield. A `constraint shrink` is geometry and linear thermal growth only.
- `kdl-check` rejects a grain word on a `type=metal` material, an unknown `constraint` kind
  and a `shape=ring` stock missing a dimension.

## See also

- `docs/adr/0033-non-wood-materials-levels-2-3.md` (decisions, the measured wagon wheel)
- `docs/rfc/0121-non-wood-materials-and-hardware.md` (Level 3, `external_part`, not built)
- `docs/adr/0017-hardware-and-materials.md` (Level 1: `type=metal`, `hardware`)
- `docs/KDL_AUTHORING.md` §5 `materials`
- `examples/wagon_wheel.kdl`, `examples/filing_cabinet_vertical.kdl` (metal parts, supplied)

## Names

Every name this page declares, how the example library uses it, and the shortest real use.

### `stock`

feature; node in 114 of 116 examples (910 uses).

Shortest real use, from `glazed_vitrine_whiplash_crest.kdl`:

```kdl
stock Glass dw 6.0 dh
```

### `ring`

stock shape; value in 1 of 116 examples (1 uses).

Shortest real use, from `wagon_wheel.kdl`:

```kdl
stock Steel shape=ring diameter=wheel_diameter section_w=rim_width section_h=tyre_band
```

### `constraint`

node in 1 of 116 examples (2 uses).

Shortest real use, from `wagon_wheel.kdl`:

```kdl
constraint shrink from=Tyre to="Spoke_{k}" radial_interference=tyre_interference temperature=tyre_heat_c
```

### `to`

word-valued property; property in 2 of 116 examples (3 uses).

Shortest real use, from `blanket_chest.kdl`:

```kdl
distribute from="0.0 - (hinge_zone / 2.0)" to="hinge_zone / 2.0"
```

### `substance`

property; property in 1 of 116 examples (1 uses).

Shortest real use, from `acrylic_panel_frame.kdl`:

```kdl
material Acrylic type=sheet substance=acrylic nominal_th=6.0 finished_th=6.0 color="#d8ecf2"
```

### `tempered`

not used by any example yet.

