feat: theoretical panel sound reduction, radiation efficiency and point mobilities#233
Conversation
There was a problem hiding this comment.
Sorry @jmrplens, your pull request is larger than the review limit of 150000 diff characters
|
Warning Review limit reached
Next review available in: 36 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (5)
📒 Files selected for processing (40)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces theoretical panel sound insulation predictions and plate radiation efficiency/theoretical point mobilities calculations, including implementations for single-panel mass law and coincidence, double-wall resonance, slit/aperture transmission, and infinite structure point mobilities. Feedback from the review highlights opportunities to improve numerical stability in slit transmission calculations—specifically by reformulating the coefficient to avoid division by zero and adding defensive checks during resonance frequency iterations—as well as a performance optimization to conditionally compute double-wall transmission loss only on required frequency subsets.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| numerator = m * k_big * cos_ke**2 | ||
| denominator = 2.0 * n**2 * ( | ||
| np.sin(kx_2ke) ** 2 / cos_ke**2 | ||
| + (k_big**2 / (2.0 * n**2)) * (1.0 + np.cos(kx) * np.cos(kx_2ke)) | ||
| ) | ||
| tau = numerator / denominator |
There was a problem hiding this comment.
The current implementation of the straight-edged slit transmission coefficient divides by cos_ke**2. If cos_ke is zero or extremely close to zero, this will result in a division-by-zero warning or error, and produce NaN values.
By multiplying both the numerator and denominator by cos_ke**2, we can reformulate the expression to be mathematically identical but numerically stable, completely avoiding any division by cos_ke.
| numerator = m * k_big * cos_ke**2 | |
| denominator = 2.0 * n**2 * ( | |
| np.sin(kx_2ke) ** 2 / cos_ke**2 | |
| + (k_big**2 / (2.0 * n**2)) * (1.0 + np.cos(kx) * np.cos(kx_2ke)) | |
| ) | |
| tau = numerator / denominator | |
| numerator = m * k_big * cos_ke**4 | |
| denominator = 2.0 * n**2 * ( | |
| np.sin(kx_2ke) ** 2 | |
| + (k_big**2 / (2.0 * n**2)) * cos_ke**2 * (1.0 + np.cos(kx) * np.cos(kx_2ke)) | |
| ) | |
| tau = numerator / denominator |
There was a problem hiding this comment.
Applied in 4d8e0ab: the slit transmission is reformulated by multiplying numerator and denominator by cos^2(Ke), so it stays finite where cos(Ke) crosses zero across a full 50-10000 Hz sweep (previously NaN/inf). Tau is identical to 2.5e-16 on the oracle grid; the slit-resonance conformance value is byte-exact.
| for _ in range(50): | ||
| k_big = (2.0 * np.pi * f / c0) * w | ||
| e = (np.log(8.0 / k_big) - _EULER_GAMMA) / np.pi | ||
| f_new = z * c0 / (2.0 * (d + 2.0 * e * w)) |
There was a problem hiding this comment.
If the slit width w is very large relative to its depth d, the effective slit depth d + 2.0 * e * w can become negative or zero during the fixed-point iteration. This causes f_new to become negative, which leads to a negative k_big in the next iteration and causes np.log to raise a ValueError or return nan.
Adding a defensive check to ensure the denominator remains positive prevents this numerical instability and provides a clear error message to the user.
| f_new = z * c0 / (2.0 * (d + 2.0 * e * w)) | |
| denom = d + 2.0 * e * w | |
| if denom <= 0.0: | |
| raise ValueError( | |
| "Slit width is too large relative to depth; " | |
| "resonance cannot be computed." | |
| ) | |
| f_new = z * c0 / (2.0 * denom) |
There was a problem hiding this comment.
Applied in 4d8e0ab: a clear ValueError is raised when the effective depth d+2e is non-positive (a slit far wider than deep, outside the Gomperts w<<lambda model), instead of returning NaN; normal narrow-slit resonances are unchanged, with a regression test.
| tl1 = _ml(f, m1) | ||
| tl2 = _ml(f, m2) | ||
| k = 2.0 * np.pi * f / c0 | ||
| tl[mid] = tl1[mid] + tl2[mid] + 20.0 * np.log10(2.0 * k[mid] * d) | ||
| tl[high] = tl1[high] + tl2[high] + 6.0 | ||
| return SoundReductionResult( |
There was a problem hiding this comment.
Currently, tl1 and tl2 are computed for the entire frequency array f, even though they are only used for the mid and high subsets.
To improve performance and avoid unnecessary floating-point operations (including np.log10 calculations), we can conditionally compute _ml only on the required subsets of f when mid or high contains elements.
tl[below] = _ml(f[below], m1 + m2)
if np.any(mid):
f_mid = f[mid]
k_mid = 2.0 * np.pi * f_mid / c0
tl[mid] = _ml(f_mid, m1) + _ml(f_mid, m2) + 20.0 * np.log10(2.0 * k_mid * d)
if np.any(high):
f_high = f[high]
tl[high] = _ml(f_high, m1) + _ml(f_high, m2) + 6.0There was a problem hiding this comment.
Declined with evidence: the frequency arrays are fixed-size band centres (about 16-21 elements), so the saved log10 calls are nanoseconds; the full-array form is correct (already verified by the earlier mask fix) and clearer. No correctness or meaningful performance benefit.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #233 +/- ##
==========================================
- Coverage 96.12% 96.10% -0.03%
==========================================
Files 136 140 +4
Lines 18322 18760 +438
==========================================
+ Hits 17612 18029 +417
- Misses 710 731 +21 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Numerical conformance report✅ 338/338 conformance checks pass across 42 domains and 210 standards - filters class 1 - weightings within IEC 61672-1 class 1. Each row pins a standard clause to its expected normative value and the value the library computes. Every section below is collapsible and stays collapsed while all of its rows pass; a section with any failing row opens automatically. ✅ Numerical validation - filters & weightings: class showcase (IEC 61260-1 · IEC 61672-1 · ISO 7196)IEC 61260-1:2014 class per filter architecture (order 6, one-third-octave, 100 Hz-10 kHz, fs = 48 kHz). For each architecture the table shows, at its binding band, the measured relative attenuation and the class-1 limit it must clear, so the number and the range it must sit in are both visible. A positive margin means the acceptance limits are met with that much room.
Only Butterworth (the library default) and Chebyshev-II are class-compliant architectures. Chebyshev-I and elliptic trade the mask for passband ripple, and Bessel for a maximally-flat group delay (soft rolloff); they cannot satisfy the IEC 61260-1 Class 1/2 attenuation mask by construction, so they are labelled By design - this is expected, not a failure or regression. Frequency-weighting conformance (A/C: IEC 61672-1 Table 3; G: ISO 7196 A.3). The max deviation from nominal is informational (it falls at a frequency extreme where the tolerance is widest and asymmetric); compliance is judged at the binding frequency - the one with the least headroom - where the deviation, the applicable tolerance band and the headroom are shown together.
✅ Filters & weightings: 100% (7/7)
✅ Levels & dosimetry: 100% (6/6)
✅ Room acoustics: 100% (12/12)
✅ Psychoacoustics: 100% (12/12)
✅ Speech transmission (IEC 60268-16): 100% (9/9)
✅ Intensity & sound power: 100% (4/4)
✅ Room & building acoustics: 100% (50/50)
✅ Building prediction & uncertainty: 100% (15/15)
✅ Outdoor propagation & occupational exposure: 100% (10/10)
✅ Materials: absorption, airflow & impedance: 100% (6/6)
✅ Scattering & diffusion (ISO 17497): 100% (5/5)
✅ In-situ road absorption (ISO 13472): 100% (3/3)
✅ Precision sound power (ISO 3745 / 9614-3): 100% (4/4)
✅ Human vibration (ISO 8041 / 2631 / 5349): 100% (15/15)
✅ Speech intelligibility (ANSI S3.5-1997): 100% (7/7)
✅ Objective intelligibility (STOI / ESTOI): 100% (3/3)
✅ Impulsive-sound prominence (NT ACOU 112): 100% (2/2)
✅ Room noise (ANSI S12.2-2019): 100% (3/3)
✅ Hearing threshold (ISO 7029 / ISO 389-7): 100% (3/3)
✅ Measurement uncertainty (GUM / Supplement 1): 100% (7/7)
✅ Noise-induced hearing loss (ISO 1999): 100% (3/3)
✅ Multiple-shock whole-body vibration (ISO 2631-5): 100% (6/6)
✅ Sound absorption in enclosed spaces (EN 12354-6): 100% (2/2)
✅ Prominent discrete tones (ECMA-418-1): 100% (2/2)
✅ Tonal audibility (ISO/PAS 20065): 100% (11/11)
✅ Psychoacoustic annoyance & fluctuation strength (Fastl & Zwicker): 100% (3/3)
✅ Electroacoustics: distortion & frequency response: 100% (14/14)
✅ Calibrated spectral analysis (Bendat & Piersol): 100% (6/6)
✅ Correlation, time delay and envelope (B&P / Knapp & Carter): 100% (7/7)
✅ Underwater acoustics (ISO 18405/17208/18406): 100% (6/6)
✅ Underwater sound propagation (transmission loss): 100% (15/15)
✅ Underwater numerical propagation (modes / rays / PE): 100% (4/4)
✅ Aircraft noise (ICAO Annex 16 / IEC 61265): 100% (14/14)
✅ Rotorcraft noise (ECAC Doc 32 / NORAH2): 100% (12/12)
✅ Wind-turbine noise (IEC 61400-11): 100% (3/3)
✅ Porous & multilayer absorbers (Mechel / Bies / Cox & D'Antonio): 100% (10/10)
✅ Program loudness (ITU-R BS.1770 / EBU R 128): 100% (8/8)
✅ 2D FDTD wave simulation (Attenborough & Van Renterghem 2021, Ch. 4): 100% (2/2)
✅ Swept-sine distortion & phase utilities (Farina / Novak): 100% (7/7)
✅ Spherical ground & barriers (Attenborough / Salomons / Bies): 100% (6/6)
✅ Panel & aperture sound insulation (Bies / Hopkins / Cremer): 100% (11/11)
✅ Atmospheric refraction (Salomons rays / GFPE): 100% (3/3)
Tests & coverage — 23934 tests, 0 failures (✅ all green)
Conformance harness: |
- 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.
…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.
…PI 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.
- 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.
4d8e0ab to
e753a33
Compare
|



Summary
Theoretical building-acoustics prediction for the building and vibration domains, clean-room from Bies, Hansen and Howard, Hopkins (Sound Insulation) and Cremer, Heckl and Petersson.
coincidence_frequencyand the Leppington/Maidanikradiation_efficiency, exposing the radiation factor for the existing sound-power-from-vibration path without changing its API..rating()per ISO 717-1 and.plot().The double wall's porous fill reuses the materials-domain porous model; nothing depends on the parallel noise-control work.
Validation
Radiation efficiency matches Hopkins Fig. 2.65a point for point across the spectrum and the coincidence peak; the single-panel 6 mm glass matches Hopkins Fig. 4.8 with Rw 32 dB. Closed forms are exact: mass law 6.02 dB per octave and per mass-doubling, the coincidence frequency, the mass-spring-mass resonance (76.9 Hz), the double wall reducing to the total-mass law below resonance, the slit resonance, and a 1 percent open area capping the composite at 20 dB. Eleven new conformance checks (335 total, byte-exact report).
Review found and fixed a real edge case where the double-wall frequency-band masks overlapped for lightweight leaves with a wide gap; normal-case continuity is unchanged.
Note
While validating, a pre-existing factor-of-pi error was found in the unrelated
building.critical_frequencyhelper (flanking module): it under-reads by pi and its only test re-derives the same formula. This PR does not use or modify it (the newcoincidence_frequencyis a correct independent implementation); it is flagged for a separate fix because correcting it changes EN 12354 flanking outputs.Checks
ruff, mypy, bandit, full pytest (3895 passed, no new warnings), conformance byte-exact, figures within tolerance (856, four new variants), api-docs with no drift, curated API table 749 names, site i18n parity and full build clean. Adversarially reviewed.