construction.brick-count
Bricks and mortar for a wall, openings subtracted, for UK standard 215 x 102.5 x 65 mm bricks with 10 mm joints.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 17 tests, run in TypeScript, Python and Rust.
What it does
Bricks and mortar for a wall of UK standard bricks, with doors and windows subtracted.
## The brick
For example
brick_count(1, 1, , 1, 0%)→ net area square metres 1, bricks 60, mortar cubic metres 0.018 one square metre of half-brick wall is the trade's 60 bricksbrick_count(10, 2.4, openings ×2, 1, 5%)→ net area square metres 20.85, bricks 1,298, mortar cubic metres 0.387 a wall with a door and a window, 5 percent wastagebrick_count(1, 1, , 2, 0%)→ net area square metres 1, bricks 119, mortar cubic metres 0.046 a one-brick wall is 119 bricks a square metre, not 120, and adds a collar joint
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
def brick_count(wall_length: float, wall_height: float, openings: Sequence[Opening], skins: int, wastage_basis_points: int) -> BrickCount
| wall_length | float | metres, more than 0 and at most 1000; taken to the nearest millimetre |
| wall_height | float | metres, more than 0 and at most 100; taken to the nearest millimetre |
| openings | Opening[] | doors and windows to leave out; may be empty |
| skins | int | 1 for a half-brick wall, 2 for a one-brick wall; 1 to 4 |
| wastage_basis_points | int | cutting and breakage, applied to bricks and mortar, 500 = 5%; 0 to 10000 |
| returns | BrickCount |
The types it declares, generated into your project
@dataclass(frozen=True)
class Opening:
"""A door, window or other hole in the wall, in metres."""
width: float
height: float
@dataclass(frozen=True)
class BrickCount:
"""What to order for the wall."""
#: the wall face less its openings
net_area_square_metres: float
#: including wastage, rounded up to whole bricks
bricks: int
#: including wastage, rounded up to the whole litre (3 dp)
mortar_cubic_metres: float
Your code names it in one line, in the file that uses it
from fune.construction.brick_count import brick_count # construction.brick-count@^1
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import math
from typing import Sequence
from .construction_brick_count_types import BrickCount, Opening
from .math_round_div import round_div ← from math.round-div ^1.0.0 · built alongside by fune
from .math_round_float import round_float ← from math.round-float ^1.0.0 · built alongside by fune
# A 215 x 65 mm brick face plus a 10 mm joint is 225 x 75 mm of wall.
FACE_MM2 = 225 * 75
# Mortar per mm2 of wall face, in mm3: each skin fills the joints around a
# brick, (16875 - 215 x 65) / 16875 of the face, 102.5 mm deep, which is
# 2378/135 mm; each collar joint between skins is 10 mm = 1350/135 mm.
SKIN_MORTAR = 2378
COLLAR_MORTAR = 1350
MORTAR_DIVISOR = 135
MM3_PER_LITRE = 1000000
def _millimetres(value: float, message: str, maximum: int) -> int:
if (
isinstance(value, bool)
or not isinstance(value, (int, float))
or not math.isfinite(value)
or value <= 0
or value > maximum
):
raise ValueError(
"%s must be a finite number greater than 0 and at most %d metres, received %r" % (message, maximum, value)
)
return int(round(round_float(value, 3) * 1000))
def brick_count(
wall_length: float, wall_height: float, openings: Sequence[Opening], skins: int, wastage_basis_points: int
) -> BrickCount:
"""Bricks and mortar for a wall of UK standard bricks, openings subtracted.
The count is the net face area over one brick's 225 x 75 mm share of it,
59.26 per m2 per skin (the trade's "60 a metre"), times the skins, plus
wastage, rounded up. Mortar is the joint volume that geometry implies.
Everything is whole millimetres and integer arithmetic.
"""
length = _millimetres(wall_length, "wallLength", 1000)
height = _millimetres(wall_height, "wallHeight", 100)
if isinstance(skins, bool) or not isinstance(skins, int) or not (1 <= skins <= 4):
raise ValueError("skins must be a whole number from 1 to 4, received %r" % (skins,))
if (
isinstance(wastage_basis_points, bool)
or not isinstance(wastage_basis_points, int)
or not (0 <= wastage_basis_points <= 10000)
):
raise ValueError("wastageBasisPoints must be a whole number from 0 to 10000, received %r" % (wastage_basis_points,))
net = length * height
for i, opening in enumerate(openings):
w = _millimetres(opening.width, "opening %d: width" % (i + 1), 1000)
h = _millimetres(opening.height, "opening %d: height" % (i + 1), 1000)
net -= w * h
if net < 0:
raise ValueError("openings are larger than the wall")
bricks = round_div(net * skins * (10000 + wastage_basis_points), FACE_MM2 * 10000, "up")
per_mm2 = skins * SKIN_MORTAR + (skins - 1) * COLLAR_MORTAR
# Up to the whole litre first, then the wastage, so no product passes 2^53.
litres = round_div(net * per_mm2, MORTAR_DIVISOR * MM3_PER_LITRE, "up")
mortar = round_div(litres * (10000 + wastage_basis_points), 10000, "up")
return BrickCount(
net_area_square_metres=net / 1000000,
bricks=bricks,
mortar_cubic_metres=mortar / 1000,
)Install
fune build
With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add construction.brick-count
The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./construction.brick-count-1.0.0-python.fune, or fetch it from a terminal with fune pull construction.brick-count@1.0.0:python.
The whole function, every language, is one file too: construction.brick-count-1.0.0.fune, 19,874 bytes, sha256 4c520d53927a6f63aa67ea0907846e0323782667d63ef704067c362ffc799a8f. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
# fune: before construction.brick-count
after — your function gets the result and the arguments, and returns the final result.
# fune: after construction.brick-count
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
# fune: replace math.round-div in construction.brick-count
# fune: replace math.round-float in construction.brick-count
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show construction.brick-count --steps.
# fune: step construction.brick-count after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| one square metre of half-brick wall is the trade's 60 bricks | 1, 1, , 1, 0% | → | net area square metres 1, bricks 60, mortar cubic metres 0.018 |
| a wall with a door and a window, 5 percent wastage | 10, 2.4, openings ×2, 1, 5% | → | net area square metres 20.85, bricks 1,298, mortar cubic metres 0.387 |
| a one-brick wall is 119 bricks a square metre, not 120, and adds a collar joint | 1, 1, , 2, 0% | → | net area square metres 1, bricks 119, mortar cubic metres 0.046 |
| exactly 100 bricks plus 10 percent is exactly 110 | 2.25, 0.75, , 1, 10% | → | net area square metres 1.688, bricks 110, mortar cubic metres 0.033 |
| a garden wall that comes out to exactly 336 bricks with wastage | 6, 0.9, , 1, 5% | → | net area square metres 5.4, bricks 336, mortar cubic metres 0.101 |
| three skins | 1, 1, , 3, 0% | → | net area square metres 1, bricks 178, mortar cubic metres 0.073 |
| dimensions are taken to the nearest millimetre | 3, 1, , 1, 0% | → | net area square metres 3, bricks 178, mortar cubic metres 0.053 |
| an opening the size of the wall leaves nothing to build | 2, 1, openings ×1, 1, 5% | → | net area square metres 0, bricks 0, mortar cubic metres 0 |
| openings larger than the wall are an error | 2, 1, openings ×1, 1, 0% | → | error: openings are larger than the wall |
| no skins is an error | 1, 1, , 0, 0% | → | error: skins must be a whole number from 1 to 4 |
Show the other 7 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| five skins is an error | 1, 1, , 5, 0% | → | error: skins must be a whole number from 1 to 4 |
| a fractional skin count is an error | 1, 1, , 1.5, 0% | → | error: skins must be a whole number from 1 to 4 |
| a zero-length wall is an error | 0, 1, , 1, 0% | → | error: wallLength must be a finite number greater than 0 and at most 1000 metres |
| a wall over 100 metres high is an error | 5, 101, , 1, 0% | → | error: wallHeight must be a finite number greater than 0 and at most 100 metres |
| a negative opening is an error | 5, 2, openings ×1, 1, 0% | → | error: opening 1: width must be a finite number greater than 0 and at most 1000 metres |
| negative wastage is an error | 1, 1, , 1, -0.01% | → | error: wastageBasisPoints must be a whole number from 0 to 10000 |
| fractional wastage is an error | 1, 1, , 1, 0.025% | → | error: wastageBasisPoints must be a whole number from 0 to 10000 |
More from the author
A UK metric brick is 215 × 102.5 × 65 mm (work size). Laid with 10 mm joints it occupies 225 × 75 mm of wall face (the coordinating size), so one square metre of a half-brick (single-skin) wall takes 1,000,000 / 16,875 = 59.26 bricks, which the trade calls "60 a metre". A one-brick wall (two skins) is twice that, 118.5, so 119 for a square metre rather than the 120 that doubling the rounded figure gives.
## How it is worked out
1. Wall and openings, given in metres, are taken to the nearest millimetre; the net face area is the wall less the openings, in whole mm². 2. **Bricks** = net area × skins × (1 + wastage) ÷ 16,875 mm², rounded up. 3. **Mortar** is the joint volume the geometry implies, not a rule of thumb: per skin, the joints are (16,875 − 215 × 65) / 16,875 of the face, 102.5 mm deep, which is 17.6 litres per m²; each collar joint between skins adds a 10 mm layer, 10 litres per m². Rounded up to a whole litre, then the wastage added and rounded up to a litre again.
All arithmetic is in integers after step 1, so it does not suffer from floating-point ceilings.
## What it does not count
Frogs and perforations (a frog-up brick takes more mortar; add it to the wastage), cavity walls built with a block inner leaf (count the brick skin here, blocks separately), ties, lintels, piers and returns, and the bond: English and Flemish bond one-brick walls use the same number of bricks per m² as two stretcher skins, but headers change how many facing bricks you need.
Limits: length up to 1000 m, height up to 100 m, 1 to 4 skins, wastage 0 to 10,000 basis points. Openings larger than the wall are an error; openings equal to it leave nothing to build.
## Sources
- BS EN 771-1 (clay masonry units) and the UK standard format of 215 × 102.5 × 65 mm with a 225 × 112.5 × 75 mm coordinating size; see "Brick sizes", Designing Buildings Wiki, https://www.designingbuildings.co.uk/wiki/Brick_sizes - The 60 bricks per m² single-skin rule as used in the UK trade, e.g. https://www.imperialbricks.co.uk/guidance/bricks-per-m2-guide/
Files
| Path | Bytes |
|---|---|
| README.md | 2,227 |
| impl/python.py | 3,006 |
| impl/rust.rs | 3,842 |
| impl/typescript.ts | 2,842 |
| vectors.json | 4,191 |