Functional Weave
Code in Python

health.bsa Unreviewed

Body surface area in m² by the Mosteller and the DuBois & DuBois formulas, to 2 decimal places.

1.0.1 · published 2026-10-03 by charlie · Anterra

Pinned by 16 tests, run in TypeScript, Python and Rust.

Unreviewed. Not for clinical use; regulatory review pending. This capability’s implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician or pharmacist has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.

Not professional advice. This capability calculates health figures from published rules. It is a software component for developers, not medical advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed in its README, and have a clinician or pharmacist review how you use it, before anyone relies on the output. Provided “as is” under its licence, without warranty.

Not a medical device. It is not intended to diagnose, treat or support clinical decisions about any individual. Anyone building it into clinical software is responsible for that software’s regulatory status, and must validate it under their own clinical governance.

What it does

Status: needs review and sign-off by a qualified clinician before it is published. Not a medical device; for decision support only; always follow local clinical guidelines.

Body surface area (BSA) in square metres from weight and height, by the two formulas in common clinical use, each rounded half away from zero to two decimal places:

For example

  • body_surface_area(60, 150) → mosteller 1.58, du bois 1.55 DuBois & DuBois's own worked example: 150 cm, 60 kg is 1.55 m² (Mosteller 1.58)
  • body_surface_area(80, 180) → mosteller 2, du bois 2 180 cm, 80 kg: Mosteller is exactly √4 = 2.00; DuBois 1.9964 rounds to 2.00
  • body_surface_area(70, 170) → mosteller 1.82, du bois 1.81 170 cm, 70 kg: the formulas disagree in the second place

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 body_surface_area(weight_kg: float, height_cm: float) -> BodySurfaceArea
weight_kgfloatbody weight in kilograms, 1 to 650
height_cmfloatheight in centimetres, 30 to 272; not metres
returnsBodySurfaceArea

The type it declares, generated into your project

@dataclass(frozen=True)
class BodySurfaceArea:
    """Both formulas, so a caller uses the one its protocol names and never mixes them."""

    #: m², √(height cm × weight kg ÷ 3600), rounded half away from zero to 2 places
    mosteller: float
    #: m², 71.84 × W^0.425 × H^0.725 cm² ÷ 10000, rounded half away from zero to 2 places
    du_bois: float

Your code names it in one line, in the file that uses it

from fune.health.bsa import body_surface_area  # health.bsa@^1
impl/python.py · 28 lines · open · raw

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 .health_bsa_types import BodySurfaceArea
from .math_pow import pow  ← from math.pow ^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


def _check_range(name: str, value: float, low: float, high: float, unit: str) -> None:
    ok = not isinstance(value, bool) and isinstance(value, (int, float)) and math.isfinite(value)
    if not ok or value < low or value > high:
        raise ValueError("%s must be a number from %s to %s %s, received %r" % (name, low, high, unit, value))


def body_surface_area(weight_kg: float, height_cm: float) -> BodySurfaceArea:
    """Body surface area by both published formulas. They differ by up to a
    few per cent, and a dose protocol names one, so both are returned rather
    than a silent default. Powers go through math.pow, never ** or
    math.sqrt, so the three languages agree to the bit before rounding."""
    _check_range("weightKg", weight_kg, 1, 650, "kg")
    # Height in metres (1.75) is the commonest unit slip.
    _check_range("heightCm", height_cm, 30, 272, "cm")
    w = float(weight_kg)
    h = float(height_cm)
    # Mosteller (1987): BSA (m2) = ([height (cm) x weight (kg)] / 3600)^1/2.
    mosteller = pow((h * w) / 3600, 0.5)
    # DuBois & DuBois (1916): A = W^0.425 x H^0.725 x 71.84, A in cm2.
    du_bois = (pow(w, 0.425) * pow(h, 0.725) * 71.84) / 10000
    return BodySurfaceArea(mosteller=round_float(mosteller, 2), du_bois=round_float(du_bois, 2))

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 health.bsa
Download for Python health.bsa-1.0.1-python.fune · 10,206 bytes sha256 cefdc985e73f03d2d25658dc9b1c00530acf8bf14190e038df345748bb273d36

The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./health.bsa-1.0.1-python.fune, or fetch it from a terminal with fune pull health.bsa@1.0.1:python.

The whole function, every language, is one file too: health.bsa-1.0.1.fune, 13,763 bytes, sha256 fe964241429628530d99653abd5ac16c3aa6ebad194a08439b4b61137669358c. 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 health.bsa

after — your function gets the result and the arguments, and returns the final result.

# fune: after health.bsa

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.pow in health.bsa
# fune: replace math.round-float in health.bsa

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 health.bsa --steps.

# fune: step health.bsa 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.

