Functional Weave
Code in TypeScript

manufacturing.capacity

Capacity against routed load per work centre over a period, with efficiency, utilisation and overload flags.

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

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

What it does

Capacity requirements for a period, work centre by work centre:

capacity = available time x efficiency x utilisation load = the standard minutes of every routed operation in the period load % = load / capacity

For example

  • workCentreLoad(work centres ×5, loads ×5) → ×5 a week across five centres: CNC 1938 min capacity against 2000 routed (two loads) is 103.20%; the lathe's 479.52 min is overloaded by 480; a painted-out centre has no percentage; packing at 110% efficiency has 950 spare
  • workCentreLoad(work centres ×1, loads ×1) → ×1 a load exactly equal to capacity is full, not overloaded
  • workCentreLoad(work centres ×1, loads ×1) → ×1 one minute over is an overload

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.

export function workCentreLoad(workCentres: readonly WorkCentre[], loads: readonly RoutedLoad[]): readonly CapacityLoad[]
workCentresWorkCentre[]each work centre once, with its available time in the period
loadsRoutedLoad[]routed minutes from orders in the same period; several per centre are added
returnsCapacityLoad[]one entry per work centre, in the order given

The types it declares, generated into your project

/** A work centre and the time it can offer in the period. */
export interface WorkCentre {
  readonly id: string;
  /** scheduled time in the period: shifts x hours x machines */
  readonly availableMinutes: number;
  /** standard minutes produced per minute worked: 9500 = 95%; may exceed 10000 */
  readonly efficiencyBasisPoints: number;
  /** share of scheduled time actually worked: 8500 = 85%; at most 10000 */
  readonly utilisationBasisPoints: number;
}

/** Standard minutes of work routed to a work centre. */
export interface RoutedLoad {
  readonly workCentre: string;
  readonly minutes: number;
}

/** One work centre's capacity, load and verdict. */
export interface CapacityLoad {
  readonly workCentre: string;
  /** available x efficiency x utilisation, rounded down */
  readonly capacityMinutes: number;
  /** the routed load, summed */
  readonly loadMinutes: number;
  /** capacityMinutes - loadMinutes; negative when overloaded */
  readonly spareMinutes: number;
  /** load as a share of capacity, rounded half-up; null when capacity is zero */
  readonly loadBasisPoints: number | null;
  /** load is more than the exact capacity */
  readonly overloaded: boolean;
}

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

import { workCentreLoad } from "#fune/manufacturing.capacity@^1";
impl/typescript.ts · 61 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 { compareRational, divideRational, multiplyRational, rational, rationalToInteger } from "./math_rational.ts";  ← from math.rational ^1.0.0 · built alongside by fune
import { type CapacityLoad, type RoutedLoad, type WorkCentre } from "./manufacturing_capacity_types.ts";

function minutes(what: string, value: number): void {
  if (!Number.isSafeInteger(value) || value < 0) {
    throw new RangeError(`${what} must be a whole number of minutes, not negative, received ${value}`);
  }
}

/**
 * Capacity, load and overload per work centre. Capacity is kept as an exact
 * fraction, so a load a fraction of a minute over it is still an overload.
 */
export function workCentreLoad(workCentres: readonly WorkCentre[], loads: readonly RoutedLoad[]): readonly CapacityLoad[] {
  const totals = new Map<string, number>();
  for (const wc of workCentres) {
    if (totals.has(wc.id)) {
      throw new RangeError(`duplicate work centre "${wc.id}"`);
    }
    minutes(`availableMinutes of "${wc.id}"`, wc.availableMinutes);
    if (!Number.isSafeInteger(wc.efficiencyBasisPoints) || wc.efficiencyBasisPoints < 0) {
      throw new RangeError(`efficiencyBasisPoints of "${wc.id}" must be a whole number, not negative, received ${wc.efficiencyBasisPoints}`);
    }
    if (!Number.isInteger(wc.utilisationBasisPoints) || wc.utilisationBasisPoints < 0 || wc.utilisationBasisPoints > 10000) {
      throw new RangeError(`utilisationBasisPoints of "${wc.id}" must be a whole number from 0 to 10000, received ${wc.utilisationBasisPoints}`);
    }
    totals.set(wc.id, 0);
  }
  for (const load of loads) {
    const total = totals.get(load.workCentre);
    if (total === undefined) {
      throw new RangeError(`a load names work centre "${load.workCentre}", which is not in the list`);
    }
    minutes(`a load on "${load.workCentre}"`, load.minutes);
    const sum = total + load.minutes;
    if (!Number.isSafeInteger(sum)) {
      throw new RangeError(`the load on "${load.workCentre}" exceeds 2^53 - 1 minutes`);
    }
    totals.set(load.workCentre, sum);
  }
  return workCentres.map((wc) => {
    const capacity = multiplyRational(
      rational(wc.availableMinutes, 1),
      multiplyRational(rational(wc.efficiencyBasisPoints, 10000), rational(wc.utilisationBasisPoints, 10000)),
    );
    const loadMinutes = totals.get(wc.id) as number;
    const load = rational(loadMinutes, 1);
    const capacityMinutes = rationalToInteger(capacity, "down");
    return {
      workCentre: wc.id,
      capacityMinutes,
      loadMinutes,
      spareMinutes: capacityMinutes - loadMinutes,
      loadBasisPoints:
        capacity.numerator === 0
          ? null
          : rationalToInteger(multiplyRational(divideRational(load, capacity), rational(10000, 1)), "half-up"),
      overloaded: compareRational(load, capacity) > 0,
    };
  });
}

