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/hastocking_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 hastocking_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
| scheme | string | eurostat or uk-countryside-stewardship |
| animals | LivestockCount[] | head counts by category of that scheme; may be empty |
| area_hectares | float | the land they are kept on, taken to 0.0001 ha (1 m²) |
| returns | StockingRate |
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
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
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.
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Case | Arguments | Expected | |
|---|---|---|---|
| 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
| Path | Bytes |
|---|---|
| NOTICE | 237 |
| README.md | 3,102 |
| data/livestock-units.json | 6,901 |
| impl/python.py | 2,603 |
| impl/rust.rs | 3,873 |
| impl/typescript.ts | 2,336 |
| vectors.json | 4,674 |