CaseArgumentsExpected
DuBois & DuBois's own worked example: 150 cm, 60 kg is 1.55 m² (Mosteller 1.58) 60, 150 → mosteller 1.58, du bois 1.55
180 cm, 80 kg: Mosteller is exactly √4 = 2.00; DuBois 1.9964 rounds to 2.00 80, 180 → mosteller 2, du bois 2
170 cm, 70 kg: the formulas disagree in the second place 70, 170 → mosteller 1.82, du bois 1.81
175 cm, 70 kg: here DuBois is the larger 70, 175 → mosteller 1.84, du bois 1.85
152 cm, 45 kg 45, 152 → mosteller 1.38, du bois 1.38
190 cm, 100 kg 100, 190 → mosteller 2.3, du bois 2.28
165 cm, 150 kg: in obesity DuBois is 0.17 m² lower 150, 165 → mosteller 2.62, du bois 2.45
a child: 110 cm, 20 kg; DuBois 0.77502 is just above the 0.775 tie 20, 110 → mosteller 0.78, du bois 0.78
a newborn: 50 cm, 3.5 kg 3.5, 50 → mosteller 0.22, du bois 0.21
the lowest accepted weight and height 1, 30 → mosteller 0.09, du bois 0.08
Show the other 6 tests
CaseArgumentsExpected
the highest accepted weight and height 650, 272 → mosteller 7.01, du bois 6.56
height in metres is refused 70, 1.75 → error: heightCm must be a number from 30 to 272 cm
zero weight is refused 0, 170 → error: weightKg must be a number from 1 to 650 kg
negative height is refused 70, -170 → error: heightCm must be a number from 30 to 272 cm
a weight in grams is refused 70,000, 170 → error: weightKg must be a number from 1 to 650 kg
a missing weight is refused —, 170 → error: weightKg must be a number from 1 to 650 kg

More from the author

- **Mosteller**: BSA (m²) = √( height (cm) × weight (kg) ÷ 3600 ) - **DuBois & DuBois**: A = W^0.425 × H^0.725 × 71.84, with A in cm², W in kg and H in cm; divided by 10 000 for m² (the familiar 0.007184 × W^0.425 × H^0.725)

## Why both

The two disagree by up to a few per cent, more in obesity (150 kg at 165 cm is 2.62 m² by Mosteller and 2.45 m² by DuBois). Chemotherapy verification standards accept either but insist on consistency: a protocol, a prescriber and a pharmacist checking the prescription must all use the same formula. So both are returned, named, and nothing picks one silently.

## Edge cases

- Weight 1 to 650 kg, height 30 to 272 cm; anything else is refused, never clamped. Height in metres (1.75) is the usual slip. These ranges are guard rails for a clinician to confirm, not part of either formula. - DuBois & DuBois derived their formula from nine subjects, one a child; paediatric protocols often specify Mosteller (or another formula) instead. - Two decimal places is the precision BSA is prescribed at. Some units round further (to 0.1 m²) for dose banding; that is the caller's step. - Powers use `math.pow` (and the square root is `math.pow(x, 0.5)`), not the platform library, so the three languages agree to the bit (`floats exact`).

## Sources

- Du Bois D, Du Bois EF. *Clinical calorimetry, tenth paper: A formula to estimate the approximate surface area if height and weight be known.* Arch Intern Med 1916;17(6):863-871. The equation, the constant 71.84 and the worked example (150 cm, 60 kg, 1.55 m²) were read from the scanned original: https://archive.org/details/sim_jama-internal-medicine_1916-06-15_17_6 - Mosteller RD. *Simplified calculation of body-surface area.* N Engl J Med 1987;317(17):1098. doi:10.1056/NEJM198710223171717. The letter itself is behind a paywall and was not retrieved; the equation and constant 3600 were confirmed from the NHS Thames Valley Cancer Network, *Verification standards SOP* (2015), which states "BSA (m²) = ([Height(cm) x Weight(kg)]/3600)½" citing the letter, and that "the DuBois or Mosteller formulae are accepted as the standard BSA nomogram for adults": https://thamesvalleycanceralliance.nhs.uk/wp-content/uploads/2022/03/TVCN-Verification-SOP-November-2015.pdf

## Before you rely on this

**Not professional advice.** This capability calculates health figures from published rules. It is a software component for developers, not medical advice. Rules change and every rate here has an effective date. Check that the dates cover your case. Verify results against the official sources listed above, and have a clinician or pharmacist review how you use it, before anyone relies on the output. Provided "as is" under its licence, without warranty.

**Not a medical device.** It is not intended to diagnose, treat or support clinical decisions about any individual. Anyone building it into clinical software is responsible for that software's regulatory status, and must validate it under their own clinical governance.

**Not for clinical use; regulatory review pending.** Whether publishing this capability makes it a medical device is under regulatory review. Until that is settled it is a developer library only.

**Unreviewed.** This capability's implementations agree in every language and pass its published test vectors, which were worked out from the official sources cited. But no qualified clinician or pharmacist has yet checked those vectors, or confirmed that the capability covers the cases it claims. Treat it as a draft. Do not use it for real people, money or decisions without your own expert review. Once a qualified reviewer signs off, this notice is replaced with their name, qualification and the date. Each new version needs fresh sign-off.

1.0.1 marks it unreviewed (not for clinical use; regulatory review pending). The code and the tests are unchanged.

Files

PathBytes
README.md4,278
impl/python.py1,411
impl/rust.rs2,045
impl/typescript.ts1,371
vectors.json2,071