Functional Weave
Code in Python

agri.stocking-rate

Stocking rate: livestock units and LU per hectare from animal numbers, by a published LU coefficient table.

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

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

What it does

Livestock units (LU) and the stocking rate in LU per hectare, from head counts by category and a published table of livestock unit coefficients. `stockingRate("eurostat", [{category: "dairy-cows", count: 100}, {category: "bovine-1-to-2-years", count: 80}], 120)` is 156 LU, 1.3 LU/ha.

## Two tables, deliberately separate

For example

  • stocking_rate(eurostat, animals ×2, 120) → livestock units 156, units per hectare 1.3, lines ×2 Eurostat: 100 dairy cows and 80 yearlings on 120 ha is 156 LU, 1.3 LU/ha
  • stocking_rate(uk-countryside-stewardship, animals ×2, 40) → livestock units 56, units per hectare 1.4, lines ×2 Countryside Stewardship: 300 lowland ewes with lambs and 20 suckler cows on 40 ha
  • stocking_rate(eurostat, animals ×1, 3) → livestock units 224, units per hectare 74.67, lines ×1 16,000 laying hens on 3 ha is 224 LU, 74.67 LU/ha

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 stocking_rate(scheme: str, animals: Sequence[LivestockCount], area_hectares: float) -> StockingRate
schemestringeurostat or uk-countryside-stewardship
animalsLivestockCount[]head counts by category of that scheme; may be empty
area_hectaresfloatthe land they are kept on, taken to 0.0001 ha (1 m²)
returnsStockingRate

The types it declares, generated into your project

@dataclass(frozen=True)
class LivestockCount:
    """How many animals of one category."""

    #: a category code of the scheme, e.g. dairy-cows, lowland-ewe-and-lamb
    category: str
    #: head, 0 or more
    count: int

@dataclass(frozen=True)
class LivestockUnitLine:
    """One category's contribution."""

    category: str
    count: int
    #: livestock units per head, from the table
    coefficient: float
    #: count x coefficient, exact (to 0.001 LU)
    livestock_units: float

@dataclass(frozen=True)
class StockingRate:
    """Total livestock units and the rate per hectare."""

    #: total, exact (to 0.001 LU)
    livestock_units: float
    #: total / area, rounded half up to 0.01 LU/ha
    units_per_hectare: float
    #: one per animals entry, in order
    lines: List[LivestockUnitLine]

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

from fune.agri.stocking_rate import stocking_rate  # agri.stocking-rate@^1
impl/python.py · 59 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 typing import List, Sequence

from .agri_stocking_rate_data import LIVESTOCK_UNITS  ← this capability’s own data, compiled from data/livestock-units.json into the same file by fune build
from .agri_stocking_rate_types import LivestockCount, LivestockUnitLine, StockingRate
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

MAX_COUNT = 10000000


def stocking_rate(scheme: str, animals: Sequence[LivestockCount], area_hectares: float) -> StockingRate:
    """Livestock units and the stocking rate per hectare.

    Coefficients are held in thousandths of a livestock unit, so a herd's total
    is an exact integer and only the rate per hectare is rounded. Adding float
    coefficients drifts: 3 x 0.7 is 2.0999999999999996, which puts 3 yearlings
    on 12 ha at 0.17 LU/ha instead of 0.18 (0.175 rounded half up).
    """
    table = [row for row in LIVESTOCK_UNITS if row.scheme == scheme]
    if not table:
        raise ValueError('unknown scheme "%s": use eurostat or uk-countryside-stewardship' % (scheme,))
    if (
        isinstance(area_hectares, bool)
        or not isinstance(area_hectares, (int, float))
        or not math.isfinite(area_hectares)
        or area_hectares <= 0
        or area_hectares > 1000000
    ):
        raise ValueError("areaHectares must be a finite number greater than 0 and at most 1000000, received %r" % (area_hectares,))
    area = int(round(round_float(float(area_hectares), 4) * 10000))
    if area < 1:
        raise ValueError("areaHectares must be at least 0.0001 hectares, received %r" % (area_hectares,))

    total = 0
    lines: List[LivestockUnitLine] = []
    for i, animal in enumerate(animals):
        row = next((r for r in table if r.category == animal.category), None)
        if row is None:
            raise ValueError('unknown category "%s" in scheme %s' % (animal.category, scheme))
        count = animal.count
        if isinstance(count, bool) or not isinstance(count, int) or count < 0 or count > MAX_COUNT:
            raise ValueError("animal %d: count must be a whole number from 0 to 10000000, received %r" % (i + 1, count))
        thousandths = count * row.thousandths
        total += thousandths
        lines.append(
            LivestockUnitLine(
                category=animal.category,
                count=count,
                coefficient=row.thousandths / 1000,
                livestock_units=thousandths / 1000,
            )
        )
    # LU/ha in hundredths = (total / 1000) / (area / 10000) x 100.
    return StockingRate(
        livestock_units=total / 1000,
        units_per_hectare=round_div(total * 1000, area, "half-up") / 100,
        lines=lines,
    )

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 agri.stocking-rate
Download for Python agri.stocking-rate-1.0.1-python.fune · 22,694 bytes sha256 f777184933d486e1b4d3e8aeddfcafd4e2c8238bfb6d4f90e4cec923c3e39aab

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

The whole function, every language, is one file too: agri.stocking-rate-1.0.1.fune, 29,126 bytes, sha256 8445c31ba2f5ec6cb65840b2379f4ef85f0522b532fb24eeb6e45dd5e6b37c21. 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 agri.stocking-rate

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

