Model Metrics#
Volume, mass, area, enclosed volumes, and a bill of materials
Names: volume no_join a b reason
When to use#
Use ww metrics <file> to ask what a design weighs, how much surface a finish must cover, what
its box holds, and what to buy. The numbers are read from the built solids, so they follow every
parameter. Add a volume node when you need an enclosed volume: the litres inside a speaker
cabinet, a chest, a drawer, a sealed compartment. Metrics are a report: they never change the
model.
Syntax#
volume Interior {
bounded_by End_L End_R Front Back Bottom Lid
subtract Shelf
}
volume <Name>is a top-level node of the project.Namekeys the result inmetrics.json.bounded_bylists the parts whose union closes the region (part ids, or any unique trailing run of one:Lid,Chest/Lid). At least one.subtractlists parts standing in the air that displace it (a shelf, a brace, a driver).- Nothing else goes inside
volume; it takes no properties.
CLI: ww metrics <file> [--json] [--set name=value] [--waste F] [--coats N] [--end-grain-max X].
The fabrication pack carries the same report as metrics.json.
Contact completeness (issue #61 Stage 1). ww metrics finds every real planar contact
between built parts (exact geometry, not bounding boxes) and warns
(ww::analysis::unaccounted_contact) on any contact with no covering join and no covering
no_join:
no_join a=Shelf b=Case reason="rests on cleats, lifts out, no joint by design"
no_joinis a top-level node of the project, likevolume.a=andb=name the two touching parts the same wayvolume'sbounded_bydoes: an exact id, or any unique trailing run of one. Both are required.reason=is required and must not be empty — the escape hatch names why a real contact has no joint, it does not silence by merely existing.- This decides no joint type and changes no solid (D8 stands): it only asks whether the author said something about a touching pair.
Finish quantity. Give a material or part a finish="'danish oil'"; ww metrics matches the
text to docs/finishes and, where that entry has a sourced coverage, reports litres as a range
(finish.groups.<text>.litres). Entries with no sourced figure (shellac, paints, milk paint)
report no quantity and say why.
Bill of materials. bom lists stock to buy per material and thickness (nominal, and with a
waste factor: --waste F, default 0.15; board feet for timber), purchased items split into
fastener (hardware with a length), insert (generated dowels, biscuits, dominos) and
hardware, and consumables: glue (the area of the glue lines) and finish litres. Give
hardware a part_no=, supplier= and price= and they appear in the list and the total.
Example#
project name="Box" units=mm schema_version=1 {
materials { material Oak type=hardwood }
component Plate { stock Oak 120.0 100.0 10.0 }
component Side { stock Oak 10.0 100.0 60.0 }
component Rail { stock Oak 100.0 10.0 60.0 }
assembly Chest {
part Bottom instance=Plate { pos x=0.0 y=0.0 z=-70.0 }
part Top instance=Plate { pos x=0.0 y=0.0 z=0.0 }
part Left instance=Side { pos x=0.0 y=0.0 z=-10.0 }
part Right instance=Side { pos x=110.0 y=0.0 z=-10.0 }
part Front instance=Rail { pos x=10.0 y=0.0 z=-10.0 }
part Back instance=Rail { pos x=10.0 y=90.0 z=-10.0 }
}
volume Inside { bounded_by Bottom Top Left Right Front Back }
}
Inside is 100 x 80 x 60 = 480 000 mm³ = 0.48 L (volumes.Inside.net_l).
Pitfalls and diagnostics#
- A leak is an answer, not a guess. If the bounding parts do not close (a missing panel, an
open driver cutout),
statusisleakand there is no volume.leak_closes_at_mmsays how wide a seam would have to be thickened to close it; absent means a face is missing. - Seams under 2 micrometres are sealed and flagged:
sealed_gap_mmandseal_error_bound_mm3say how much the volume might be short. - Closed dowel sockets are not the enclosure: chambers under 1 mL or under 1% of the
bounding box are listed as
ignored_voids, not counted. - Mass is
nullwhere the material has no known density, with a reason: never a default. Give the materialspecies="white-oak"ordensity_kg_m3=.... Mass is air-dry, about 12% moisture content. - Contact is planar only: round tenons in round mortises are not found as contact, so free area there is a slight over-statement.
- Finish litres are a range, and the end-grain multiplier (default x2 high end) is an
assumption: no maker figure for end grain is recorded. Curved end grain is not detected.
Never trust a finish total with
area_without_litres_m2above zero as the whole job. - Glue is an area, not a quantity. No spread rate is recorded for any glue, so none is
invented. Joined pairs that meet over a curved surface are counted in
pairs_without_contactand left out of the area. - Stock totals are blanks, not a board layout: waste is a flat factor, and a ring blank
(sized by its circumscribing square) over-counts. Only a hardware entry with a
lengthis listed as a fastener. - Unpriced items are counted, never priced at zero: read
unpriced_itemsbesidepriced_total. - A
role=referencepart is reported but is in no total and cannot bound a volume. - A real contact with no
joinand nono_joinisww::analysis::unaccounted_contact. This can flood a design that has many incidental true contacts by its own nature (a woven seat's warp/weft crossings, a veneer top's leaf-to-leaf edges) — each is a real finding, not a bug in the check, but excusing hundreds of such pairs oneno_joinat a time is often not the right fix; it is a sign the design's structure is not well served by a per-pair declaration and is worth raising, not silencing. - A
no_joinnaming a part that does not resolve is reported, not silently ignored, and the contact it was meant to excuse is still flagged.
See also#
docs/adr/0036-model-metrics.md: the method, accuracy, and the JSON schemadocs/KDL_AUTHORING.md, thevolumesectiondocs/proposals/0132-derived-quantities-and-linked-parameters.mdsection 3
Names#
Every name this page declares, how the example library uses it, and the shortest real use.
volume#
project-level node; node in 2 of 116 examples (2 uses); property in 2 of 116 examples (2 uses).
Shortest real use, from six_board_chest.kdl:
volume Interior {
bounded_by End_L End_R Front Back Bottom Lid
}
no_join#
project-level node; not used by any example yet.
a#
word-valued property; not used by any example yet.
b#
word-valued property; not used by any example yet.
reason#
word-valued property; not used by any example yet.