feat: atmospheric refraction by ray tracing and the parabolic equation#231
Conversation
Add phonometry.environmental.atmospheric_refraction, the refracting-atmosphere counterpart of the underwater numerical-propagation solvers. - Effective sound-speed profiles: linear c(z)=c0+g z and the logarithmic surface-layer profile c(z)=c0+b ln(1+z/z0) (Salomons Eq. 4.5). - atmospheric_ray_paths: Snell's-law ray tracing (RK4, range marching, ground reflection), returning curved paths, turning points, travel times and ground reflections; reuses the ray core of the ocean ray_trace. - ray_curvature_radius and shadow_zone_distance: closed forms for the linear gradient (exact circular arcs, upward-refraction shadow boundary). - atmospheric_parabolic_equation: the Green's Function PE (split-step Fourier, Gaussian starter with ground image, finite-impedance ground reflection, absorbing top layer), returning the relative-level field over range/height. In the homogeneous limit it reproduces the spherical-wave ground effect. Results expose .plot() renderers (profile, ray paths, relative-level field).
…cles Add three checks (ray turning height vs the circular-arc geometry, the GFPE relative level vs the exact spherical-wave ground effect over grassland, and vs the coherent two-ray field over a rigid ground) and place the module in the environmental section of the API taxonomy. Regenerate the report.
A single-concept, two-panel figure: the upward-refracting ray fan leaving a near-ground shadow, and the GFPE relative-level field over the range-height plane showing the same acoustic shadow. Rendered as PNG (the imshow field) in the four language x theme variants.
Add the nine public names to the quick-reference table and regenerate the Starlight API pages, sidebar and llms-full.txt.
Add the GitHub guide and the bilingual site guides (typed APA references, ThemeImage figure, dual-snippet figure code), index and sidebar entries, and the [Unreleased] changelog note.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds atmospheric refraction modeling with effective sound-speed profiles, ray tracing, a GFPE solver, result plotting, conformance checks, public exports, generated figures, and English/Spanish documentation. ChangesAtmospheric refraction
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Caller
participant Profile as EffectiveSoundSpeedProfile
participant RayModel as atmospheric_ray_paths
participant PEModel as atmospheric_parabolic_equation
participant Results
Caller->>Profile: create effective sound-speed profile
Profile->>RayModel: provide sampled speeds
RayModel->>Results: return ray paths and diagnostics
Profile->>PEModel: provide refracting profile
PEModel->>Results: return GFPE relative-level field
Results-->>Caller: evaluate or plot propagation results
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 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 a new module, phonometry.environmental.atmospheric_refraction, which implements ray tracing and a Green's Function Parabolic Equation (GFPE) solver to model sound propagation in a refracting atmosphere. It includes effective sound-speed profiles (linear and logarithmic), closed-form geometric calculations, validation tests, and comprehensive documentation. One review comment suggests optimizing the ray-tracing loop by performing the specular ground reflection check before calculating the travel time increment, which avoids clipping negative heights and improves accuracy.
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.
| # Travel time increment (dt/dr = 1/(xi c^2)) at the new height. | ||
| cc = _speed_at(np.clip(z, 0.0, None)) | ||
| ray_t[:, s] = ray_t[:, s - 1] + dr / (xi * cc**2) | ||
| # Specular ground reflection: fold z < 0 back up and flip zeta. | ||
| below = z < 0.0 | ||
| z = np.where(below, -z, z) | ||
| zeta = np.where(below, -zeta, zeta) |
There was a problem hiding this comment.
Evaluating the effective sound speed cc before the specular ground reflection clamps any negative intermediate heights to 0.0 via np.clip. Specular reflection maps z < 0 to -z (the actual physical height of the reflected ray). By performing the ground reflection check first, we can evaluate cc at the true physical height z without needing np.clip, which improves the accuracy of the travel time calculation.
| # Travel time increment (dt/dr = 1/(xi c^2)) at the new height. | |
| cc = _speed_at(np.clip(z, 0.0, None)) | |
| ray_t[:, s] = ray_t[:, s - 1] + dr / (xi * cc**2) | |
| # Specular ground reflection: fold z < 0 back up and flip zeta. | |
| below = z < 0.0 | |
| z = np.where(below, -z, z) | |
| zeta = np.where(below, -zeta, zeta) | |
| # Specular ground reflection: fold z < 0 back up and flip zeta. | |
| below = z < 0.0 | |
| z = np.where(below, -z, z) | |
| zeta = np.where(below, -zeta, zeta) | |
| # Travel time increment (dt/dr = 1/(xi c^2)) at the new height. | |
| cc = _speed_at(z) | |
| ray_t[:, s] = ray_t[:, s - 1] + dr / (xi * cc**2) | |
| bounces += below.astype(np.int_) |
There was a problem hiding this comment.
Applied in 3d63590: the ground reflection is now evaluated at the true folded height (-z, specular per Salomons) before sampling the effective sound speed, and the clip is gone. It improves the reflected-step travel time without affecting the trajectories; the free-field travel-time test is unchanged.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #231 +/- ##
========================================
Coverage 96.11% 96.12%
========================================
Files 135 136 +1
Lines 18041 18322 +281
========================================
+ Hits 17341 17612 +271
- Misses 700 710 +10 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/phonometry/environmental/atmospheric_refraction.py`:
- Around line 603-610: Update the nonpositive-range branch in the relative-level
field calculation to assign NaN instead of positive infinity. Preserve the
existing finite-range computation and ensure the resulting first column of
AtmosphericPEResult.relative_level and downstream
level_at_height/plot_atmospheric_pe usage receives NaN.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e2c169c0-503d-45b4-b636-42128d5aa5ee
⛔ Files ignored due to path filters (5)
.github/images/atmospheric_refraction.pngis excluded by!**/*.png.github/images/atmospheric_refraction_dark.pngis excluded by!**/*.png.github/images/atmospheric_refraction_es.pngis excluded by!**/*.png.github/images/atmospheric_refraction_es_dark.pngis excluded by!**/*.pngsite/src/generated/api-sidebar.mjsis excluded by!**/generated/**
📒 Files selected for processing (19)
CHANGELOG.mddocs/CONFORMANCE.mddocs/README.mddocs/api-reference.mddocs/atmospheric-refraction.mdllms-full.txtscripts/api_taxonomy.pyscripts/conformance_report.pyscripts/generate_graphs.pysite/astro.config.mjssite/src/content/docs/es/guides/atmospheric-refraction.mdxsite/src/content/docs/guides/atmospheric-refraction.mdxsite/src/content/docs/reference/api/environment/atmospheric-refraction.mdsite/src/content/docs/reference/api/index.mdsrc/phonometry/__init__.pysrc/phonometry/_plot/environmental.pysrc/phonometry/environmental/__init__.pysrc/phonometry/environmental/atmospheric_refraction.pytests/environmental/test_atmospheric_refraction.py
Numerical conformance report✅ 327/327 conformance checks pass across 41 domains and 201 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)
✅ Atmospheric refraction (Salomons rays / GFPE): 100% (3/3)
Tests & coverage — 23490 tests, 0 failures (✅ all green)
Conformance harness: |
…ction - ray_curvature_radius: reject a zero/non-finite gradient with abs(g) > 0 instead of a float equality test (Sonar S1244); physics unchanged. - _plot/environmental: hoist the duplicated 'Height [m]'/'Range [m]' axis labels into module constants (Sonar S1192). - atmospheric_ray_paths: evaluate the travel-time sound speed at the folded (true, non-negative) ray height after the ground reflection, dropping the np.clip and improving the reflection-step travel time. - atmospheric_parabolic_equation: store NaN (not +inf) in the singular source-range column so reductions and colormap autoscaling stay well-behaved.
|



Summary
The refracting-atmosphere counterpart of the underwater numerical solvers, for the environmental domain, clean-room from Salomons (Computational Atmospheric Acoustics) and Attenborough and Van Renterghem.
linear_sound_speed_profileandlog_linear_sound_speed_profile: effective sound-speed profiles (sound speed plus wind).atmospheric_ray_paths: Snell's-law ray tracing with ground reflection, turning points and travel times, plus the closed-formray_curvature_radiusandshadow_zone_distancefor a linear gradient.atmospheric_parabolic_equation: the Green's-function PE (GFPE) returning the relative-level field over range and height, with a finite-impedance ground, a Gaussian starter and its ground image, a refraction phase screen and an absorbing top layer.The submarine PE and ray cores are reused rather than duplicated: the GFPE is the same range-marching split-step family as the ocean solver, adapted for air with a full FFT and the plane-wave ground reflection in place of the pressure-release surface, and the ground impedance is resolved through the existing porous and ground-effect machinery.
Validation
In a homogeneous atmosphere the GFPE reproduces the independent Weyl-Van der Pol spherical ground effect to 0.10-0.13 dB over grassland from 50 to 1000 m, and the coherent two-ray +6 dB over hard ground to 0.44 dB. The ray curvature radius matches its closed form exactly, and source-receiver reciprocity holds to 0.25 dB. Three new conformance checks (327 total, byte-exact report).
Checks
ruff, mypy, bandit, full pytest (3851 passed, no new warnings), conformance byte-exact, figures within tolerance (856, four new variants), api-docs with no drift, site i18n parity and full build clean. Adversarially reviewed with no bugs found.
Summary by CodeRabbit
New Features
Documentation
Tests