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).
Follow the ANSI/ASA S12.2-2019 5.2.2 NC procedure and tighten the room fiches (#354)
Rate NC spectra by the standard's two-step procedure (5.2.2): the speech
interference level (clause 3.2, the average of the 500/1000/2000/4000 Hz
octave-band levels) selects the NC-(SIL) curve and the spectrum is
designated NC-(SIL) unless an octave band exceeds it, in which case the
tangency method (5.2.3) applies. NCResult gains sil, tangency_rating,
method, out_of_range and a label property; the tangency rating stays
available on every in-family result. Spectra outside the NC-15 to NC-70
family of Table 1 are flagged out of range instead of clamping the
interpolation to fabricated NC-71/NC-14 designations: above the family
the governing band is the maximum exceedance over the NC-70 curve and the
label reads ">NC-70 (band)"; below it, "<NC-15". Fiche, plot and
verdict render the flagged designation in English and Spanish.
On the RC Mark II side, warn when the clause D.4 minimum band set
(31.5 Hz through 4000 Hz) is incomplete, flag ratings outside the
tabulated RC-25 to RC-50 family (Table D.1) with an extrapolation note on
the fiche, and document the combined RH tag as a diagnostic extension
beyond the clause D.3.5 letters (N, R, H, RV; the RV vibration/rattle
classification needs the Table 6 test and is not implemented).
On the ISO 3382-1/-2 fiche, label the mid-frequency descriptor honestly
for one-third-octave data (only the 500 Hz and 1 kHz one-third-octave
bands are averaged, so the octave "500-1000 Hz" claim is dropped and the
bands are named), name the band of a fallback T30/EDT descriptor when the
range does not span both mid bands, and choose the EDT_mid vs EDT label
from EDT's own band coverage instead of T30's. Fiche verdicts (ISO 3382
and S12.2) now compare display-rounded values so the printed numbers can
never contradict the verdict at a tolerance boundary.
Correct two citations in the room-acoustics docstrings: the 5 %
reverberation-time JND is ISO 3382-1:2009 Table A.1 (ISO 3382-2:2008
Table A.1 tabulates the uncertainty constants G and H) and the -20 dB
onset trigger is ISO 3382-1:2009 A.3.4, with a note on the at-edge
sample choice. English ISO 3382-3 strings use point decimals
(STI = 0.50/0.20); the Spanish strings keep the comma.
Docs (EN/ES), API reference, conformance check, changelog and the
affected example fiches are updated accordingly.
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).
Follow the ANSI/ASA S12.2-2019 5.2.2 NC procedure and tighten the room fiches (#354)
Rate NC spectra by the standard's two-step procedure (5.2.2): the speech
interference level (clause 3.2, the average of the 500/1000/2000/4000 Hz
octave-band levels) selects the NC-(SIL) curve and the spectrum is
designated NC-(SIL) unless an octave band exceeds it, in which case the
tangency method (5.2.3) applies. NCResult gains sil, tangency_rating,
method, out_of_range and a label property; the tangency rating stays
available on every in-family result. Spectra outside the NC-15 to NC-70
family of Table 1 are flagged out of range instead of clamping the
interpolation to fabricated NC-71/NC-14 designations: above the family
the governing band is the maximum exceedance over the NC-70 curve and the
label reads ">NC-70 (band)"; below it, "<NC-15". Fiche, plot and
verdict render the flagged designation in English and Spanish.
On the RC Mark II side, warn when the clause D.4 minimum band set
(31.5 Hz through 4000 Hz) is incomplete, flag ratings outside the
tabulated RC-25 to RC-50 family (Table D.1) with an extrapolation note on
the fiche, and document the combined RH tag as a diagnostic extension
beyond the clause D.3.5 letters (N, R, H, RV; the RV vibration/rattle
classification needs the Table 6 test and is not implemented).
On the ISO 3382-1/-2 fiche, label the mid-frequency descriptor honestly
for one-third-octave data (only the 500 Hz and 1 kHz one-third-octave
bands are averaged, so the octave "500-1000 Hz" claim is dropped and the
bands are named), name the band of a fallback T30/EDT descriptor when the
range does not span both mid bands, and choose the EDT_mid vs EDT label
from EDT's own band coverage instead of T30's. Fiche verdicts (ISO 3382
and S12.2) now compare display-rounded values so the printed numbers can
never contradict the verdict at a tolerance boundary.
Correct two citations in the room-acoustics docstrings: the 5 %
reverberation-time JND is ISO 3382-1:2009 Table A.1 (ISO 3382-2:2008
Table A.1 tabulates the uncertainty constants G and H) and the -20 dB
onset trigger is ISO 3382-1:2009 A.3.4, with a note on the at-edge
sample choice. English ISO 3382-3 strings use point decimals
(STI = 0.50/0.20); the Spanish strings keep the comma.
Docs (EN/ES), API reference, conformance check, changelog and the
affected example fiches are updated accordingly.
Follow the ANSI/ASA S12.2-2019 5.2.2 NC procedure and tighten the room fiches (#354)
Rate NC spectra by the standard's two-step procedure (5.2.2): the speech
interference level (clause 3.2, the average of the 500/1000/2000/4000 Hz
octave-band levels) selects the NC-(SIL) curve and the spectrum is
designated NC-(SIL) unless an octave band exceeds it, in which case the
tangency method (5.2.3) applies. NCResult gains sil, tangency_rating,
method, out_of_range and a label property; the tangency rating stays
available on every in-family result. Spectra outside the NC-15 to NC-70
family of Table 1 are flagged out of range instead of clamping the
interpolation to fabricated NC-71/NC-14 designations: above the family
the governing band is the maximum exceedance over the NC-70 curve and the
label reads ">NC-70 (band)"; below it, "<NC-15". Fiche, plot and
verdict render the flagged designation in English and Spanish.
On the RC Mark II side, warn when the clause D.4 minimum band set
(31.5 Hz through 4000 Hz) is incomplete, flag ratings outside the
tabulated RC-25 to RC-50 family (Table D.1) with an extrapolation note on
the fiche, and document the combined RH tag as a diagnostic extension
beyond the clause D.3.5 letters (N, R, H, RV; the RV vibration/rattle
classification needs the Table 6 test and is not implemented).
On the ISO 3382-1/-2 fiche, label the mid-frequency descriptor honestly
for one-third-octave data (only the 500 Hz and 1 kHz one-third-octave
bands are averaged, so the octave "500-1000 Hz" claim is dropped and the
bands are named), name the band of a fallback T30/EDT descriptor when the
range does not span both mid bands, and choose the EDT_mid vs EDT label
from EDT's own band coverage instead of T30's. Fiche verdicts (ISO 3382
and S12.2) now compare display-rounded values so the printed numbers can
never contradict the verdict at a tolerance boundary.
Correct two citations in the room-acoustics docstrings: the 5 %
reverberation-time JND is ISO 3382-1:2009 Table A.1 (ISO 3382-2:2008
Table A.1 tabulates the uncertainty constants G and H) and the -20 dB
onset trigger is ISO 3382-1:2009 A.3.4, with a note on the at-edge
sample choice. English ISO 3382-3 strings use point decimals
(STI = 0.50/0.20); the Spanish strings keep the comma.
Docs (EN/ES), API reference, conformance check, changelog and the
affected example fiches are updated accordingly.
feat: image-source room impulse responses and steady-state room field (#229)
* feat(room): image-source RIR and steady-state room field
Add two rectangular-room field models to phonometry.room.
image_source (A1) synthesises the room impulse response of a shoebox by
mirroring a point source in its walls (Kuttruff Room Acoustics 4.1,
Eqs. 4.4-4.6; Vorlander Auralization 11.4; Allen & Berkley 1979): the
direct sound plus one delayed, attenuated impulse per image, amplitude
prod(R_wall^count) * exp(-m r / 2) / (4 pi r) with the pressure reflection
factor R = sqrt(1 - alpha) and delay r / c. It returns a broadband or
per-octave-band RIR plus the exact sub-sample reflection table (times,
distances, orders, amplitudes, image positions), with audible_image_count
(Eq. 9.23) and reflection_density (Eq. 4.6) helpers and a reflectogram
.plot().
steady_field (A4) predicts the steady-state level
Lp = Lw + 10 lg(Q/(4 pi r^2) + 4/R) with the room constant
R = S alpha/(1 - alpha) (Bies Engineering Noise Control 6.43-6.44), the
critical distance sqrt(Q R / 16 pi) and the Schroeder frequency
2000 sqrt(T/V) (Kuttruff 3.44, 5.44). SteadyFieldResult carries the
direct/reverberant/total split versus distance with a .plot().
* test(room): image-source and steady-state field oracles
Pin the exact reflection geometry (direct and first-reflection delay and
amplitude), the audible image count (Kuttruff Eq. 9.23), the reflection
density (Eq. 4.6), per-band and per-wall assembly, and the domain guards.
Cross-check the Eyring reverberation time recovered from the synthetic
decay in the near-cubic limit, and use the 2D FDTD solver of
phonometry.simulation as an independent oracle for the rigid-wall echo
delay and the uniform-damping T60. Verify the closed-form room constant,
the critical distance as the direct/reverberant crossover, the Schroeder
frequency and the steady-state level.
* test(conformance): image-source and steady-state field checks
Add seven Room-acoustics anchors: the image-source direct-sound amplitude
and delay, the audible image count (Kuttruff Eq. 9.23), the reflection
density (Eq. 4.6), the room constant (Bies Eq. 6.44), the critical distance
as the direct/reverberant crossover, the Schroeder frequency (Kuttruff
Eq. 3.44) and the steady-state level (Bies Eq. 6.43). Register the two new
modules in the API taxonomy. 319/319 checks pass; the report regenerates
byte-identically.
* feat(figures): image-source reflectogram
Add the image-source room-impulse-response reflectogram of a 7x5x3 m room:
the direct sound with the reflection cloud coloured by reflection order,
decaying under the 1/r spreading envelope. Four language/theme variants;
Spanish strings added to the translation table. Committed as PNG (the dense
reflection cloud is far lighter than the vector form).
* docs: image-source and steady-state room field guide
New guide across the three documentation trees (GitHub docs, theory chapter
and the EN/ES site guides): the image-source room impulse response with its
reflection-count decomposition, the audible-image count and reflection
density, the reflectogram figure with its double snippet, and the
steady-state field with the room constant, critical distance and Schroeder
frequency. Cross-linked from the room-acoustics section pages and the
sidebar. Regenerates the Starlight API reference for the two new modules,
extends the curated API table, the README highlights, the bibliography
(Vorlander, Allen & Berkley) and the llms files, and records the change in
the changelog.
* fix: PR review round (Sonar gate and bot findings)
Sonar:
- S1244 (image_source.py): drop float == comparisons. The coincident
source/receiver guard and the fully-absorbing-wall mask now use <= 0.0
(both quantities are non-negative by construction, so the behaviour and
the pinned oracles are unchanged).
- S3776 (image_source_rir cognitive complexity): extract _resolve_band_maps,
_image_lattice, _image_amplitudes and _sample_ir with identical semantics;
the reflection count (1561 at order 10) and exact geometry are unchanged.
- S5778 (tests): move the array constructions out of the pytest.raises blocks.
Bot findings:
- Reflectogram renderer: guard the scatter and colorbar so a max_order=0
(direct-only) result does not crash on an empty scatter; regression test.
- reflection_density: clamp negative times so the t**2 term cannot report a
spurious positive density before the direct sound.
- steady_state_spl: rename the room-constant local to r_const to avoid
clashing with the critical distance rc.
- Tests: assert the per-band energy decay monotonically across all bands.
- Docs: language identifiers on the formula fences (MD040); describe T as the
Eyring reverberation time (not a rate) and the fitted slope as recovering
it; present the Schroeder frequency as a heuristic modal-to-diffuse
crossover rather than a hard cutoff; note the order-limited T30 in the
example. References sentence completed. llms regenerated.
* docs: correct the Bies 5th-edition year to 2017 in the bibliography