Add ISO 9053-1 static airflow-resistance report via .report() (#339)
* Add ISO 9053-1 static airflow-resistance report via .report()
Add a one-page PDF material airflow-resistance test report to
StaticAirflowResult, laid out like an accredited ISO 9053-1:2018
static/direct airflow-method certificate.
The fiche carries a standard-basis line, an optional metadata header
(client, manufacturer, specimen, specimen thickness, test facility,
date, climate), a two-panel body with a metrics table (the evaluation
velocity, the fitted pressure difference, the airflow resistance R, the
specific airflow resistance R_s, the airflow resistivity sigma when a
thickness is available, and the through-origin fit coefficients a and b)
beside the fitted pressure-drop curve, and a boxed specific airflow
resistance R_s with R and sigma alongside, all read at the clause 7.5
reference velocity of 0.5 mm/s. ISO 9053-1 is a material
characterisation, so the fiche carries no pass/fail verdict.
The renderer reuses the shared report layout and metadata container, and
renders in English and Spanish. A worked example is registered in the
report generator and its rendered fiche committed alongside the others.
* Document the plot dependency and use a language-neutral precision note
The static airflow-resistance fiche embeds the fitted curve, so state that
rendering needs both reportlab and matplotlib (phonometry[report,plot]) in the
English and Spanish guides and the materials doc. Describe the evaluation-velocity
precision as one decimal place rather than with a locale-specific separator, and
regenerate the API page.
* Extract the shared material-test fiche scaffold
The dynamic-stiffness (EN 29052-1 / ISO 9052-1) and static airflow-resistance
(ISO 9053-1) fiches share the same one-page shape: title and basis line,
metadata identity grid, a metrics table beside the result's self-scaling plot,
a boxed single-number result with extended terms, and the footer. Move that
common scaffold into a single _material_fiche helper (the content dataclass, the
identity-grid builder, the standard-basis-line helper and the renderer) and have
both fiches supply only their own labels, metric rows and boxed statement. The
rendered output is unchanged.
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).
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).
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).
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).
Correct fiche verdict rules and mandatory report items across the .report() layer (#281)
* fix(report): apply the current R 128 tolerance rules and the standards' mandatory report items across the fiches
The EBU R 128 programme-loudness fiche passed its verdict on a blanket
+/-0.5 LU about the target, the June 2014 (V3) rule that R 128 dropped in
its August 2020 revision. ProgramLoudnessResult.report() now selects the
tolerance explicitly: tolerance="qc" (default) applies the +/-0.2 LU
measurement-error allowance of item i) for loudness workflows such as
Quality Control, tolerance="live" applies the +/-1.0 LU tolerance of
item h) for programmes where the Target Level is not achievable
practically; the fiche prints the applied rule and its R 128 item, and the
verdict is evaluated on the loudness rounded to the displayed 0.1 LU
(Tech 3341 display precision), so the printed numbers can never contradict
it at the boundary. The committed example signal is trimmed 0.4 dB onto
the -23.0 LUFS target so it passes the QC rule it demonstrates.
Also fixed across the fiche layer:
- language="es" now reaches the embedded chart: the fiche language is
forwarded to the result's own plot(), so Spanish fiches embed Spanish
axis labels and legends instead of English ones.
- ISO 532-1: the fiche states the clause 7 mandatory items: the method
used (stationary, clause 5, or time-varying, clause 6), the sound field
(ZwickerLoudness gains a field attribute set by the constructors), the
loudness-versus-time function N(t) for time-varying sounds and the
"Maximum loudness Nmax" labelling of the reported maximum.
- ICAO Annex 16: effective_perceived_noise_level() gains
procedure="aeroplane"|"helicopter", plumbing the App. 2 4.3.1 Step 1
tone-correction start band (50 Hz for helicopters and tilt-rotors)
through the public chain; the verdict determines the EPNL once to one
decimal place, as the Annex states it, before the level, margin and
status are derived; the reference-conditions strip cites Part II
3.6.1.5 in full (25 C i.e. ISA + 10 C, 70 % RH, 1013.25 hPa, zero wind).
- ISO 717: the caption declares the band set actually rated (an
octave-band rating was captioned one-third-octave, against the clause
4.4 statement requirement); report() gains symbol= so field quantities
(DnT,w, L'nT,w, ...) are not mislabelled with the laboratory Rw / Ln,w
descriptor; adaptation terms print a sign only when negative, the style
of the standard's own examples.
- IEC 61260-1: verify_filter_class() reports range_limited (and per-band
checked_to_omega) when the stop-band mask extends beyond a band's
processing Nyquist, and the fiche qualifies its COMPLIES statement
accordingly; bands are labelled with nominal mid-band frequencies; the
1995 fiche cites that edition's relative-attenuation definition (3.13
Note); a required_class absent from the verified edition is rejected.
- ISO 11654: the shape indicator is written in the clause 5.3 style
(0.60(M), no space) and the 5.3 NOTE recommendation is printed when an
indicator applies.
- Header grids reprint client-supplied metadata verbatim (1.23 m2 no
longer becomes 1.2), values that round to zero never print -0.0, and
the remaining English fragments on the Spanish IEC 61260 fiche are
localised.
Every fiche gains at least one pypdf content test pinning the exact
displayed values, plus boundary tests for the display-rounded verdicts
and the helicopter start band.
* chore: regenerate example fiches, API reference pages and llms files
The example fiches pick up the corrected verdict rules and report items
(the programme-loudness example now demonstrates the default +/-0.2 LU QC
rule on target), and the generated API pages and llms files pick up the
new report keywords and result attributes.
* fix(report): integrate the fiche corrections with the field-report engine
The rebase onto the ISO 16283 fiches left two seams: the verdict strings were
added by both branches (duplicate i18n keys) and the new field renderer's
embedded-plot closure did not accept the language argument the layout now
forwards. Dedupe the table, accept the keyword, and re-verify the rendered
examples.
* fix(report): draw the LRA band as a pale opaque tint so the curves stay visible
The report pipeline renders through svglib, which drops alpha, so the
translucent LRA span came out as saturated orange and hid the momentary
trace. Use the 15 percent composite of the accent over white as an opaque
fill below the curves, matching the other fiches' band treatment.
* fix(report): keep fiche charts full width and legends tidy
Cap the relocated legend at three columns: a single four-entry row (the
impact fiches add the 500 Hz read marker) grew wider than the axes and
tight_layout shrank the chart, leaving the impact fiche visibly narrower
than its airborne sibling. Also compact the field-fiche y-axis labels to
the quantity symbol (the full name already heads the page and the table),
regenerate the affected examples, and drop the now-unused secondary color
import.
* review: remove float equality, reduce complexity and hoist raise-block setup
Sonar flagged the signed-zero normalisation in format_number and the
display rounding's zero check as float equality: normalise the sign by
inspecting the formatted text and compare the non-negative magnitude with
an order relation instead. Extract the per-band evaluation of
verify_filter_class and the loudness fiche's basis line into helpers to
bring both functions under the complexity threshold. Hoist the setup calls
out of four pytest.raises blocks, print the live tolerance through
format_number so it keeps one decimal, and round the loudness metrics row
with the same display rule as the boxed statement.
Add ISO 9053-1 static airflow-resistance report via .report() (#339)
* Add ISO 9053-1 static airflow-resistance report via .report()
Add a one-page PDF material airflow-resistance test report to
StaticAirflowResult, laid out like an accredited ISO 9053-1:2018
static/direct airflow-method certificate.
The fiche carries a standard-basis line, an optional metadata header
(client, manufacturer, specimen, specimen thickness, test facility,
date, climate), a two-panel body with a metrics table (the evaluation
velocity, the fitted pressure difference, the airflow resistance R, the
specific airflow resistance R_s, the airflow resistivity sigma when a
thickness is available, and the through-origin fit coefficients a and b)
beside the fitted pressure-drop curve, and a boxed specific airflow
resistance R_s with R and sigma alongside, all read at the clause 7.5
reference velocity of 0.5 mm/s. ISO 9053-1 is a material
characterisation, so the fiche carries no pass/fail verdict.
The renderer reuses the shared report layout and metadata container, and
renders in English and Spanish. A worked example is registered in the
report generator and its rendered fiche committed alongside the others.
* Document the plot dependency and use a language-neutral precision note
The static airflow-resistance fiche embeds the fitted curve, so state that
rendering needs both reportlab and matplotlib (phonometry[report,plot]) in the
English and Spanish guides and the materials doc. Describe the evaluation-velocity
precision as one decimal place rather than with a locale-specific separator, and
regenerate the API page.
* Extract the shared material-test fiche scaffold
The dynamic-stiffness (EN 29052-1 / ISO 9052-1) and static airflow-resistance
(ISO 9053-1) fiches share the same one-page shape: title and basis line,
metadata identity grid, a metrics table beside the result's self-scaling plot,
a boxed single-number result with extended terms, and the footer. Move that
common scaffold into a single _material_fiche helper (the content dataclass, the
identity-grid builder, the standard-basis-line helper and the renderer) and have
both fiches supply only their own labels, metric rows and boxed statement. The
rendered output is unchanged.
feat: porous-material models and multilayer absorber prediction (#220)
* feat: porous-material models and multilayer absorber prediction
Equivalent-fluid models (Delany-Bazley with the Bies Table D.1 presets,
Miki's positive-real revision, Johnson-Champoux-Allard), a transfer-matrix
multilayer solver over porous, air, perforated, microperforated (Maa's
exact Bessel impedance with the Eq. 5 end corrections) and limp-membrane
layers at any incidence angle with rigid/free/impedance terminations,
evaluated through a numerically robust admittance recursion that still
exposes the reciprocal chain matrix, the random-incidence Paris integral
with the locally reacting closed form (maximum 0.951), closed-form
Helmholtz/membrane resonance helpers, fit-range warnings and .plot() on
every result.
* test: printed-source oracles for the porous and multilayer models
Digit-exact anchors on Bies 5e Table D.1 / Mechel 2e G.11 and Miki 1990
Eqs. 30-34 (hand-digitized evaluation points), the Johnson et al. 1987
DC and high-frequency asymptotes, hard-backed and lossless-cavity closed
forms, oblique Snell refraction, energy/reciprocity and recursion-vs-
chain-matrix identities over random stacks, Maa 1998 (exact vs the
printed Eq. 4 approximation, Eq. 9/23 absorption identities, the Eq. 11
resonance condition, Table I and the Fig. 5 design example), the Paris
integral against direct quadrature and its 0.951 maximum, and the
round-trip through the ASTM E2611 TransferMatrix machinery.
* feat(conformance): porous and multilayer absorber checks
Ten additive checks: Delany-Bazley and Miki digitization points, the JCA
static viscous limit, the hard-backed TMM closed form, the lossless
quarter-wave cavity, the 0.951 statistical-absorption maximum, the
membrane resonance closed form, the Maa 1998 Fig. 5 / Cox Fig. 7.28
microperforated peak and the Eq. 5a/10 peak-absorption cross-check with
Maa's printed resistance approximation.
* docs(figures): multilayer absorber design comparison
Four 50 mm constructions (porous layer, microperforated panel + cavity,
perforated panel + porous + air, membrane + air + porous) predicted by
the transfer-matrix solver, with the closed-form Helmholtz and membrane
resonances marked; EN/ES, light/dark.
* docs: porous and multilayer absorbers guide (EN/ES), errata and indices
The three-tree guide (GitHub docs, site EN, site ES) with verified
snippets and the design-comparison figure, API-reference and taxonomy
rows, generated API page, sidebar and section-landing wiring, reference
entries, changelog, and three source misprints recorded in the errata
registry (Maa 1998 Eq. 5b; Attenborough & Van Renterghem 2021 Table 5.1
and Eq. 5.13).
* refactor: quality pass on porous-absorber module and tests
- Restructure the transfer-matrix solver into single-purpose helpers
(layer evaluation, termination admittance, admittance recursion, chain
matrix) and replace every float equality with ordering tests on
validated non-negative quantities.
- Evaluate the Mechel D.5 arctan term of statistical_absorption without
dividing by the exact-zero admittance imaginary part: switch to the
derived g2 -> 0 limit below a 1e-30 threshold (O(g2^2) truncation
error) and drop the errstate guard; pin the exactly real branch to the
classical closed form (8/xi)(1 + 1/(1+xi)) - (16/xi^2) ln(1+xi) and to
the reference Paris quadrature.
- Hoist auxiliary constructions out of pytest.raises blocks so each
block holds a single raising call.
- Deduplicate the open-area validation message and the frequency axis
label into module constants.
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).