# fune: after agri.stocking-rate

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 agri.stocking-rate
# fune: replace math.round-float in agri.stocking-rate

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 agri.stocking-rate --steps.

# fune: step agri.stocking-rate 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
Eurostat: 100 dairy cows and 80 yearlings on 120 ha is 156 LU, 1.3 LU/ha eurostat, animals ×2, 120 → livestock units 156, units per hectare 1.3, lines ×2
Countryside Stewardship: 300 lowland ewes with lambs and 20 suckler cows on 40 ha uk-countryside-stewardship, animals ×2, 40 → livestock units 56, units per hectare 1.4, lines ×2
16,000 laying hens on 3 ha is 224 LU, 74.67 LU/ha eurostat, animals ×1, 3 → livestock units 224, units per hectare 74.67, lines ×1
piglets at 0.027 LU each eurostat, animals ×1, 10 → livestock units 27, units per hectare 2.7, lines ×1
3 yearlings on 12 ha is 0.175, rounded up to 0.18, where float coefficients say 0.17 eurostat, animals ×1, 12 → livestock units 2.1, units per hectare 0.18, lines ×1
horses and ponies under Countryside Stewardship uk-countryside-stewardship, animals ×2, 5.5 → livestock units 4.4, units per hectare 0.8, lines ×2
the two tables differ: a goat is 0.1 LU in Eurostat, 0.12 in Countryside Stewardship eurostat, animals ×1, 10 → livestock units 5, units per hectare 0.5, lines ×1
hill ewes on rough grazing, a rate below 0.005 rounds to zero uk-countryside-stewardship, animals ×1, 100 → livestock units 0.4, units per hectare 0, lines ×1
no animals is zero eurostat, , 10 → livestock units 0, units per hectare 0, lines
a count of zero keeps its line eurostat, animals ×1, 1 → livestock units 0, units per hectare 0, lines ×1
Show the other 7 tests
CaseArgumentsExpected
the smallest area, one square metre eurostat, animals ×1, 0 → livestock units 0.1, units per hectare 1,000, lines ×1
an unknown scheme is an error defra, , 10 → error: unknown scheme "defra"
a category from the other table is an error eurostat, animals ×1, 10 → error: unknown category "lowland-ewe-and-lamb" in scheme eurostat
a negative count is an error eurostat, animals ×1, 10 → error: animal 1: count must be a whole number from 0 to 10000000
a fractional count is an error eurostat, animals ×1, 10 → error: animal 1: count must be a whole number from 0 to 10000000
zero area is an error eurostat, , 0 → error: areaHectares must be a finite number greater than 0
an area below a square metre is an error eurostat, , 0 → error: areaHectares must be at least 0.0001 hectares

More from the author

A livestock unit is a convention, not a measurement, and the published tables disagree (a goat is 0.1 LU to Eurostat and 0.12 LU to Countryside Stewardship), so the caller names the scheme and each category belongs to one:

- `eurostat`: the EU livestock unit (LSU) coefficients used in farm statistics, from the reference of a dairy cow = 1 LSU. 21 categories: bovine animals by age and type, sheep, goats, equidae, pigs, poultry, breeding rabbits. - `uk-countryside-stewardship`: the grazing livestock unit table Defra and the RPA use to set and check stocking in England's Countryside Stewardship agreements: cattle over 2 years 1.0, 6 months to 2 years 0.6, lowland ewe and lamb or ram 0.12, hill ewe and lamb, store lamb or hogg/teg 0.08, horse 1.0, pony or donkey 0.8, goat 0.12. Cattle under 6 months are not in the table, so there is no category for them.

Category codes are listed in `data/livestock-units.json`, with each row's full name and source. The data has no `effective` dates: these are reference conventions rather than rules with commencement dates, and a revised table will be published as a new version of this capability.

## Exactness

Coefficients are held in thousandths of an LU, so each line and the total are exact (0.027 LU piglets included) and only the rate per hectare is rounded, once, half up to 0.01 LU/ha. The area is taken to 0.0001 ha (1 m²). Adding float coefficients drifts: 3 x 0.7 is 2.0999999999999996, which puts three yearlings on 12 ha at 0.17 LU/ha rather than 0.18.

It does not judge whether a rate is too high: limits differ by scheme option, land type and season.

## Limits

Counts are whole numbers from 0 to 10,000,000; area more than 0 and at most 1,000,000 ha, and at least 0.0001 ha after rounding. An empty list is 0 LU.

## Sources

- Eurostat, "Glossary: Livestock unit (LSU)", https://ec.europa.eu/eurostat/statistics-explained/index.php?title=Glossary:Livestock_unit_(LSU) (coefficients checked against the page, September 2026). - Defra and Rural Payments Agency, "Countryside Stewardship Higher Tier manual for agreements starting on 1 January 2023", Annex 8C "Convert livestock numbers into Livestock Units", https://www.gov.uk/government/publications/countryside-stewardship-higher-tier-manual-for-agreements-starting-on-1-january-2023/annex-8c-convert-livestock-numbers-into-livestock-units (the same table is Annex 6C of the Mid Tier manual).

## Notices

Contains public sector information licensed under the Open Government Licence v3.0 (https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/).

Source: Eurostat; Defra and the Rural Payments Agency.

1.0.1 adds its attribution notices (NOTICE). The code and the tests are unchanged.

Files

PathBytes
NOTICE237
README.md3,102
data/livestock-units.json6,901
impl/python.py2,603
impl/rust.rs3,873
impl/typescript.ts2,336
vectors.json4,674