|
| 1 | +"""The per-solid-angle spelling: one operator token, every quantity. |
| 2 | +
|
| 3 | +A volumetric emission rate resolved into direction carries sr^-1 in its unit |
| 4 | +(m^-3.s^-1.sr^-1). ``per_solid_angle`` is what carries that steradian in the |
| 5 | +name, so the unit string is not the only place the per-solid-angle character |
| 6 | +survives, and it composes onto any quantity rather than needing a base per |
| 7 | +emitted species. |
| 8 | +""" |
| 9 | + |
| 10 | +import pytest |
| 11 | + |
| 12 | +from imas_standard_names import compose, parse |
| 13 | +from imas_standard_names.grammar import parse_name |
| 14 | +from imas_standard_names.grammar.vocab_loaders import load_operators |
| 15 | + |
| 16 | +_PER_SOLID_ANGLE_NAMES = [ |
| 17 | + "source_rate_per_solid_angle", |
| 18 | + "hard_xray_source_rate_per_solid_angle", |
| 19 | + "electron_energy_source_rate_per_solid_angle", |
| 20 | + "radiance_per_solid_angle", |
| 21 | +] |
| 22 | + |
| 23 | + |
| 24 | +@pytest.mark.parametrize("name", _PER_SOLID_ANGLE_NAMES) |
| 25 | +def test_per_solid_angle_names_parse_strictly(name: str) -> None: |
| 26 | + parse(name, strict=True) |
| 27 | + |
| 28 | + |
| 29 | +@pytest.mark.parametrize("name", _PER_SOLID_ANGLE_NAMES) |
| 30 | +def test_per_solid_angle_names_round_trip(name: str) -> None: |
| 31 | + assert compose(parse(name, strict=True).ir) == name |
| 32 | + |
| 33 | + |
| 34 | +@pytest.mark.parametrize("name", _PER_SOLID_ANGLE_NAMES) |
| 35 | +def test_the_operator_leaves_the_base_intact(name: str) -> None: |
| 36 | + """The token normalizes the quantity; it never becomes part of the base.""" |
| 37 | + without = name.removesuffix("_per_solid_angle") |
| 38 | + assert parse_name(name).physical_base == parse_name(without).physical_base |
| 39 | + |
| 40 | + |
| 41 | +def test_the_token_is_a_dimension_transforming_postfix_operator() -> None: |
| 42 | + """sr^-1 joins the unit, so base-implies-unit inference must be suppressed.""" |
| 43 | + operator = load_operators().operators["per_solid_angle"] |
| 44 | + |
| 45 | + assert operator.kind == "unary_postfix" |
| 46 | + assert operator.dimension_transforming is True |
| 47 | + |
| 48 | + |
| 49 | +def test_one_token_serves_every_emitted_quantity() -> None: |
| 50 | + """No base spelled '<base>_per_solid_angle' — the operator replaces them.""" |
| 51 | + from imas_standard_names.grammar.vocab_loaders import load_physical_bases |
| 52 | + |
| 53 | + assert not [ |
| 54 | + token |
| 55 | + for token in load_physical_bases().bases |
| 56 | + if token.endswith("_per_solid_angle") or token.endswith("_per_steradian") |
| 57 | + ] |
0 commit comments