Install

fune build

With that line in your source, in a TypeScript project (language typescript in fune.project), fune build resolves it and its 1 dependency, pins them in fune.lock, downloads only the TypeScript 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 manufacturing.capacity
Download for TypeScript manufacturing.capacity-1.0.0-typescript.fune · 15,300 bytes sha256 951ac4c5f2b2c4a60cb85e051a31c177dfc9526ea77345b88abcd7180c61bf1d

The manifest, vectors and README with only the TypeScript implementation. Install it without the registry with fune add ./manufacturing.capacity-1.0.0-typescript.fune, or fetch it from a terminal with fune pull manufacturing.capacity@1.0.0:typescript.

The whole function, every language, is one file too: manufacturing.capacity-1.0.0.fune, 24,339 bytes, sha256 800880c66390d77d913aba0819f30b53e18201a1e71deffe04f2b59a02f81183. 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 manufacturing.capacity

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

// fune: after manufacturing.capacity

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.rational in manufacturing.capacity

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 manufacturing.capacity --steps.

// fune: step manufacturing.capacity 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
a week across five centres: CNC 1938 min capacity against 2000 routed (two loads) is 103.20%; the lathe's 479.52 min is overloaded by 480; a painted-out centre has no percentage; packing at 110% efficiency has 950 spare work centres ×5, loads ×5 → ×5
a load exactly equal to capacity is full, not overloaded work centres ×1, loads ×1 → ×1
one minute over is an overload work centres ×1, loads ×1 → ×1
capacity 479.52 with 479 minutes of load fits: rounded down capacity and the exact one agree work centres ×1, loads ×1 → ×1
a centre with zero capacity and no load is not overloaded work centres ×1, → ×1
a load percentage half-way between basis points rounds up: 1 minute on 20000 is 0.5 bp work centres ×1, loads ×1 → ×1
no work centres, no result , →
a load on a work centre not in the list is an error work centres ×5, loads ×1 → error: a load names work centre "WELD", which is not in the list
a work centre listed twice is an error work centres ×2, → error: duplicate work centre "CNC"
utilisation over 100% is an error work centres ×1, → error: utilisationBasisPoints of "CNC" must be a whole number from 0 to 10000
Show the other 4 tests
CaseArgumentsExpected
negative efficiency is an error work centres ×1, → error: efficiencyBasisPoints of "CNC" must be a whole number, not negative
negative available time is an error work centres ×1, → error: availableMinutes of "CNC" must be a whole number of minutes, not negative
a negative load is an error work centres ×1, loads ×1 → error: a load on "CNC" must be a whole number of minutes, not negative
a fractional load is an error work centres ×1, loads ×1 → error: a load on "CNC" must be a whole number of minutes, not negative

More from the author

With 2,400 minutes scheduled (a week of 8-hour days), 95% efficiency and 85% utilisation, a work centre can deliver 1,938 standard minutes; 2,000 minutes of routed work is 103.20% of that, overloaded by 62 minutes.

**Utilisation and efficiency.** Utilisation is the share of scheduled time the centre actually works (after breakdowns, waiting and absence), so it cannot exceed 100%. Efficiency is standard minutes earned per minute worked, and can exceed 100% when the standards are loose. Both are basis points.

**Overload is decided on the exact capacity.** Capacity is an exact fraction (`math.rational`) and a centre is overloaded when its load is more than that fraction. `capacityMinutes` reports it rounded down, which agrees with the flag because loads are whole minutes: 480 minutes of load against 479.52 minutes of capacity is overloaded, and a tool that rounded capacity to the nearest minute (480) would call it exactly full. `spareMinutes` is capacity minus load, so it is negative exactly when `overloaded` is true.

**Zero capacity.** A centre with no capacity in the period (down for maintenance, utilisation 0) has no load percentage: `loadBasisPoints` is null, and any load on it is an overload.

**Rules.** Every load must name a work centre in the list, and each centre appears once; loads for the same centre are added. Times are whole minutes, not negative. Results follow the order of `workCentres`, including centres with no load. The period is whatever the caller's available times and loads cover: a day, a week, a month.

Source: the capacity requirements planning calculation as defined in the APICS (ASCM) Dictionary, entries "capacity", "efficiency", "utilization" and "rated capacity" (rated capacity = available time x utilization x efficiency).

Files

PathBytes
README.md2,037
impl/python.py3,167
impl/rust.rs5,520
impl/typescript.ts2,786
vectors.json6,024