Skip to content

limits

Limit sets: the regulatory tables a material is measured against.

limits

Limit sets: the regulatory tables a material is measured against.

Every limit set is data, loaded from JSON in data/limits. Adding a jurisdiction means adding a file and a build script, never an elif in the calculation. Only two things in these regulations cannot be expressed as a table, and both are named explicitly rather than worked around:

  • limits given per gram of material (the NRC's nCi/g transuranic entries), which need the material's density before they can be compared against Ci/m3, and
  • limits that depend on the material's own nuclides (the NRC Class A rule that anything with a half-life under five years takes a 700 Ci/m3 limit), held in DYNAMIC_RULES.

Classes:

  • LimitSet

    A named table of per-nuclide activity limits.

Functions:

LimitSet dataclass

LimitSet(name: str, label: str, units: str, limits: dict[str, float], jurisdiction: str = '', default_limit: float | None = None, unlimited: tuple[str, ...] = (), secular_equilibrium: dict[str, tuple[str, ...]] = dict(), secular_equilibrium_sec: dict[str, tuple[str, ...]] = dict(), limits_secular_equilibrium: dict[str, float] = dict(), metal_overrides: dict[str, float] = dict(), limits_per_gram: dict[str, float] = dict(), limits_upper: dict[str, float] = dict(), dynamic_rule: str | None = None, min_half_life_scope: float | None = None, threshold: float = 1.0, source: str = '', url: str = '', retrieved: str = '', notes: str = '')

A named table of per-nuclide activity limits.

Attributes:

  • name (str) –

    Stable identifier, such as UK_EPR16_out_of_scope.

  • label (str) –

    Human readable description for reports.

  • jurisdiction (str) –

    Issuing authority, such as UK or Germany.

  • units (str) –

    Activity units the limits are in: Bq/g, Ci/m3, or Bq for a total activity limit, which needs the material's mass.

  • limits (dict[str, float]) –

    Limit per canonical nuclide name.

  • default_limit (float | None) –

    Limit applied to a nuclide absent from limits, or None where the regulation has no catch-all. Three UK sets define one: 0.01 Bq/g for UK_EPR16_out_of_scope and UK_IRR17_notification, and 0.1 Bq/g for UK_IRR17_registration. The other UK sets, and every German, US, EU and IAEA set, have none.

  • unlimited (tuple[str, ...]) –

    Nuclides the source explicitly places no limit on. They are covered, and contribute nothing, which is different from being absent.

  • secular_equilibrium (dict[str, tuple[str, ...]]) –

    Parent to the daughters whose contribution the parent's limit already includes, straight from the regulation's own table. These are the "+" rows.

  • secular_equilibrium_sec (dict[str, tuple[str, ...]]) –

    Parent to the daughters covered by its whole chain "sec" value, which is a different and usually much longer list carrying a different and much stricter limit. UK EPR 2016 gives U-238 three progeny at 1 Bq/g under "U-238+" and fourteen at 0.01 Bq/g under "U-238sec". The stricter value is stored in limits under a _sec key.

  • limits_secular_equilibrium (dict[str, float]) –

    The limit to use for a parent that the source lists twice, once plain and once marked "+", when its daughters are actually present. StrlSchV gives Th-232 as 10 Bq/g plain and 0.01 Bq/g marked, and which one applies depends on the material, not on the table.

  • metal_overrides (dict[str, float]) –

    Limits replacing or adding to limits when the material is activated metal.

  • limits_per_gram (dict[str, float]) –

    Limits in nCi/g, converted using the material density.

  • limits_upper (dict[str, float]) –

    Upper end of a limit given as a range in the source, kept for reference. limits holds the conservative lower end.

  • dynamic_rule (str | None) –

    Key into DYNAMIC_RULES for a rule that depends on the material's own nuclides.

  • min_half_life_scope (float | None) –

    Half-life in seconds below which, if every radionuclide present falls under it, the material is outside the regulation altogether. A whole-material test, not a per-nuclide filter.

  • threshold (float) –

    Index value at or above which the material fails, normally 1.

  • source, ((url, retrieved, notes)) –

    Provenance.

Methods:

  • __post_init__

    Canonicalise and validate, so a hand-built set behaves like a loaded one.

  • __hash__

    Hash by name and units.

  • daughters_of

    Daughters whose activity this set's limit for parent already covers.

  • __len__

    The number of nuclides limited, not counting whole chain variants.

covered_nuclides property

covered_nuclides: frozenset[str]

Every nuclide this set names, whether limited or explicitly unlimited.

The _sec keys are whole chain variants of a nuclide already counted, not nuclides of their own, so they are not included.

__post_init__

__post_init__()

Canonicalise and validate, so a hand-built set behaves like a loaded one.

A set built in Python went through none of the normalisation the JSON loader applies, so {"Co-60": 0.1} silently matched nothing. Doing the work here means both paths cannot diverge.

Limits are also required to be positive. A limit of zero means "no activity of this is permitted", the strictest possible value, but the sum of fractions cannot express that, and the evaluation used to skip it as though the nuclide had no limit at all, which is the opposite. Rejecting it here keeps that contradiction out of the data.

__hash__

__hash__() -> int

Hash by name and units.

The generated hash would cover every field, and five of them are dicts, so it raises TypeError even though the class presents as immutable. A set is identified by its name, which the registry already treats as a key, so that is what it hashes by. Equality still compares every field.

daughters_of

daughters_of(parent: str) -> tuple[str, ...]

Daughters whose activity this set's limit for parent already covers.

__len__

__len__() -> int

The number of nuclides limited, not counting whole chain variants.

limit_sets

limit_sets(jurisdiction: str | None = None) -> tuple[str, ...]

Names of the available limit sets, sorted.

Parameters:

  • jurisdiction

    (str | None, default: None ) –

    Restrict to one issuing authority, such as UK.

get_limit_set

get_limit_set(name: str | LimitSet) -> LimitSet

Look up a limit set by name.

Parameters:

  • name

    (str | LimitSet) –

    A registered name, or an already built LimitSet.

Raises:

  • KeyError

    If no such set is registered, listing the ones that are.

register_limit_set

register_limit_set(limit_set: LimitSet) -> None

Add a limit set at runtime, for a site-specific or draft table.

Parameters:

  • limit_set

    (LimitSet) –

    The set to register. Replaces any set of the same name, warning first if that name came from the shipped regulatory data, since shadowing a published table by accident would be hard to spot in a result that only records the name.