feat: theoretical panel sound reduction, radiation efficiency and point mobilities (#233)
* feat: theoretical panel sound insulation, plate radiation efficiency and point mobilities
Predict the airborne sound reduction index R(f) of building elements from
their physical properties, closing the EN 12354 chain from panel physics to
the single-number rating without a laboratory measurement.
building.panel_transmission adds the mass law and coincidence dip by Sharp's
method (single_panel_transmission_loss), the mass-spring-mass double wall with
an optional porous cavity fill (double_wall_transmission_loss,
mass_spring_mass_resonance), all from Bies, Hansen & Howard 5e Section 7.2.
building.aperture_transmission adds transmission through slits (Gomperts) and
circular holes (Wilson & Soroka) and their area-weighted composition with the
wall (Hopkins Section 4.3.10, Eq. 4.92), so a bare opening caps the composite
at 10 lg(S/Sa).
vibration.radiation_efficiency predicts the Leppington/Maidanik radiation
efficiency of a bending plate (Hopkins Section 2.9), the radiation factor
ISO 7849 otherwise measures; vibration.point_mobility adds the closed-form
point impedances and mobilities of infinite plates, beams and rods and the
injected power (Cremer, Heckl & Petersson 3e Table 5.1).
Every prediction exposes .plot() and, for R(f), .rating() (ISO 717-1). Anchored
by closed-form oracles (exact mass law, coincidence and mass-air-mass
frequencies, aperture area limit) and digitized curves from the source books.
* docs: panel sound insulation guide, theory, conformance, figure and API reference
Add the Predicting Panel Sound Insulation guide (EN/ES), the concept figure
(single/double wall, radiation efficiency, composite aperture), theory sections
in the rooms-buildings and vibration references, eleven conformance checks
(mass law slope, coincidence and mass-air-mass frequencies, aperture area
limit, slit resonance, plate/beam mobilities), the generated API reference
pages and the curated api-reference table, llms.txt and the CHANGELOG entry.
* refactor: address PR #233 review (Sonar S1192, slit numerical stability)
- Extract the duplicated 'Frequency [Hz]' axis label (_plot/building.py) and the
'frequency must be positive' message (panel_transmission.py) into module
constants (SonarQube S1192).
- Reformulate the Gomperts slit transmission coefficient (Eq. 4.99) by
multiplying numerator and denominator by cos^2(Ke): mathematically identical
(tau unchanged to 2.5e-16) but finite where cos(Ke) crosses zero, instead of
dividing by it.
- Guard slit_resonance_frequencies against a slit so wide that the effective
depth d + 2e turns non-positive, raising a clear error instead of a NaN.
- Propagate the earlier em-dash prose fix into the embedded llms-full.txt.
- Add regression tests for the wide-slit guard and the cos(Ke)=0 sweep.
Base the whole-body A(8) on the Directive 2002/44/EC Part B dominant axis and tighten the vibration fiches (#352)
Directive 2002/44/EC bases the whole-body daily exposure A(8) on the
highest of the frequency-weighted axis values 1,4*a_wx, 1,4*a_wy, a_wz
(Annex, Part B, point 1), not on the ISO 2631-1 Eq. (10) vector total
a_v; the hand-arm side keeps the Part A vector total a_hv.
- Relabel the whole-body magnitude of the daily-exposure fiche as
a_w,max with a printed note stating the Part B basis (EN/ES); add
vibration.wbv_exposure_basis() returning the dominant-axis value and
document in daily_vibration_exposure which magnitude each kind must
be fed with; state the same basis in the human-vibration guide
(EN/ES) and the measurement-chain diagram.
- Derive the boxed exposure zone of the fiche from the same
displayed-rounded comparisons as the assessment rows and the verdict,
so an A(8) printing exactly at a threshold cannot show a zone one
step below its own Exceeded row.
- ISO 10848 Kij fiche: distinguish an empty single-number mean caused
by every in-range band being bracketed (M < 0,25) from a spectrum
with no bands in the Annex A range (EN/ES).
- Spanish ISO 2631-5 outputs: translate the subject sex in the
injury-probability plot title and the word Formula in the fiche's
clause references.
- Correct the running_rms comment: the linear window divides by the
full window length (zero-padded front), it does not average over the
available samples.
Structure-borne sound power and installed prediction reports via .report() (#327)
* Structure-borne sound power characterization and installed prediction reports via .report()
Add one-page PDF .report() fiches to the two structure-borne building
result types, closing the EN 15657 -> EN 12354-5 chain from source
characterization to installed prediction.
StructureBornePowerResult.report() renders an EN 15657:2018 reception-plate
structure-borne sound power characterization: the per-band spatial mean plate
velocity level Lv and the injected power level L_Ws (Formula 14), the L_Ws(f)
spectrum, and the boxed band-summed total L_Ws (dB re 1 pW) with the plate
mass per area m and area S. verbose=True adds the plate loss factor eta
column; the basis strip states Formula 14 and the conversion to the
plate-independent source quantities required before EN 12354-5.
InstalledSourceResult.report() renders an EN 12354-5:2009 installed
structure-borne sound prediction, clearly labelled a prediction and not a
measurement: the per-band installed power level L_Ws,inst, each transmission
path's normalised SPL L_n,s,ij and the combined total L_n,s, the per-path and
total L_n,s(f) spectra, and the boxed band-summed total L_n,s. verbose=True
adds one column per transmission path; the basis strip states Formulae 18a/17
and the prediction disclaimer.
Both fiches reuse the shared sound-power report engine (per-band table,
spectrum, boxed result and flow assembly), extended with a caller-supplied
verdict so the structure-borne quantity symbols are stated consistently, and
render in English and Spanish. Register one example per fiche, add structural
and number-presence tests, document the reports in the guides, and record the
additions in the changelog.
* Address review: per-path column [dB] units and Spanish accent
Add the [dB] unit to the installed-prediction table's per-path column headers
so they match the installed-power and total columns, and correct the accent in
the Spanish structure-borne power guide (acelerometro -> acelerometro with the
acute accent).
Structure-borne sound power and installed prediction reports via .report() (#327)
* Structure-borne sound power characterization and installed prediction reports via .report()
Add one-page PDF .report() fiches to the two structure-borne building
result types, closing the EN 15657 -> EN 12354-5 chain from source
characterization to installed prediction.
StructureBornePowerResult.report() renders an EN 15657:2018 reception-plate
structure-borne sound power characterization: the per-band spatial mean plate
velocity level Lv and the injected power level L_Ws (Formula 14), the L_Ws(f)
spectrum, and the boxed band-summed total L_Ws (dB re 1 pW) with the plate
mass per area m and area S. verbose=True adds the plate loss factor eta
column; the basis strip states Formula 14 and the conversion to the
plate-independent source quantities required before EN 12354-5.
InstalledSourceResult.report() renders an EN 12354-5:2009 installed
structure-borne sound prediction, clearly labelled a prediction and not a
measurement: the per-band installed power level L_Ws,inst, each transmission
path's normalised SPL L_n,s,ij and the combined total L_n,s, the per-path and
total L_n,s(f) spectra, and the boxed band-summed total L_n,s. verbose=True
adds one column per transmission path; the basis strip states Formulae 18a/17
and the prediction disclaimer.
Both fiches reuse the shared sound-power report engine (per-band table,
spectrum, boxed result and flow assembly), extended with a caller-supplied
verdict so the structure-borne quantity symbols are stated consistently, and
render in English and Spanish. Register one example per fiche, add structural
and number-presence tests, document the reports in the guides, and record the
additions in the changelog.
* Address review: per-path column [dB] units and Spanish accent
Add the [dB] unit to the installed-prediction table's per-path column headers
so they match the installed-power and total columns, and correct the accent in
the Spanish structure-borne power guide (acelerometro -> acelerometro with the
acute accent).
refactor: reorganize the package into twelve domain subpackages (3.2) (#162)
* refactor: private core to _internal, module-path shims, _plot skeleton (C1)
Foundation of the 3.2 package reorganization:
- _validation/_types/_warnings/_levels_math/utils move (git mv) into the
private phonometry/_internal/ package; ~90 import sites retargeted.
- New phonometry/_compat.py: dynamic sys.modules shims keep every moved
public module path importable for one deprecation cycle (silent import,
DeprecationWarning on attribute access); generalizes and absorbs the
former loudness.py PEP 562 shim (its 3.1 wording preserved).
- _plotting.py moves (git mv) to phonometry/_plot/common.py; a silent
explicit re-export shim keeps the old private path and the 84 lazy
.plot() call sites working until each domain retargets them.
- _warn_renamed gains a 'since' parameter (3.2 for the package moves).
- tests: new test_package_architecture.py (ast edge whitelist +
fresh-interpreter subpackage imports); test_deprecated_aliases gains the
frozen 85-path pre-move snapshot and the _MOVED shim behavior test;
pytest pythonpath=tests for the upcoming mirrored test tree.
- .git-blame-ignore-revs scaffold (hashes appended at the end of Phase 1).
* refactor: metrology subpackage (C2)
core, filter_design, frequencies, parametric_filters, levels, calibration,
compliance and uncertainty move (git mv) into phonometry/metrology/ with a
curated re-export __init__; the flat API and the old module paths keep
working (facade re-exports + _compat shims). Uncertainty renderers carve out
of _plot/common.py into _plot/metrology.py; the 14 metrology test files move
to tests/metrology/ and deep imports across tests/scripts point at the new
canonical paths.
* refactor: psychoacoustics subpackage (C3)
The thirteen psychoacoustics modules (Zwicker/ECMA/Moore-Glasberg loudness,
contours, sharpness, tonality, roughness, fluctuation strength, annoyance,
tonal audibility, private Zwicker tables) move into
phonometry/psychoacoustics/; renderers carve into _plot/psychoacoustics.py;
twelve test files move to tests/psychoacoustics/. The phonometry.loudness
alias now resolves to the relocated canonical module in a single hop.
* refactor: hearing and emission subpackages (C4+C5)
hearing/ gains threshold (renamed from hearing.py), noise-induced hearing
loss, occupational exposure, SII and STI; emission/ gains the ISO 3740
sound-power family, ISO 9614 intensity and ISO/TS 7849 vibration-based
power. Renderers carve into _plot/hearing.py and _plot/emission.py; the
eleven domain test files move under tests/hearing/ and tests/emission/.
* refactor: materials, room and building subpackages (C6-C8)
materials/ collects ISO 354/11654/12999-2 absorption, scattering-diffusion,
road absorption, impedance tube, airflow resistance and EN 29052-1 dynamic
stiffness; room/ the room-acoustics, impulse-response, open-plan, room-noise,
reverberation-prediction and EN 12354-6 modules; building/ the eleven-module
EN 12354 / ISO 717 / ISO 16283 insulation family with EN 15657 and
EN 12354-5 structure-borne sound. Renderers carve into the matching _plot
modules; twenty-six test files move under their domain directories.
* fix: complete the building plot TYPE_CHECKING imports (C8 follow-up)
* refactor: vibration and environmental subpackages (C9+C10)
vibration/ collects human vibration (ISO 8041-1/2631/5349), multiple-shock
(ISO 2631-5), mechanical mobility (ISO 7626-1) and transfer stiffness
(ISO 10846); environmental/ collects the renamed rating (Lden/Ldn) and
measurement (ISO 1996-2) modules plus outdoor propagation (ISO 9613-2),
air absorption (ISO 9613-1), wind-turbine noise (IEC 61400-11) and NT
ACOU 112 impulse prominence. Renderers carve into their _plot modules and
ten test files move under the domain directories.
* refactor: aircraft, underwater and electroacoustics subpackages (C11-C13)
aircraft/ collects EPNL, the renamed atmospheric absorption, ECAC Doc 29
airport contours and ECAC Doc 32 rotorcraft hemispheres; underwater/ the
renamed acoustics/propagation/sound_speed modules with ship noise, pile
driving, ambient noise, sonar equation, seabed reflection and the numerical
solvers; electroacoustics/ distortion and frequency response. Renderers
carve into their _plot modules; seventeen test files move under the domain
directories. All twelve domain subpackages are now in place.
* refactor: finalize the 3.2 package reorganization (C14)
- api-reference gains a Namespaces section (the twelve subpackages with
scope and the 'from phonometry import aircraft as air' idiom); README
quick-start notes the namespaces; CHANGELOG records the added namespaces,
the deprecated flat module paths and the pickle note.
- plot_excitation moves from _plot/common.py to _plot/room.py and the
facade imports it from there; _plot/common.py now holds only shared
helpers and infrastructure.
- hearing/ and environmental/ package __init__ re-export the full public
surface of their renamed modules (threshold, rating) so the pre-move
package-path imports stay silent, as promised by the migration contract.
- SonarCloud cpd exclusion follows core.py to metrology/; llms.txt
regenerated; .git-blame-ignore-revs lists the move commits and the local
blame config registers it.
Full suite 2647 passed; conformance 194/194; golden baseline byte-stable;
import time unchanged vs main (1.09 s vs 1.17 s).
* fix: re-export the measurement surface on phonometry.environmental
Subagent-review finding: the generated environmental/__init__.py missed the
ISO 1996-2 measurement module (its facade import block was momentarily
path-corrupted when the subpackage exports were collected), so
phonometry.environmental.assess_tonal_audibility and 15 sibling names
raised AttributeError while every other domain namespace was complete. Adds
the missing re-exports (including EnvironmentalMeasurementWarning on the
domain namespace) and a new architecture invariant test asserting that
every name the facade imports from a domain submodule is reachable on that
subpackage, so this class of gap cannot recur.
* test: forbid absolute self-imports in the architecture check
Review feedback: the ast edge extraction only saw relative imports, so an
absolute 'from phonometry.x import y' inside the package would bypass the
cross-package whitelist. Such imports now fail the architecture test
directly (the codebase has none; relative imports are the convention).