at main
3 folders
6 files
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
Make a refusal test pin the refusal it is named for (2 of 3) (#638)
* Make a refusal test pin the refusal it is named for (2 of 3)
* Give the three absolute-zero guards the tests they never had
Correcting a comma decimal in seven error messages dropped new-code coverage
to 57.1 percent, and the arithmetic says what the gate was pointing at: four
of the seven guards were already exercised and three were not, and four over
seven is that number exactly.
The three are refused now through their public entry points, and each was
shown to pin its guard by deleting the guard in a scratch tree and watching
the test fail. What the unguarded code returns for a temperature below
absolute zero is worth recording: an air density of minus thirteen kilograms
per cubic metre, a speed of sound of NaN out of a square root of a negative
number, and a sound power level of NaN out of a logarithm of a negative
ratio. None of the three refused anything on its own.
None of the tests pins the number. -273.15 and 0.5 are the library's, and
pinning the very value that was just corrected would be the defect this
branch exists to close, wearing the other face.
A fourth test is here that moves no coverage at all. The Poisson ratio guard
was already reached, but only ever from above: the existing test probes 0.5
and nothing had tried the other half of -1 < nu < 0.5. Both bounds turn out
to be closed, and below the lower one the unguarded frame modulus is exactly
zero, which divides.
Compute the two sonar terms the equation could only be handed (#691)
`underwater.array_directivity_index` and `underwater.detection_threshold` give
the sonar equation two of its own terms. Both were arguments a caller had to
find elsewhere: the guide typed them in, which is not a model.
The directivity index of an unshaded line array follows from its length, the
wavelength and the direction it is steered in, through the sine-integral
footprint of Ainslie, *Principles of Sonar Performance Modelling* (Springer
2010), Equations (6.49) to (6.57) on printed folio 267. It is also the array
gain whenever the noise is isotropic and the signal a plane wave, which is the
case the sonar equation is written for, so one number answers both. Broadside
and endfire are the same array, and the printed check values for each are a
conformance row apiece.
The detection threshold at 50 % detection probability comes from Equation
(11.22) on printed folio 581. The logarithm inside it is base two, not a square:
the two are typeset alike, they differ by half a decibel at p_fa = 1e-4, and the
distinction has a test of its own. It is written `-log2(2 p)` rather than
`log2(1 / (2 p))`, since the reciprocal overflows to infinity for the smallest
subnormal probability, where the threshold is a perfectly finite 29.5 dB. A
false-alarm probability of one half is refused rather than returned, since the
threshold diverges there: half the empty beams are already being declared
detections.
Four conformance rows, and the sonar domain is the sixty-third.
The guide said the two decibels between decades were three, and that they were
the same for every decade; the price of demanding fewer false alarms falls away
as it is paid, and the guide now says which decade costs what. The rest: a
duplicate `### Added` heading, one section numbered 3.1 twice, and the decimal
commas this work wrote while citing Ainslie, which is an English source and
takes a point.
The sine-integral footprint guarded its removable singularity with
`x == 0.0`, the only float compared for equality anywhere in `src`, and an
endfire steer is exactly where it lands. Writing `sin^2(x)/x` as `x sinc(x)^2`
removes the singularity and the comparison together; the two agree to one unit
in the last place across the range. An architecture test now refuses any float
compared for equality in the source, checked by putting the guard back and
watching it name the file and the line.
Two more from review, both about the same edge. The docstring called the
low-frequency 0 dB a cutoff below one wavelength; it is a limit, and an array
one wavelength long returns 3.45 dB. And the closed form divided by
`G0 = 2L/lambda`, which underflows to nought for a short enough array, so a
positive finite length and a positive finite wavelength could raise a domain
error or a division by zero instead of returning that limit. Both factors of
`G0` cancel by hand, so it no longer appears in a denominator, and
`sigma(x)/x` comes from its series below 1e-4, where the omitted term is worth
9e-19 and the closed form is the one that loses a digit to cancellation. The
answer moves by at most 7e-15 dB anywhere it used to work.
Name the refusal where the failure was anonymous (#631)
* Name the refusal where the failure was anonymous
An audit recorded the call sites where a bad input dies deep inside
stdlib or numpy, in words that name neither the parameter nor the
function: a KeyError spelling out a dict key, an IndexError about
axis 0, LAPACK announcing that an SVD did not converge because a NaN
reached it. The maintainer approved changing those observable
failures, and 93 of the 99 surviving sites now refuse by name at the
entry point the caller actually typed; the other six were measured and
found already fixed by earlier waves.
Reproducing each failure first is what the batch was for, and it paid
in the usual currency: several sites were not loud and wrong but
silent and wrong. A misspelt label side rendered byte-identically to
the left it fell back to, in a module whose stated point is that a
typo must break generation readably. A temperature below absolute
zero returned an absorption area of minus 127 square metres behind a
warning. An infinite airflow input returned an all-NaN result without
a word, and a four-entry room dimension was truncated to three.
Every guard carries a refusal test matched on the parameter name, and
the tests that pinned the old anonymous failures now pin the named
ones, which is the approved change stated in the suite's own words.
* Answer Sonar without unsaying what the guards say
Four findings, one of them half-wrong in an instructive way. The
operator rule asked for <= where a guard wrote `not >`, and the
negation was the point: it is how NaN gets refused. The rewrite keeps
the refusal and makes it legible instead, naming isnan beside the
bound, which is better than either the flagged form or the suggestion.
The band-centre validation moves out of precision_qualification into a
named helper, since the guard pushed the function one branch past what
one reader holds; and two refusal tests build their odd input on the
line above the block, so the block covers only the call that must
raise.
* Extend the guards where the review read past them
Sixteen review threads said each new guard misses an adjacent bad
input. Ten reproduced and are fixed: non-finite levels and power
levels that printed nan without a word, an infinite speed of sound
that zeroed the speed-dependent term of Eq. (5) because infinity is
positive, an overlap so large float() overflowed, settled by keeping
the comparison in exact integer arithmetic rather than by catching
the overflow. Six were measured and rejected: a float filter order
proven bit-identical to its integer twin across every band, an array
index the pinned numpy already refuses by name, and booleans, policed
nowhere, because True computes the correct n=1 answer and a caller
writing it is not plausible arithmetic.
One thread was answered twice, by accident and then on purpose. A
batch agent rejected naming the conversion failures in the shared
validators as out of scope while its neighbour, needing it for the
hydrophone depths, implemented it; the tree kept the implementation,
so the rejection is retracted on the thread and the work completed:
all four coercing validators route through one conversion that
answers 'name' must be numeric with numpy's own error chained
underneath, and a parametrized test drives three kinds of garbage
through each.
* Let the reference carry the raises the review round added
The ten thread fixes documented their new refusals, and the pages are
generated from those docstrings, so they move together. Regenerated as
the last step this time, which is the rule the previous drift taught
and this branch forgot once more.
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.
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.