A Spanish figure writes its decimal comma on every panel it draws, and two guards keep it that way (#777)
Every helper that writes text takes a language and defaults to English, so a call that forgets to pass it on raises nothing and puts an English decimal point into a Spanish figure. The plotting modules, the figure generators and the fiches now hand the caller's language to every helper that takes one, and localize_axes is called once per axes a plot builds rather than once per figure: a twin axis, a colorbar and the z of a 3-D panel each carry a formatter of their own, and each was shipping in English beside a panel already in Spanish. What decides whether an axis is localised is its formatter and not its scale, so a log axis of distances still writing its own numbers is reached and a pinned frequency axis is left to format_frequency_axis. A zoom inset and a contour colorbar, which the save-time pass of the figure generators cannot reach, are localised where they are built. The band column of the room criterion sheet and of the whole sound-power fiche family is written in the sheet's language too.
scripts/check_language_forwarding.py fails on any call to a helper that takes a language, made from anywhere a language is in scope, that does not pass it on. It reads module and class bodies as well as functions, follows a helper through a local alias and a functools.partial, names the shapes it cannot reach, keys an exemption by its line so one approved call cannot cover another, and reports a method name only some of whose namesakes take the language, which silently switched the untyped-receiver check off.
scripts/check_figure_decimal_point.py reads the committed Spanish figures and fails on a tick label that is a number with a point in it, whichever pass was meant to write the comma. It found six figures, regenerated here. Both run from the Makefile and in CI.
The five verify_* returned a dictionary, and two of them had a twin that wrapped it (#758)
* The five verify_* return a result object, and the two twins that wrapped one are gone
Every verifier in the library returns one of the 194 result dataclasses
except these five, which returned a bare dict[str, Any]. Two of them had a
public twin whose only job was to run the verifier and package what it gave
back: filters.filter_class_compliance and emission.intensity_class_compliance.
The verifier now returns the object itself, so there is one door onto each
computation instead of two, and the rows a caller reads come back on a name
rather than on a string key.
verify_filter_class returns the FilterComplianceResult and
verify_intensity_class the IntensityInstrumentComplianceResult that their
twins used to build, with .plot() and the accredited .report() fiche on them
as before. The two twins are gone.
The other three gain a result class of their own: WeightingComplianceResult,
QuasiPeakDynamicsResult and AircraftSystemComplianceResult. Each one pins the
summary it states against the rows under it, because the summary is the
conjunction, the minimum or the strictest class of a column a reader prints
beside it, and a sheet whose box disagrees with its own table is the one
reading a caller cannot correct.
Everything after the argument the call is about is keyword-only:
verify_filter_class took num_points positionally where its twin did not, and
verify_quasi_peak_dynamics took the ballistics second.
* Pin the three new verdicts against the rows under them, and publish the weighting result
* Say keyword-only where the table says it, and name the verifier in the pages that pointed at its twin
* Every class the edition defines has to be on the row, and on the sweep
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
The unit goes in the name, where the caller writes the number (#752)
* The unit goes in the name, where the caller writes the number
A static pressure of 101 325 and a static pressure of 101.325 are both
legitimate values in this tree: the ISO 3740 family prints kilopascals,
IEC 61094-2 and ISO 15186-3 Annex A print pascals. Nothing separates them
by magnitude, because either reading is a real duty somewhere else, and
the result of the mix-up is still a float.
146 public parameters take their unit as a suffix. No value changes:
every function keeps the unit it already documented, so no oracle, no
printed figure and no conformance row moves.
temperature -> temperature_c across 39 callables, with temperature1_c,
temperature2_c, temperatures_c and temperature_celsius folded into the
same spelling; GasStream.inlet_temperature, which IEC 60534-8-3 states in
kelvin, becomes inlet_temperature_k. relative_humidity and the ISO 354
humidity both become relative_humidity_percent. The ambient pressure of
the propagation and aircraft chains becomes atmospheric_pressure_kpa, the
name air_density_astm and air_density_iso10534 already used; the ISO 3740
family takes static_pressure_kpa and the pascals of ISO 15186-3 Annex A,
ISO 9053-2 and ISO 7235 take static_pressure_pa, matching fluids.air.
Every other member of the family takes _pa.
Acoustic pressure keeps its bare name: a waveform is always in pascals
and a Signal carries its own calibration, so a suffix would restate the
type rather than resolve an ambiguity.
scripts/check_parameter_units.py holds the rule from here on. It walks
the imported package, not the source tree, because ReportMetadata is
published from the root and defined in a private module. Against main it
reports 154 offenders; here it reports none.
* Regenerate the API reference and the llms artifacts after the rewrap
* Pin the renamed argument in the four refusals nothing was reading
The unit goes in the name, where the caller writes the number (#752)
* The unit goes in the name, where the caller writes the number
A static pressure of 101 325 and a static pressure of 101.325 are both
legitimate values in this tree: the ISO 3740 family prints kilopascals,
IEC 61094-2 and ISO 15186-3 Annex A print pascals. Nothing separates them
by magnitude, because either reading is a real duty somewhere else, and
the result of the mix-up is still a float.
146 public parameters take their unit as a suffix. No value changes:
every function keeps the unit it already documented, so no oracle, no
printed figure and no conformance row moves.
temperature -> temperature_c across 39 callables, with temperature1_c,
temperature2_c, temperatures_c and temperature_celsius folded into the
same spelling; GasStream.inlet_temperature, which IEC 60534-8-3 states in
kelvin, becomes inlet_temperature_k. relative_humidity and the ISO 354
humidity both become relative_humidity_percent. The ambient pressure of
the propagation and aircraft chains becomes atmospheric_pressure_kpa, the
name air_density_astm and air_density_iso10534 already used; the ISO 3740
family takes static_pressure_kpa and the pascals of ISO 15186-3 Annex A,
ISO 9053-2 and ISO 7235 take static_pressure_pa, matching fluids.air.
Every other member of the family takes _pa.
Acoustic pressure keeps its bare name: a waveform is always in pascals
and a Signal carries its own calibration, so a suffix would restate the
type rather than resolve an ambiguity.
scripts/check_parameter_units.py holds the rule from here on. It walks
the imported package, not the source tree, because ReportMetadata is
published from the root and defined in a private module. Against main it
reports 154 offenders; here it reports none.
* Regenerate the API reference and the llms artifacts after the rewrap
* Pin the renamed argument in the four refusals nothing was reading
A condition with a default is written by name (#756)
* A condition with a default is written by name
#752 put the unit in the name of every public pressure, temperature and
humidity. This is the other half of that rule: the name is now on the
line the caller reads, because the argument cannot be passed as a bare
number any more.
The shape it removes is three numbers in a row.
air_attenuation(freqs, 20.0, 50.0, 101.325) is a temperature, a relative
humidity and a pressure in an order nobody remembers, and swapping the
last two is a call that still runs and still returns an array.
Nine functions take a bare star after the arguments the call is about.
Where one already separated some of its arguments the star moves up
rather than a second appearing, so the parameters in between join the
keyword-only half. Six dataclasses follow: the three that are nothing
but conditions are kw_only, and the three that carry a required field
take KW_ONLY after it. ReportMetadata joins them for its own reason, 34
optional fields and a comment saying new ones were appended at the end
to keep the positional order stable, which is not an API anyone can use
correctly.
The guard grows the second half of the rule and is exempt only where a
signature has to keep its positional form: metrology.sensitivity, whose
positional shape belongs to the Signal overload contract. Against main
it reports 35 conditions that can still be passed as a bare number.
No value changes: the same 995 conformance checks with the same numbers.
* Regenerate the API reference for the ReportMetadata signature
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Sound power in the 16 kHz octave band, by ISO 9295:2015 (#873)
* Determine sound power in the 16 kHz octave band (ISO 9295)
The general sound power methods stop at the 10 kHz one-third octave band.
phonometry.emission.sound_power_high_frequency adds the four methods of
ISO 9295:2015 for the octave band centred on 16 kHz: the room constant from
the measured reverberation time through the Eyring relation (Formulae (4)
and (5)), the room constant from the calculated air absorption (Formula (7)),
the direct level L_W = L_p - 10 lg(4/R) (Formula (6)), the comparison with a
reference sound source for broadband noise and for tones (Formulae (8) and
(9)), the analyser bandwidth and sideband sum for a moving microphone
(Formulae (2) and (3)) and the absorption correction of the free-field
method beyond 2 m (Formula (10)). Clause 10.1 carries the levels to the
reference meteorological conditions with the C1 and C2 of ISO 3741.
Annex A is ISO 9613-1 written in nepers per metre and evaluated up to
22,4 kHz, so air_absorption_np_per_m reads the library's ISO 9613-1
implementation without the advisory that implementation raises above
10 kHz. Tables 1 and 2 are pinned cell by cell: 581 of their 624 cells are
Annex A rounded to four decimals with the temperature converted as
theta + 273,16 K, and the other 43 are cells whose fourth decimal is 0 and
is printed as another digit.
* Pin ISO 9295 Tables 1 and 2 cell by cell, and register the 43 cells they misprint
Eleven conformance checks cover ISO 9295:2015. Two hold the 303 and 278
cells that Tables 1 and 2 print correctly to Annex A rounded to their four
decimals, two name the 9 and 34 cells they misprint with the value Annex A
gives, one holds the library's own conversion within a unit of the fourth
decimal of all 581, and six pin Formulae (2) to (10) in closed form.
The errata registry gains one entry, in both languages, listing the 43
cells: in every one Annex A gives 0 as the fourth decimal and the page prints
another digit, most often the third decimal repeated. The entry also records
that the tables were computed with theta + 273,16 K, which is why 61 of the
correct cells sit one unit away from Annex A evaluated at theta + 273,15 K.
The 2013 draft prints the same cells.
* Give the 16 kHz octave band its guide, in both languages, with its figures
The new guide "Sound Power in the 16 kHz Octave (ISO 9295)" takes a printer
and a power supply through the methods: the room constant from the measured
reverberation time and from the air absorption of Annex A, the direct level
with the C1 and C2 clause 10.1 asks for, the comparison for tones with the
bandwidth a moving microphone needs and the sideband sum, and the free-field
correction beyond 2 m. One figure sets Annex A against every printed cell of
Tables 1 and 2 and shows the 43 misprints; the other draws the two
determinations with the result's own plot.
The guide is registered in the sidebar, the guides index (139 guides), the
device and emission overviews and the documentation index, the API table
gains a row for each new name, and the emission share of llms.txt splits off
the three reverberant-field routes so each shard stays under its budget.
* Say what the 43 misprinted ISO 9295 cells share: a 0 of Annex A printed as another digit
* The minimum analyser bandwidth takes speed_of_sound, the name every other function gives it
* The 16 kHz guide says how clause 5.4 points the microphone: upwards, with the equipment at grazing incidence
* Ask ISO 9295 Table 3 which levels to determine, and hold the 16 kHz formulae away from the reference state
high_frequency_levels_to_determine reads Table 3 of ISO 9295:2015: for the
noise below 8 kHz and in the 16 kHz octave it returns the levels a report
gives, and refuses the two combinations the table has no row for.
The tests now pin Formulae (4) and (5) in a room whose volume and surface
differ, C1 and C2 at 30 degC and 90 kPa in both methods, the static pressure
through Annex A, Formula (7) and Formula (10), every boundary the standard
states, the tonal direct method, a two-dimensional reference source and what
the plot draws. The conformance rows of the misprinted cells show what the
page prints and count the cells that are Annex A with the first trailing 0
set as the digit before it. The curated API table spells speed_of_sound as
the function does and names the ISO 9295 emitters of SoundPowerWarning and
AtmosphericAbsorptionWarning, and Formula (10) is written K_alpha as printed.
* The 16 kHz guide and the errata name the largest misprint of Tables 1 and 2, and the symbol Formula (A.5) writes wrong
The largest misprint is "0,05 50" for 0,050 0 at 21 500 Hz, 27 degC and
60 %, 0,005 0 Np/m, not "0,033 0": the three cells whose Annex A value ends in
two zeros are each printed 10 % high, and two more such cells are printed
correctly. Formula (A.5) writes the oxygen relaxation frequency with a digit
zero where the symbol list and Formula (A.3) use the letter O; that slip has
its own entry. The guide gains a section on Table 3, says that the gap
between bar and dot is the room term plus C1 and C2, gives 1.6 dB at
12.5 kHz, uses alpha_room in Spanish as in English, and its figure names the
theta + 273.16 K conversion the right panel measures against.
* The conformance counts, API reference, llms files and site pages follow the ISO 9295 rows
1321 conformance checks across 91 domains and 467 standards, on the current
main; the API reference lists high_frequency_levels_to_determine, and the
site errata carry the corrected ISO 9295 entries.
* The labels the high-frequency sound power plots share are named once
* The generated pages follow the layers below
The unit of critical_angle depended on which function you called (#757)
* The unit of critical_angle depended on which function you called
#755 closed the bare names of the geometry. These are the compounds built
on the same words, and one of them is worse than anything the first pass
found: in weston_regimes.py, WestonRegimeBoundaries.critical_angle is in
radians and weston_regime_boundaries(critical_angle=...) one screen below
it takes degrees, while seabed_reflection.py has it in degrees again. The
same name, 57 times apart. The field is critical_angle_rad now and the two
overrides critical_angle_deg, which is what the function converts to
anyway.
The rest, read off each docstring: degrees for bank_angle, path_angle(s),
grazing_angle, launch_angles, arrival_angles, source_angle and
limiting_angle; radians for incidence_angle and angle_limit; metres for
the thirteen diameters; steradians for solid_angle; per second for
sound_speed_gradient; nepers per radian for reflection_loss_gradient and
its override. runway_gradient becomes runway_gradient_ratio, because its
own docstring says dimensionless.
Two public functions were caught by the sweep and put back:
critical_angle(c1, c2) and reflection_loss_gradient(seabed). The rule is
about parameters; what a function returns is said by its return type.
element_diameter and pitch_diameter stay bare in the guard's exemptions,
because the bearing formulae use them only as the ratio d/D.
The guard matches the geometric quantities as whole words inside a name
now, so critical_angle and duct_diameter are held to the rule and
triangles is not, and the two tuples of the first pass collapse into one.
No value changes: the same 995 conformance checks with the same numbers.
One clip is re-rendered, because its scene reaches the renamed parameters.
* The geometric conditions with a default are written by name too
The rule of #756 now reaches the names #755 and this branch renamed.
Thirteen more conditions carry a default and could still be passed as a
bare number.
piston_directivity_pattern and hydrophone_depths take a star before
their angle argument, which keeps the quantity the call is about
positional.
Nine result dataclasses take KW_ONLY after the fields they are about,
among them ApproachStep and DepartureStep, whose ANP steps were being
written as five numbers in a row against a column of sheet C-6.2.
* Say both halves of the rule in the guard's own docstring
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Determine the sound power of a whole multisource industrial plant from a contour round it, by ISO 8297 (#911)
A quarry, a crushing plant or a petrochemical complex is too large for an enveloping surface and too full of sources to measure one at a time, so ISO 8297:1994 measures the plant as one source for the prediction of the levels at its neighbours. The new emission.sound_power_plant module lays the positions on a contour drawn as a polygon round the plant, with the distances, aspect angles, microphone directions and heights of clause 9, and runs the nine steps of clause 10 to the octave-band and A-weighted sound power with the uncertainty of Table 1. The air absorption is Table 3 as printed, or the ISO 3891 Annex A coefficient for the weather of the measurement. check_plant_measurement gives one verdict on the requirements of clauses 1.2, 6 b), 7.1, 9.1, 9.3, 9.5.1, 9.5.3 and 10.2, keeping apart those the standard lets go unmet if reported, and partial_plant_contributions puts together the parts of an industrial area measured on their own contours. Every result has a plot, a new guide in English and Spanish measures a crushing plant whose sources are known and lands within half a decibel of their power, and four misprints of the standard are in the errata.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.
A flag is written by name everywhere, and the clip gate learns which changes cannot move a frame (#760)
#759 closed the flags a caller could reach. This closes the rest, so ruff's
FBT family is selected outright instead of documented as absent: 202
signatures take the star, and the 296 calls that passed a bare True name the
parameter.
Every one of the 156 call sites the change forced was named rather than left
positional. Two of them no static gate sees: _cached_weighting_sos and
_cached_weighting_filter are lru_cache'd, so mypy reads the call through
_lru_cache_wrapper and checks nothing, and only the suite found them.
The one place a keyword-only parameter is not a rewrite: Callable[[...], R]
cannot say that a parameter is keyword-only. ColumnsBuilder in
_report/_insulation_fiche.py and a new LeftCellBuilder in
_report/ansi_s12_2.py become protocols, which say what the alias could not.
FBT003 came down to ax.grid(True, alpha=0.3) in 295 of its 296 sites.
matplotlib declares that parameter visible, so the sites say visible=True and
a test pins the name, because a matplotlib release that moved it would
otherwise surface as a wall of unrelated red. The one that was left is
np.bool_(True) in a test, a cast rather than a flag; it is np.True_ now.
The clips are not re-rendered, and none of them needed to be.
animation_fingerprint already normalises away what cannot move a frame, the
docstrings, the import order, the TYPE_CHECKING blocks, with the reason on
each. Where the keyword-only star sits is the same kind of thing: it decides
how a caller may write the call, not what the function computes. With that
normalisation the 43 fingerprints are identical either side of the signature
sweep, measured, not assumed. The manifest is re-stamped because the
fingerprint itself changed, and the stamp records the same clips it always
described.
scripts/check_public_flags.py goes: ruff now says the same thing on every
definition, and a guard that cannot fire is noise.
Every verdict reads its pass and fail flags, grades and classes from the values it holds (#949)
A result no longer stores a verdict beside the readings it came from. Sixty-two result classes used to keep the flag, grade or class their function had worked out (`passes`, `complies`, `qualified`, the `*_met` and `*_ok` flags, the advisories, the per-band classes of the IEC 61260-1, IEC 61672-1 and IEC 61043 verdicts, the ISO 9614-2 grade, the ISO/PAS 1996-3 category, the IEC TS 61400-11-2 block status and the rest), so a result built by hand or rewritten with `dataclasses.replace` could hold readings that fail and a flag that says they pass. Each of them is now a read-only property, read from the values the result keeps and the limit the standard prints, and so is any figure that only fed it, such as the rounded level of an RD 1367/2007 period or the acceptance region of a trend test.