feat: reactive silencers, HVAC attenuation, machine enclosures and the radiating piston (#232)
* feat(electroacoustics): radiating circular piston in an infinite baffle
Add the canonical rigid circular piston radiator (Beranek & Mellow 2e,
sections 4.19 and 13.7): the radiation impedance from the piston resistance
R1 = 1 - 2 J1(2ka)/2ka and reactance X1 = 2 H1(2ka)/2ka, the low-frequency
radiation mass 8 rho a^3 / 3, the far-field directivity 2 J1(ka sin t)/(ka sin t)
and the directivity index. radiating_piston returns a frozen RadiatingPistonResult
with .plot(); piston_resistance, piston_reactance and piston_directivity are
exposed as building blocks. Documented in the electroacoustics guide.
* feat(noise_control): silencers, HVAC duct methods and machine enclosures
New phonometry.noise_control domain (Bies, Hansen & Howard 5e; Munjal):
- Reactive silencers by the four-pole transmission-matrix method: expansion
chamber (matches the closed form 10 lg[1 + (1/4)(m - 1/m)^2 sin^2 kL]
exactly), Helmholtz and quarter-wave side-branch resonators, and
extended-tube chambers, with transmission and insertion loss for
configurable source/radiation impedances. The four-pole primitives
(duct_matrix, shunt_matrix, cascade, transmission_loss, insertion_loss,
helmholtz_impedance, quarter_wave_impedance) are exposed for composition.
- HVAC duct methods: end reflection (ASHRAE Table 8.14), bend insertion loss
(Table 8.11), plenum transmission loss (Wells' method) and flow-generated
noise of straight ducts and mitred bends (VDI 2081).
- Machine-enclosure insertion loss IL = R - C, where the panel R is supplied
by the caller (array or callable) and the interior correction reuses the
room constant of phonometry.room.
Validated against the closed forms, digitised table nodes and an independent
2D FDTD cross-check of a single expansion chamber. Documented with a guide in
both languages and a concept figure of the expansion-chamber TL.
* feat: expose piston and noise-control APIs, conformance and indexes
Re-export the piston and high-level noise-control functions at the top level,
add the noise_control API-reference section and the piston module to the
taxonomy, and register 13 additive numerical conformance checks (piston
resistance/reactance/mass/directivity and the silencer, plenum, end-reflection
and enclosure closed forms). Regenerate the conformance report, the API
reference pages/sidebar and the quick-reference table, and note the additions
in the changelog.
* fix(noise_control): correct insertion-loss sign and straight-duct flow-noise constant
Adversarial review and a source re-check turned up two numerical errors:
- insertion_loss returned the reciprocal of the correct ratio, so a real
silencer reported a negative insertion loss. Use
IL = 20 lg|(T11 Zr + T12 + Zs Zr T21 + Zs T22)/(Zs + Zr)|, which is 0 for a
through connection and equals the transmission loss for the anechoic
reference Zs = rho c/S_in, Zr = rho c/S_out. Added a test pinning IL = TL for
that reference (the previous identity-only test could not see the sign) and a
conformance check.
- flow_noise_straight_duct dropped the -2 constant of Bies Eq. (8.251)
(VDI 2081-1); added it and pinned the numeric value.
Also harden shunt_matrix against the divide-by-zero at an exact lossless
resonance, and correct the flow_noise_bend docstring: the radiated power grows
as U^6/U^8 (dipole/quadrupole) while only the stream-power efficiency grows as
U^3/U^5 (the formula itself already matched Bies Eqs. (8.252)/(8.254)).
* fix(noise_control,electroacoustics): address Sonar and bot review findings
Sonar:
- S1244 (piston): replace exact float == 0 tests with finite-masking at the
removable 0/0 singularities of R1, X1 and the directivity (physics and all
oracles unchanged).
- S3776 (enclosures): split enclosure_insertion_loss into _resolve_frequencies
and _resolve_panel_r helpers with identical semantics.
- S5863 (FDTD cross-check test): compare two independently computed runs
instead of a self-comparison, keeping the determinism check meaningful.
Review bots (verified against Bies / Beranek & Mellow before applying):
- Vectorise the piston directivity-index quadrature over frequency (bit-identical
results, confirmed by the byte-stable conformance report).
- Validate that a silencer chain shares one frequency grid (cascade) and that an
extended tube's inlet+outlet extensions do not exceed the chamber length.
- Reject a multi-dimensional interior absorption in enclosures.
- Use kwargs.setdefault for the plot line styles so a caller-supplied lw/marker/ms
no longer raises, and keep the resonance legend label when the first resonance
is out of range.
- Rephrase the side-branch conformance description with abs(...) so the pipes no
longer break the Markdown table, and add a text language hint to the formula
code blocks of the noise-control doc.
Rejected: converting the enclosures docstring equation blocks to inline literals
(the indented-equation-block style is the library-wide docstring convention,
used by ~120 modules; changing one would be inconsistent).
Correct the framing and the guards of the perception and noise-control fiches (#355)
A review pass over the psychoacoustics, hearing, speech, environmental and
noise-control fiches. The numerics held up; what did not were the statements
printed around them, plus one wrong table lookup.
ISO 1999 validated domain. nipts()/htlan() applied Formula (2) wherever they
were asked to: L_EX,8h = 130 dB over 60 years at the 0,99 fractile returned a
357,9 dB threshold shift with nothing marking it as an extrapolation. Since the
formula is quadratic in (L_EX,8h - L0) that number is meaningless, not merely
uncertain. Both now warn (NoiseInducedHearingLossWarning) outside the stated
ranges while still computing: durations outside 1-40 years (clause 6.3.1
validates Formula (2) over 10-40 and Formula (3) over 1-10), fractiles in the
tails clause 6.3.2 says "should not be estimated" (Q below 5 % or above 95 %),
and levels above the 100 dB of Annex D that the Scope's NOTE 4 restricts
validity to. The fiches print the caveat when the conditions sit outside.
ISO 1999 Q. The fiches printed the library's fractile under ISO 1999's symbol,
inverting its meaning: in Formulae (4)/(5) Q is the percentage with worse
hearing, so the most-susceptible tenth is Q = 10 %, not the "Q = 0.9" the fiche
showed while citing 6.3.2. They now print ISO's Q as ISO defines it; the guides
note the difference from the fractile argument. Two further disclosures on the
same fiches: the age component H is evaluated from ISO 7029:2017 (the edition
ISO 1999 references undated in 6.2.2), which departs from the illustrative
Table A.3 selection by up to about 7 dB at the median, and the 2/3/4 kHz
average is the user's own choice, the Scope's NOTE 1 specifying no frequency
combination. The footer states a population scope rather than "the results
relate only to the tested specimen", which describes a laboratory specimen.
ISO 1999 Annex C is wired in as an independent oracle: its worked example (male
population aged 50, 90 dB for 30 years, 1/2/4 kHz at Q = 10 %) reproduces the
Table D.2 shifts it consumes (0, 9, 19 dB), the 13,3 dB left of the 4 kHz shift
after the Formula (1) compression (C.5) and the 31,1 dB combined threshold
(C.11). The composition it exercises is now exposed as combine_age_and_noise(),
which clause 6.2 wants available on its own: 6.2.3 recommends a database B
collected on a control population of the country under consideration, and this
applies Formula (1) to whichever database the user brings.
Verdict precision. The STI and SII fiches printed the requirement with one
decimal beside a measured value at full precision, so a verdict could contradict
itself: "STI = 0.50, required >= 0.5 -> FAIL" against a 0,52 requirement, and an
SII minimum of 0,75 printed as "0.8" (the committed example said so). Both now
print the requirement at the quantity's own precision. IEC 60268-16 Annex M
joins the conformance report as an end-to-end oracle: its printed MTF matrix,
speech spectrum and ambient noise give back its published MTI row and
STI = 0,76, exercising the whole A.5.3 to A.5.8 chain.
Noise-control prediction framing. The enclosure, silencer and HVAC fiches
compute design models but read as measurements: no prediction statement,
measurement headers, a footer scoped to a tested specimen, and committed
examples citing instrumentation that never existed ("Two-microphone
transfer-matrix bench"). They now follow the EN/ISO 12354 prediction exemplar
already in the repo, with a prediction-basis line, a statement naming what each
model does not represent, and a prediction footer. Their verdicts also decided
on the half-away-from-zero display rounding while printing Python's
round-half-to-even, so 0,25 printed 0.2 against a verdict taken on 0,3; both
now use the display rounding.
ISO/PAS 1996-3. With every onset rate at or below 10 dB/s the fiche withheld
the adjustment on the onset-rate gate (clauses 4.5/8) but justified it with "no
prominent impulse is present (governing P = 6.01 <= 5)", contradicting its own
boxed P. The note now states whichever gate governs. The assessment period is
read from the result instead of being hardcoded: 30 min is the Clause 5
*default*, now the default of the new assessment_period_min argument.
Smaller corrections:
- the HVAC elbow insertion loss took the wrong row of Bies Table 8.11 for a
W/lambda exactly on a bin edge; the rows read "a <= W/lambda < b", so an edge
opens its row (1 dB at 0,14, not 0 dB);
- the wind-turbine tonality fiche cited "subclauses 9.5.2-9.5.5" of
IEC 61400-11 for a chain running through 9.5.6 to 9.5.8 (Formulae 31-34);
- the ISO 1996-2 tonal fiche notes that Table J.1 keys on the mean audibility
of the J assessed spectra (ISO/PAS 20065 Clause 5.3.9), which a
single-spectrum assessment stands in for;
- the shared 10-90 % fractile band carried a hardcoded English legend in
Spanish figures, and the Spanish silencer and HVAC fiches printed the device
kind and duct-element label in English.
docs/ERRATA.md records two more source defects found on the way: ISO/PAS
1996-3:2022 Clause 5 swaps the 3.4/3.5 cross-references of the onset rate and
the level difference (its own units contradict the clause numbers printed with
them), and ISO 9613-2:1996 Table 2 prints 4,1 dB/km at 15 C / 80 % / 1 kHz
where ISO 9613-1 gives 4,1511, the neighbouring cells rounding correctly. The
library computes that coefficient from ISO 9613-1 directly, so it is unaffected.
Committed example fiches, the conformance report, the API reference and the
llms files are regenerated.