Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes from_corner/extrude for mixed single/multi-band materials #388

Merged
merged 4 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ Bugfix
directivity to ``Room.add_microphone_array``, the directivity was dropped
from the object.

- Fixes issues #381: When creating a room with from_corners with multi-band
material, and then making it 3D with ``extrude`` with a single band material
for the floor and ceiling, an error would occur.
After the fix, the materials for floor and ceiling are automatically extended
to multi-band, as expected.

- Fixes issue #380: Caused by the attribute ``cartesian`` of ``GridSphere`` not
being set properly when the grid is only initialized with a number of points.

Expand All @@ -30,7 +36,7 @@ Bugfix
Changed
~~~~~~~

- Makes the ``pyroomacoustics.utilities.resample`` backend is made configurable
- Makes the ``pyroomacoustics.utilities.resample`` backend configurable
to avoid ``soxr`` dependency. The resample backend is configurable to
``soxr``, ``samplerate``, if these packages are available, and otherwise
falls back to ``scipy.signal.resample_poly``.
Expand Down
58 changes: 45 additions & 13 deletions pyroomacoustics/room.py
Original file line number Diff line number Diff line change
Expand Up @@ -1395,24 +1395,45 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None):
if libroom.area_2d_polygon(floor_corners) <= 0:
floor_corners = np.fliplr(floor_corners)

walls = []
wall_corners = {}
wall_materials = {}
for i in range(nw):
corners = np.array(
name = str(i)
wall_corners[name] = np.array(
[
np.r_[floor_corners[:, i], 0],
np.r_[floor_corners[:, (i + 1) % nw], 0],
np.r_[floor_corners[:, (i + 1) % nw], 0] + height * v_vec,
np.r_[floor_corners[:, i], 0] + height * v_vec,
]
).T
walls.append(
wall_factory(
corners,
self.walls[i].absorption,
self.walls[i].scatter,
name=str(i),

if len(self.walls[i].absorption) == 1:
# Single band
wall_materials[name] = Material(
energy_absorption=float(self.walls[i].absorption),
scattering=float(self.walls[i].scatter),
)
elif len(self.walls[i].absorption) == self.octave_bands.n_bands:
# Multi-band
abs_dict = {
"coeffs": self.walls[i].absorption,
"center_freqs": self.octave_bands.centers,
"description": "",
}
sca_dict = {
"coeffs": self.walls[i].scatter,
"center_freqs": self.octave_bands.centers,
"description": "",
}
wall_materials[name] = Material(
energy_absorption=abs_dict,
scattering=sca_dict,
)
else:
raise ValueError(
"Encountered a material with inconsistent number of bands."
)
)

############################
# BEGIN COMPATIBILITY CODE #
Expand Down Expand Up @@ -1477,12 +1498,23 @@ def extrude(self, height, v_vec=None, absorption=None, materials=None):
# we need the floor corners to ordered clockwise (for the normal to point outward)
new_corners["floor"] = np.fliplr(new_corners["floor"])

for key in ["floor", "ceiling"]:
# Concatenate new walls param with old ones.
wall_corners.update(new_corners)
wall_materials.update(materials)

# If some of the materials used are multi-band, we need to resample
# all of them to have the same number of values
if not Material.all_flat(wall_materials):
for name, mat in wall_materials.items():
mat.resample(self.octave_bands)

walls = []
for key, corners in wall_corners.items():
walls.append(
wall_factory(
new_corners[key],
materials[key].absorption_coeffs,
materials[key].scattering_coeffs,
corners,
wall_materials[key].absorption_coeffs,
wall_materials[key].scattering_coeffs,
name=key,
)
)
Expand Down
68 changes: 68 additions & 0 deletions pyroomacoustics/tests/test_from_corners_extrude.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import numpy as np
import pytest

import pyroomacoustics as pra

_FS = 16000
_MAX_ORDER = 3


def test_from_corner_extrude():

room_dim = [3, 4, 5]
# This test is sensitive to the selection of this value.
# If some symmetries are present, for some reasons differences between
# the two simulated methods happen.
src_loc = [1.001, 0.999, 1.002]
mic_loc = [2, 3, 4]
mat = pra.Material(energy_absorption=0.1)

room_ref = pra.ShoeBox(room_dim, fs=_FS, max_order=_MAX_ORDER, materials=mat)
room_ref.add_source(src_loc).add_microphone(mic_loc)
room_ref.compute_rir()

# Now construct the same room with the other set of primitives.
corners = np.array(
[[0, 0], [room_dim[0], 0], [room_dim[0], room_dim[1]], [0, room_dim[1]]]
).T
room = pra.Room.from_corners(corners, fs=_FS, max_order=_MAX_ORDER, materials=mat)
room.extrude(height=room_dim[2], materials=mat)
room.add_source(src_loc).add_microphone(mic_loc)
room.compute_rir()

assert np.allclose(room_ref.rir[0][0], room.rir[0][0], rtol=1e-4, atol=1e-4)


def test_from_corner_extrude_different_materials():

room_dim = [3, 4, 5]
# This test is sensitive to the selection of this value.
# If some symmetries are present, for some reasons differences between
# the two simulated methods happen.
src_loc = [1.001, 0.999, 1.002]
mic_loc = [2, 3, 4]
mat1 = "hard_surface"
mat2 = 0.1

materials = pra.make_materials(
east=mat1, west=mat1, south=mat1, north=mat1, floor=mat2, ceiling=mat2
)
room_ref = pra.ShoeBox(room_dim, fs=_FS, max_order=_MAX_ORDER, materials=materials)
room_ref.add_source(src_loc).add_microphone(mic_loc)
room_ref.compute_rir()

# Now construct the same room with the other set of primitives.
corners = np.array(
[[0, 0], [room_dim[0], 0], [room_dim[0], room_dim[1]], [0, room_dim[1]]]
).T
room = pra.Room.from_corners(
corners,
fs=_FS,
max_order=_MAX_ORDER,
materials=pra.Material(energy_absorption=mat1),
)
room.extrude(height=room_dim[2], materials=pra.Material(energy_absorption=mat2))
room.add_source(src_loc).add_microphone(mic_loc)
room.compute_rir()

assert np.allclose(room_ref.rir[0][0], room.rir[0][0], rtol=1e-4, atol=1e-4)
Loading