feat: underwater seabed reflection, ambient noise and ship-traffic source levels (#149)
* feat: underwater seabed reflection, ambient noise and ship-traffic source levels
Second half of the closed-form underwater propagation sub-phase (P1), building
on the transmission-loss/sound-speed/sonar core:
- seabed_reflection: fluid-fluid Rayleigh reflection coefficient (Medwin & Clay),
critical grazing angle arccos(c1/c2) and bottom loss BL = -20 lg|R|.
- ocean_ambient_noise: Wenz-framework spectrum levels, energy-summing wind noise
(rule of fives) and Mellen thermal noise, with an optional caller-supplied
shipping term.
- ship_traffic_noise: predicted ship source-level spectra from vessel class,
speed and length via JOMOPANS-ECHO (MacGillivray & de Jong 2021, default,
validated to the authors' File S1 reference calculator within 0.01 dB), plus
RANDI 3.1 and Wales & Heitmeyer (2002).
Each module exposes a frozen result dataclass with .plot(), a conformance check
with an independent oracle, one figure in four language/theme variants, and full
English docs plus EN/ES site guides. Conformance 179/179; full suite 2450 passed.
* refactor: address PR review feedback on underwater modules
- seabed_reflection: guard the c1 == c2 & grazing = 0 singular limit
(0/0 -> normal-incidence coefficient) so |R| never returns NaN; add test.
- ocean_ambient_noise: allow a calm sea (wind_speed_knots = 0) to return -inf
(zero energy contribution) rather than raising; add test.
- Migrate the three modules' input validation to the shared _validation helpers
(new require_positive_array enforces finite, positive, 1-D); removes
duplicated hand-rolled checks.
- Clarify units: source spectral-density is "dB re 1 µPa²/Hz at 1 m" (not the
ambiguous "/Hz m") in the plot label, figure, api-reference and docs; scope the
plane-wave reference note so it no longer claims to cover source levels; note
the wind-noise validity range (500 Hz-5 kHz) for the wide-range Wenz example.
- Label the wind/thermal component curves in the ambient-noise figure.
Kept the conformance thermal oracle's hardcoded physical constants (Boltzmann,
1 µPa) for clean-room independence from the module under test. Conformance
179/179; full suite 2452 passed.
* fix: SonarCloud float-equality and long-title docs-deploy failures
- seabed_reflection / ocean_ambient_noise: avoid float equality checks
(SonarCloud S1244). Detect the seabed 0/0 singular limit via np.isnan of the
result instead of comparing the denominator to 0.0; gate the calm-sea branch
with require_non_negative + a relational check.
- Shorten four site-guide titles so the rendered "<title> | phonometry" stays
within the 70-char html-validate limit (long-title), which was blocking the
docs deployment: the two underwater-propagation guides (EN/ES, lengthened in
this PR) and the two pre-existing wind-turbine-noise guides (EN/ES). The full
detail stays in each guide's description.
feat: underwater seabed reflection, ambient noise and ship-traffic source levels (#149)
* feat: underwater seabed reflection, ambient noise and ship-traffic source levels
Second half of the closed-form underwater propagation sub-phase (P1), building
on the transmission-loss/sound-speed/sonar core:
- seabed_reflection: fluid-fluid Rayleigh reflection coefficient (Medwin & Clay),
critical grazing angle arccos(c1/c2) and bottom loss BL = -20 lg|R|.
- ocean_ambient_noise: Wenz-framework spectrum levels, energy-summing wind noise
(rule of fives) and Mellen thermal noise, with an optional caller-supplied
shipping term.
- ship_traffic_noise: predicted ship source-level spectra from vessel class,
speed and length via JOMOPANS-ECHO (MacGillivray & de Jong 2021, default,
validated to the authors' File S1 reference calculator within 0.01 dB), plus
RANDI 3.1 and Wales & Heitmeyer (2002).
Each module exposes a frozen result dataclass with .plot(), a conformance check
with an independent oracle, one figure in four language/theme variants, and full
English docs plus EN/ES site guides. Conformance 179/179; full suite 2450 passed.
* refactor: address PR review feedback on underwater modules
- seabed_reflection: guard the c1 == c2 & grazing = 0 singular limit
(0/0 -> normal-incidence coefficient) so |R| never returns NaN; add test.
- ocean_ambient_noise: allow a calm sea (wind_speed_knots = 0) to return -inf
(zero energy contribution) rather than raising; add test.
- Migrate the three modules' input validation to the shared _validation helpers
(new require_positive_array enforces finite, positive, 1-D); removes
duplicated hand-rolled checks.
- Clarify units: source spectral-density is "dB re 1 µPa²/Hz at 1 m" (not the
ambiguous "/Hz m") in the plot label, figure, api-reference and docs; scope the
plane-wave reference note so it no longer claims to cover source levels; note
the wind-noise validity range (500 Hz-5 kHz) for the wide-range Wenz example.
- Label the wind/thermal component curves in the ambient-noise figure.
Kept the conformance thermal oracle's hardcoded physical constants (Boltzmann,
1 µPa) for clean-room independence from the module under test. Conformance
179/179; full suite 2452 passed.
* fix: SonarCloud float-equality and long-title docs-deploy failures
- seabed_reflection / ocean_ambient_noise: avoid float equality checks
(SonarCloud S1244). Detect the seabed 0/0 singular limit via np.isnan of the
result instead of comparing the denominator to 0.0; gate the calm-sea branch
with require_non_negative + a relational check.
- Shorten four site-guide titles so the rendered "<title> | phonometry" stays
within the 70-char html-validate limit (long-title), which was blocking the
docs deployment: the two underwater-propagation guides (EN/ES, lengthened in
this PR) and the two pre-existing wind-turbine-noise guides (EN/ES). The full
detail stays in each guide's description.
fix: deterministic documentation-figure rendering (end the flaky figures job) (#131)
* fix: make documentation-figure rendering deterministic across machines
The "Documentation figures up to date" CI job was intermittently failing on the
heavy compute figures (excitation_signals, multiple_shock, sii_vocal_efforts,
sottek_specific_loudness, tonality_roughness_demo): their multi-threaded
numerical backends reorder floating-point reductions depending on the available
core count, so the CI runner produced byte-different SVG/PNG output than the
committed (single-threaded) baseline, and a plain re-run usually "fixed" it.
Pin the numerical thread pools to a single thread so rendering is reproducible
regardless of the host core count:
- generate_graphs.py / generate_diagrams.py set OMP/MKL/OPENBLAS/NUMEXPR/NUMBA/
VECLIB thread counts to 1 before the numeric backends import (covers direct
invocation).
- The Makefile `graphs` target also exports those plus PYTHONHASHSEED=0 before
the interpreter starts (PYTHONHASHSEED cannot be set from within the process);
CI runs `make graphs`, so it inherits this with no workflow change.
Regenerating every figure under these settings produces a zero-byte diff against
the committed images, confirming the current baseline is already the
single-threaded canonical output — so no figure files change here.
* fix: compare documentation figures within tolerance, not byte-exact
The "Documentation figures up to date" job regenerated .github/images with
`make graphs` and required a byte-identical result. That is unachievable on
GitHub's hardware-heterogeneous runner fleet: the pinned software stack
computes a few plotted path coordinates ~1 ULP apart depending on which CPU
microarchitecture (SIMD kernel) the run lands on, so the job failed
intermittently even though the figures were numerically and visually
identical. Single-thread pinning removes intra-machine reduction races but
cannot remove this cross-CPU last-bit difference.
Replace the byte diff with scripts/check_figures.py, which compares the
freshly regenerated figures against the committed versions within a tolerance:
* SVG -- non-numeric structure (elements, text, colours, ordering) must match
exactly; every numeric token must agree within an absolute/relative
tolerance. A moved element or relabelled axis fails; a last-bit
coordinate wobble passes.
* PNG -- identical dimensions, at most a small number of pixels changed beyond
a level threshold (catches localised changes a global RMS dilutes),
and a bounded RMS (catches broad changes the pixel count misses).
* other -- exact byte compare. Added/removed files always fail.
The thread-pinning determinism work from the previous commit is retained (it
still removes genuine intra-machine flakiness); this makes the check robust to
the cross-machine floating-point non-determinism it cannot eliminate.
* fix: harden figure check against hash-id drift and orphan figures
Two robustness gaps found reviewing the tolerance-aware figure check:
* SVG clip-path / marker ids are SHA256 hashes of the underlying float
geometry (matplotlib backend_svg._make_id). A cross-CPU 1-ULP wobble in that
geometry avalanches the whole hash, so the id text would change and trip the
strict structural gate -- the very drift the check exists to tolerate. Now
canonicalise every id and its url(#...)/href="#..." references to
first-appearance placeholders before comparing, so equivalent documents match
regardless of hash text while a genuine id add/remove/reorder/repoint fails.
* The generators only overwrite files, never delete, so a figure that is no
longer produced would survive on disk byte-identical to HEAD and pass the
"committed figure no longer generated" check. Clear the generated SVG/PNG at
the start of the `graphs` target before regenerating (animations *.gif/*.webm
are from the separate `animations` target and are preserved). A full
clear+regenerate reproduces the committed tree exactly, confirming no
committed figure is orphaned.
feat: underwater seabed reflection, ambient noise and ship-traffic source levels (#149)
* feat: underwater seabed reflection, ambient noise and ship-traffic source levels
Second half of the closed-form underwater propagation sub-phase (P1), building
on the transmission-loss/sound-speed/sonar core:
- seabed_reflection: fluid-fluid Rayleigh reflection coefficient (Medwin & Clay),
critical grazing angle arccos(c1/c2) and bottom loss BL = -20 lg|R|.
- ocean_ambient_noise: Wenz-framework spectrum levels, energy-summing wind noise
(rule of fives) and Mellen thermal noise, with an optional caller-supplied
shipping term.
- ship_traffic_noise: predicted ship source-level spectra from vessel class,
speed and length via JOMOPANS-ECHO (MacGillivray & de Jong 2021, default,
validated to the authors' File S1 reference calculator within 0.01 dB), plus
RANDI 3.1 and Wales & Heitmeyer (2002).
Each module exposes a frozen result dataclass with .plot(), a conformance check
with an independent oracle, one figure in four language/theme variants, and full
English docs plus EN/ES site guides. Conformance 179/179; full suite 2450 passed.
* refactor: address PR review feedback on underwater modules
- seabed_reflection: guard the c1 == c2 & grazing = 0 singular limit
(0/0 -> normal-incidence coefficient) so |R| never returns NaN; add test.
- ocean_ambient_noise: allow a calm sea (wind_speed_knots = 0) to return -inf
(zero energy contribution) rather than raising; add test.
- Migrate the three modules' input validation to the shared _validation helpers
(new require_positive_array enforces finite, positive, 1-D); removes
duplicated hand-rolled checks.
- Clarify units: source spectral-density is "dB re 1 µPa²/Hz at 1 m" (not the
ambiguous "/Hz m") in the plot label, figure, api-reference and docs; scope the
plane-wave reference note so it no longer claims to cover source levels; note
the wind-noise validity range (500 Hz-5 kHz) for the wide-range Wenz example.
- Label the wind/thermal component curves in the ambient-noise figure.
Kept the conformance thermal oracle's hardcoded physical constants (Boltzmann,
1 µPa) for clean-room independence from the module under test. Conformance
179/179; full suite 2452 passed.
* fix: SonarCloud float-equality and long-title docs-deploy failures
- seabed_reflection / ocean_ambient_noise: avoid float equality checks
(SonarCloud S1244). Detect the seabed 0/0 singular limit via np.isnan of the
result instead of comparing the denominator to 0.0; gate the calm-sea branch
with require_non_negative + a relational check.
- Shorten four site-guide titles so the rendered "<title> | phonometry" stays
within the 70-char html-validate limit (long-title), which was blocking the
docs deployment: the two underwater-propagation guides (EN/ES, lengthened in
this PR) and the two pre-existing wind-turbine-noise guides (EN/ES). The full
detail stays in each guide's description.
Audit pass 6: split the three overloaded guides (#115)
* docs: split the three overloaded guides
Audit batch 6 - reorganization with full content preservation (verified
line-by-line against HEAD across the three surfaces):
- room-acoustics (1096 lines) keeps ISO 18233 + ISO 3382-1/2/3 + ISO 354
measurement; the building half (ISO 16283 + ISO 717, ISO 10140,
EN 12354-1/2, ISO 12999-1) moves to a new docs/building-acoustics.md
built from the site's existing split, resolving the repo/site
asymmetry. The flanking figure is reunited with its <details> code and
flanking_path is introduced before it is referenced (audit fixes), on
all three surfaces.
- psychoacoustics extracts STI/STIPA to speech-transmission.md
(paralleling speech-intelligibility.md) with STI-vs-SII disambiguation
boxes on the three pages involved; ISO 226 moves into psychoacoustics
where its perception content belongs, fixing its stale 'upcoming
feature' sentence.
- levels extracts occupational-exposure.md (ISO 9612) and
tone-prominence.md (ECMA-418-1), keeping Leq/LN/peak/SEL/Lden and the
spectrogram.
Indexes, sidebar, theory/guide cross-links (~40 edits in 19 files) and
the llms.txt generator PAGES list follow the moves; the four extracted
topics now survive into llms-full.txt instead of dropping out.
* docs: Spanish grammar fixes in the split guides
- 'función de conveniencia' instead of the false friend 'comodidad'
for convenience function (Gemini)
- plural agreement 'estrategias basadas' at the two flagged sites
(Gemini); the remaining 'basada' occurrences are correct singular
agreements ('medición basada en tareas', 'la estrategia basada...')
* docs: address second-round review on the splits
- flanking_path() gains its own parameter table (with the kij_min
Formula E.4 floor contract) in the building guide, all three surfaces
(CodeRabbit)
- the flanking figure snippet explicitly annotates its reuse of paths/
res from the snippet above; the speech-transmission figure snippet is
now fully self-contained (imports + fs) on all surfaces (CodeRabbit)
- ISO 10140-3 joins the laboratory standards footer (CodeRabbit)
- the Speech Intelligibility guide joins llms.txt (regenerated) and the
root README table (CodeRabbit)