Geometry drawings: to-scale device cross-sections for materials - #381
Conversation
New rendering module with drafting primitives (dimension lines with extension leaders, material fills, microphone/loudspeaker symbols, cross-section emblems) and six to-scale drawings: - Layered absorber stack (porous/air/perforated/MPP/membrane), with each layer dimensioned and the hole pattern of perforated plates drawn at its real pitch. - Square-section Helmholtz resonator. - One period of the slit metamaterial absorber (slow-sound panel). - QRD well profile from qrd_well_depths. - ISO 10534-2 impedance tube and ASTM E2611 transmission tube: side views at true scale with microphone positions, sample, termination, cross-section emblem and the plane-wave working range. API: layer dataclasses and HelmholtzResonator gain .plot(); LayeredAbsorberResult, SlitResonatorAbsorberResult, DiffuserPolarResponse, ImpedanceTubeResult and TransferMatrix gain .plot_geometry() with the defining geometry retained on the result (trailing optional fields, backward compatible); six public plot_*_geometry functions cover design-time drawing before any computation. All drawings are bilingual and validate inputs before touching matplotlib. 24 generated SVG variants, guide embeds (EN/ES and the GitHub mirrors) with runnable snippets, curated API table rows and generated API pages, and 16 new tests (smoke, retention round-trips, to-scale checks, refusal paths for hand-built results).
|
Warning Review limit reached
Next review available in: 19 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 Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds to-scale geometry plotting APIs for acoustic materials and measurement setups, stores geometry in solver results for redraws, exposes the helpers publicly, generates documentation figures, and adds validation and rendering tests. ChangesGeometry Visualization
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ 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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #381 +/- ##
==========================================
+ Coverage 96.82% 96.84% +0.01%
==========================================
Files 218 219 +1
Lines 30210 30615 +405
==========================================
+ Hits 29252 29649 +397
- Misses 958 966 +8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 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 `@scripts/generate_graphs.py`:
- Around line 7942-7944: Make the QRD geometry use a 3.6 m total width including
default fins: update the well-width calculation at
scripts/generate_graphs.py:7942-7944, then apply the same corrected calculation
and dimensions to docs/surface-scattering.md:225-227 and
site/src/content/docs/es/guides/surface-scattering.mdx:304-306, preserving the
runnable examples and translated text.
- Line 7893: Update the six figure generator save calls in
scripts/generate_graphs.py at lines 7893-7893, 7925-7925, 7946-7946, 7967-7967,
7988-7988, and 8009-8009 to write PNG source files instead of SVG files.
Regenerate the assets through the shared PNG-to-SVG pipeline and run
scripts/check_figures.py to verify the committed variants.
In `@site/src/content/docs/reference/api/materials/porous-absorber.md`:
- Around line 662-663: Update plot_absorber_stack in
site/src/content/docs/reference/api/materials/porous-absorber.md:662-663 and
plot_slit_absorber_geometry in
site/src/content/docs/reference/api/materials/slow-sound-absorber.md:265-266
consistently so general Sequence inputs are normalized and rendered
element-by-element, or narrow both public annotations to list | tuple to match
runtime behavior.
In `@src/phonometry/_plot/geometry.py`:
- Around line 730-780: Validate the optional diameter and sample_thickness
values in plot_impedance_tube_geometry before using them for geometry or labels,
rejecting any supplied value that is not strictly positive with a clear
ValueError. Preserve the existing nominal defaults when either argument is None,
and keep _nominal_bore responsible only for selecting the diameter fallback.
- Around line 386-392: Update all six public module-level renderer functions in
geometry.py, including plot_absorber_stack and the functions at the referenced
locations, to call check_language(language) before performing rendering or
delegating to translation helpers. Ensure direct exports validate language
identically to the existing .plot() and .plot_geometry() wrapper methods, while
preserving current rendering behavior for supported languages.
- Around line 386-452: Validate every layer in plot_absorber_stack, including
the _layer_kind_and_thickness dispatch, before creating a new axes. Preserve the
existing validation error for unsupported layer types, then create ax only after
validation succeeds and reuse the validated results during rendering.
In `@src/phonometry/materials/diffuser_design.py`:
- Around line 248-251: Update the DiffuserPolarResponse well_width docstring to
state that predictor-generated responses retain the supplied well width,
including explicit-reflection surfaces; only hand-built responses may have
well_width set to None. Keep the existing documentation about retaining depths
and periods for plot_geometry, while distinguishing that depths—not
well_width—is conditionally absent.
🪄 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 Plus
Run ID: 2658eb32-fe9d-4982-a97f-68d07ecaa016
⛔ Files ignored due to path filters (24)
.github/images/absorber_stack_geometry.svgis excluded by!**/*.svg.github/images/absorber_stack_geometry_dark.svgis excluded by!**/*.svg.github/images/absorber_stack_geometry_es.svgis excluded by!**/*.svg.github/images/absorber_stack_geometry_es_dark.svgis excluded by!**/*.svg.github/images/helmholtz_resonator_geometry.svgis excluded by!**/*.svg.github/images/helmholtz_resonator_geometry_dark.svgis excluded by!**/*.svg.github/images/helmholtz_resonator_geometry_es.svgis excluded by!**/*.svg.github/images/helmholtz_resonator_geometry_es_dark.svgis excluded by!**/*.svg.github/images/impedance_tube_geometry.svgis excluded by!**/*.svg.github/images/impedance_tube_geometry_dark.svgis excluded by!**/*.svg.github/images/impedance_tube_geometry_es.svgis excluded by!**/*.svg.github/images/impedance_tube_geometry_es_dark.svgis excluded by!**/*.svg.github/images/qrd_geometry.svgis excluded by!**/*.svg.github/images/qrd_geometry_dark.svgis excluded by!**/*.svg.github/images/qrd_geometry_es.svgis excluded by!**/*.svg.github/images/qrd_geometry_es_dark.svgis excluded by!**/*.svg.github/images/slit_absorber_geometry.svgis excluded by!**/*.svg.github/images/slit_absorber_geometry_dark.svgis excluded by!**/*.svg.github/images/slit_absorber_geometry_es.svgis excluded by!**/*.svg.github/images/slit_absorber_geometry_es_dark.svgis excluded by!**/*.svg.github/images/transmission_tube_geometry.svgis excluded by!**/*.svg.github/images/transmission_tube_geometry_dark.svgis excluded by!**/*.svg.github/images/transmission_tube_geometry_es.svgis excluded by!**/*.svg.github/images/transmission_tube_geometry_es_dark.svgis excluded by!**/*.svg
📒 Files selected for processing (24)
docs/api-reference.mddocs/materials.mddocs/porous-absorbers.mddocs/surface-scattering.mdscripts/api_taxonomy.pyscripts/generate_graphs.pysite/src/content/docs/es/guides/materials.mdxsite/src/content/docs/es/guides/porous-absorbers.mdxsite/src/content/docs/es/guides/surface-scattering.mdxsite/src/content/docs/guides/materials.mdxsite/src/content/docs/guides/porous-absorbers.mdxsite/src/content/docs/guides/surface-scattering.mdxsite/src/content/docs/reference/api/materials/diffuser-design.mdsite/src/content/docs/reference/api/materials/impedance-tube.mdsite/src/content/docs/reference/api/materials/porous-absorber.mdsite/src/content/docs/reference/api/materials/slow-sound-absorber.mdsrc/phonometry/__init__.pysrc/phonometry/_plot/geometry.pysrc/phonometry/materials/__init__.pysrc/phonometry/materials/diffuser_design.pysrc/phonometry/materials/impedance_tube.pysrc/phonometry/materials/porous_absorber.pysrc/phonometry/materials/slow_sound_absorber.pytests/materials/test_geometry_plots.py
…idation - The drawn N = 7 QRD keeps the published 3.6 m total width: an 85.7 mm pitch split into an 80.7 mm well plus a 5 mm fin (figure, snippets and alt texts updated). - plot_absorber_stack and plot_slit_absorber_geometry normalise any Sequence, matching their advertised contracts. - The six module-level renderers validate language and reject non-positive diameter/sample_thickness before creating any figure; layer-type dispatch moved ahead of axes creation. - DiffuserPolarResponse docstring: well_width/periods are always retained by the predictor; only depths distinguishes the explicit-reflection path.
Numerical conformance report✅ 427/427 conformance checks pass across 53 domains and 278 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% (10/10)
✅ Levels & dosimetry: 100% (6/6)
✅ Room acoustics: 100% (12/12)
✅ Psychoacoustics: 100% (12/12)
✅ Speech transmission (IEC 60268-16): 100% (10/10)
✅ System measurement (Golay / Kirkeby / Mueller-Massarani): 100% (5/5)
✅ Intensity & sound power: 100% (6/6)
✅ Room & building acoustics: 100% (52/52)
✅ 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% (14/14)
✅ 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)
✅ Impulsive-sound prominence (ISO/PAS 1996-3): 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% (6/6)
✅ 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% (20/20)
✅ Calibrated spectral analysis (Bendat & Piersol): 100% (12/12)
✅ Multiple-input coherence (Bendat & Piersol): 100% (5/5)
✅ Time-frequency analysis (Bendat & Piersol): 100% (3/3)
✅ Correlation, time delay and envelope (B&P / Knapp & Carter): 100% (7/7)
✅ Cepstrum, liftering and envelope spectrum (Havelock / B&P): 100% (3/3)
✅ Time synchronous averaging (McFadden 1987): 100% (5/5)
✅ Data qualification and Rice statistics (Bendat & Piersol): 100% (8/8)
✅ 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% (15/15)
✅ 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)
✅ Slow-sound perfect absorbers (Jimenez et al. Appl. Sci. 2017): 100% (3/3)
✅ 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% (7/7)
✅ Panel & aperture sound insulation (Bies / Hopkins / Cremer): 100% (11/11)
✅ Bending-wave plate-junction transmission (Cremer / Craik / Hopkins): 100% (6/6)
✅ Atmospheric refraction (Salomons rays / GFPE): 100% (3/3)
✅ Electroacoustics: 100% (6/6)
✅ Industrial noise control: 100% (9/9)
Tests & coverage — 33708 tests, 0 failures (✅ all green)
Conformance harness: |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/phonometry/_plot/geometry.py (1)
585-592: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winValidate resonator entries before creating axes.
A non-empty sequence containing an invalid item passes this block, then
_new_axes()runs before_draw_resonator()fails on missing resonator geometry. Validate each entry (and its dimensions) before Line 592.🤖 Prompt for 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. In `@src/phonometry/_plot/geometry.py` around lines 585 - 592, Update the resonator validation flow in the plotting function around the chain construction to validate every entry, including its required dimensions, before the ax is created at the ax is None branch. Reject invalid resonator objects early while preserving the existing empty-chain ValueError and valid-entry plotting behavior.
🤖 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/_plot/geometry.py`:
- Around line 787-792: Update both tube renderer validation blocks in
src/phonometry/_plot/geometry.py: the anchor site at lines 787-792 and the
sibling site at lines 881-886. Require every listed dimension to be finite as
well as positive—spacing, x1, optional diameter, and optional sample_thickness
in the first block; all distances, thickness, and optional diameter in the
second—so NaN and infinities are rejected before figure creation.
- Around line 581-584: Update the validation guarding the geometry setup to
reject slit_height values greater than period, while continuing to require
slit_height, lattice_step, and period to be positive. Ensure invalid values
raise ValueError before axes are created, preventing negative y_slit geometry.
---
Outside diff comments:
In `@src/phonometry/_plot/geometry.py`:
- Around line 585-592: Update the resonator validation flow in the plotting
function around the chain construction to validate every entry, including its
required dimensions, before the ax is created at the ax is None branch. Reject
invalid resonator objects early while preserving the existing empty-chain
ValueError and valid-entry plotting behavior.
🪄 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 Plus
Run ID: 75de2f52-0f5d-48a4-bbac-905953842208
⛔ Files ignored due to path filters (4)
.github/images/qrd_geometry.svgis excluded by!**/*.svg.github/images/qrd_geometry_dark.svgis excluded by!**/*.svg.github/images/qrd_geometry_es.svgis excluded by!**/*.svg.github/images/qrd_geometry_es_dark.svgis excluded by!**/*.svg
📒 Files selected for processing (7)
docs/surface-scattering.mdscripts/generate_graphs.pysite/src/content/docs/es/guides/surface-scattering.mdxsite/src/content/docs/guides/surface-scattering.mdxsrc/phonometry/_plot/geometry.pysrc/phonometry/materials/diffuser_design.pytests/materials/test_geometry_plots.py
| if slit_height <= 0.0 or lattice_step <= 0.0 or period <= 0.0: | ||
| raise ValueError( | ||
| "'slit_height', 'lattice_step' and 'period' must be positive." | ||
| ) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reject slit heights exceeding the panel period.
slit_height > period passes validation, making y_slit negative and drawing a cavity outside the panel. Require slit_height <= period before creating axes.
🤖 Prompt for 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.
In `@src/phonometry/_plot/geometry.py` around lines 581 - 584, Update the
validation guarding the geometry setup to reject slit_height values greater than
period, while continuing to require slit_height, lattice_step, and period to be
positive. Ensure invalid values raise ValueError before axes are created,
preventing negative y_slit geometry.
| if spacing <= 0.0 or x1 <= spacing: | ||
| raise ValueError("'spacing' must be positive and 'x1' > 'spacing'.") | ||
| if diameter is not None and diameter <= 0.0: | ||
| raise ValueError("'diameter' must be positive when given.") | ||
| if sample_thickness is not None and sample_thickness <= 0.0: | ||
| raise ValueError("'sample_thickness' must be positive when given.") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Require finite dimensions in both tube renderers. Positivity comparisons alone allow NaN through, so invalid geometry reaches figure creation.
src/phonometry/_plot/geometry.py#L787-L792: requirespacing,x1, optionaldiameter, and optionalsample_thicknessto be finite.src/phonometry/_plot/geometry.py#L881-L886: require all distances,thickness, and optionaldiameterto be finite.
📍 Affects 1 file
src/phonometry/_plot/geometry.py#L787-L792(this comment)src/phonometry/_plot/geometry.py#L881-L886
🤖 Prompt for 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.
In `@src/phonometry/_plot/geometry.py` around lines 787 - 792, Update both tube
renderer validation blocks in src/phonometry/_plot/geometry.py: the anchor site
at lines 787-792 and the sibling site at lines 881-886. Require every listed
dimension to be finite as well as positive—spacing, x1, optional diameter, and
optional sample_thickness in the first block; all distances, thickness, and
optional diameter in the second—so NaN and infinities are rejected before figure
creation.
…tionals - _dim: tolerance-based checks instead of float equality for the span and the offset. - Incident-sound label computed once per arrow. - Tube emblem shape extracted from the nested conditional. - Exception tests build their fixtures outside pytest.raises. - Regenerated API page for the DiffuserPolarResponse docstring.
|



Summary
Every materials device so far plotted only its spectra. This PR adds the other half: dimensioned, to-scale cross-section drawings of the physical devices, the way a lab manual would draw them. The geometry is what you build; the spectrum is what you measure.
What is drawn
layer.plot(),plot_absorber_stack(layers),LayeredAbsorberResult.plot_geometry()HelmholtzResonator.plot(),plot_helmholtz_resonator_geometry()plot_slit_absorber_geometry(),SlitResonatorAbsorberResult.plot_geometry()plot_qrd_geometry(depths, well_width),DiffuserPolarResponse.plot_geometry()plot_impedance_tube_geometry(),ImpedanceTubeResult.plot_geometry()plot_transmission_tube_geometry(),TransferMatrix.plot_geometry()The tube drawings place the microphones at their exact normative positions (ISO: mic 1 farther at x1; ASTM: l1/l2 from the front face), keep the three-diameter source margin of ISO 10534-2 4.3, draw the cross-section emblem (circle or square) at the same scale, and print the plane-wave working range those dimensions buy. Perforated plates draw their hole pattern at the real pitch derived from the open area.
Implementation notes
_plot/geometry.pywith shared drafting primitives (dimension lines with dashed extension leaders and a bar-ended tight mode for short spans, material fills, microphone/loudspeaker symbols). Domain classes referenced only under TYPE_CHECKING; layers dispatched by class name at runtime; matplotlib imported inside functions; inputs validated before any figure is created so an invalid call cannot leak an open figure.plot_geometry()with an actionable message.language="en"|"es") and works in both site themes.Verification
Summary by CodeRabbit
plot_geometry()methods for relevant results to redraw retained measurement/design geometry.