-
Notifications
You must be signed in to change notification settings - Fork 2
Feature: add paris fomula to utils #35
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
base: develop
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,7 +1,7 @@ | ||
| imkar | ||
| ===== | ||
| imkar.utils | ||
| =========== | ||
|
|
||
| .. automodule:: imkar | ||
| .. automodule:: imkar.utils | ||
| :members: | ||
| :undoc-members: | ||
| :show-inheritance: |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,65 @@ | ||||||||||||
| """Utilities for imkar.""" | ||||||||||||
| import numpy as np | ||||||||||||
| import pyfar as pf | ||||||||||||
|
|
||||||||||||
|
|
||||||||||||
| def paris_formula(coefficients, incident_directions): | ||||||||||||
| r""" | ||||||||||||
| Calculate the random-incidence coefficient | ||||||||||||
| according to Paris formula. | ||||||||||||
ahms5 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
|
|
||||||||||||
| The implementation follows the Equation 2.53 from [#]_ and is | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please add a little general introduction? Something like: 'The Paris formula computes the ... It is valid for... and requires ...'. This is valid for scattering, absorption and generally all direction dependend energetic properties? |
||||||||||||
| discretized as: | ||||||||||||
|
|
||||||||||||
| .. math:: | ||||||||||||
| c_{rand} = \sum_{\Omega_S} c(\Omega_S) \cdot |\Omega_S \cdot n| \cdot w | ||||||||||||
|
|
||||||||||||
| with the ``coefficients`` :math:`c`, and the | ||||||||||||
| area weights :math:`w` from the ``incident_directions``. | ||||||||||||
ahms5 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
| :math:`|\Omega_S \cdot n|` represent the cosine of the angle between the | ||||||||||||
| surface normal and the incident direction. | ||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is very implicit. Can you please be more verbose here and also add a check for |
||||||||||||
|
|
||||||||||||
| .. note:: | ||||||||||||
| The incident directions should be | ||||||||||||
| equally distributed to get a valid result. | ||||||||||||
|
|
||||||||||||
| Parameters | ||||||||||||
| ---------- | ||||||||||||
| coefficients : pyfar.FrequencyData | ||||||||||||
| coefficients for different incident directions. Its cshape | ||||||||||||
| needs to be (..., n_incident_directions) | ||||||||||||
| incident_directions : pyfar.Coordinates | ||||||||||||
|
||||||||||||
| Defines the incidence directions of each `coefficients` in a | ||||||||||||
| Coordinates object. Its cshape needs to be (n_incident_directions). In | ||||||||||||
| sperical coordinates the radii needs to be constant. The weights need | ||||||||||||
| to reflect the area weights. | ||||||||||||
ahms5 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
|
|
||||||||||||
| Returns | ||||||||||||
| ------- | ||||||||||||
| random_coefficient : pyfar.FrequencyData | ||||||||||||
| The random-incidence scattering coefficient. | ||||||||||||
|
|
||||||||||||
| References | ||||||||||||
| ---------- | ||||||||||||
| .. [#] H. Kuttruff, Room acoustics, Sixth edition. Boca Raton: | ||||||||||||
| CRC Press/Taylor & Francis Group, 2017. | ||||||||||||
| """ | ||||||||||||
| if not isinstance(coefficients, pf.FrequencyData): | ||||||||||||
| raise ValueError("coefficients has to be FrequencyData") | ||||||||||||
| if not isinstance(incident_directions, pf.Coordinates): | ||||||||||||
|
||||||||||||
| raise ValueError("incident_directions have to be None or Coordinates") | ||||||||||||
ahms5 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||
| if incident_directions.cshape[0] != coefficients.cshape[-1]: | ||||||||||||
| raise ValueError( | ||||||||||||
| "the last dimension of coefficients needs be same as " | ||||||||||||
| "the incident_directions.cshape.") | ||||||||||||
|
|
||||||||||||
| theta = incident_directions.colatitude | ||||||||||||
| weight = np.cos(theta) * incident_directions.weights | ||||||||||||
| norm = np.sum(weight) | ||||||||||||
| coefficients_freq = np.swapaxes(coefficients.freq, -1, -2) | ||||||||||||
| random_coefficient = pf.FrequencyData( | ||||||||||||
| np.sum(coefficients_freq*weight/norm, axis=-1), | ||||||||||||
|
||||||||||||
| coefficients_freq = np.swapaxes(coefficients.freq, -1, -2) | |
| random_coefficient = pf.FrequencyData( | |
| np.sum(coefficients_freq*weight/norm, axis=-1), | |
| random_coefficient = pf.FrequencyData( | |
| np.sum(coefficients_freq*weight/norm, axis=-2), |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import pytest | ||
| import numpy as np | ||
| import pyfar as pf | ||
|
|
||
| from imkar import utils | ||
|
|
||
|
|
||
| @pytest.mark.parametrize("c_value", [ | ||
| (0), (0.2), (0.5), (0.8), (1)]) | ||
| @pytest.mark.parametrize("frequencies", [ | ||
| ([100, 200]), ([100])]) | ||
| def test_random_constant_coefficient( | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A little docstring or comments for this test would be nice.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest that all tests start with |
||
| c_value, frequencies): | ||
| incident_directions = pf.Coordinates.from_spherical_colatitude( | ||
| 0, np.linspace(0, np.pi/2, 10), 1) | ||
| incident_directions.weights = np.sin(incident_directions.colatitude) | ||
| shape = np.append(incident_directions.cshape, len(frequencies)) | ||
| coefficient = pf.FrequencyData(np.zeros(shape)+c_value, frequencies) | ||
| c_rand = utils.paris_formula(coefficient, incident_directions) | ||
| np.testing.assert_allclose(c_rand.freq, c_value) | ||
| assert c_rand.comment == 'random-incidence coefficient' | ||
|
|
||
|
|
||
| def test_random_non_constant_coefficient(): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. docstring and/or comments would be nice. |
||
| incident_directions = pf.samplings.sph_gaussian(10) | ||
| incident_directions = incident_directions[incident_directions.z >= 0] | ||
| incident_cshape = incident_directions.cshape | ||
| s_value = np.arange( | ||
| incident_cshape[0]).reshape(incident_cshape) / incident_cshape[0] | ||
| theta = incident_directions.colatitude | ||
| actual_weight = np.cos(theta) * incident_directions.weights | ||
| actual_weight /= np.sum(actual_weight) | ||
| coefficient = pf.FrequencyData(s_value.reshape((50, 1)), [100]) | ||
| c_rand = utils.paris_formula(coefficient, incident_directions) | ||
| desired = np.sum(s_value*actual_weight) | ||
| np.testing.assert_allclose(c_rand.freq, desired) | ||
|
|
||
|
|
||
| def test_paris_formula_coefficients_type_error(): | ||
| incident_directions = pf.samplings.sph_gaussian(10) | ||
| with pytest.raises( | ||
| ValueError, match="coefficients has to be FrequencyData"): | ||
| utils.paris_formula(np.zeros(10), incident_directions) | ||
|
|
||
|
|
||
| def test_paris_formula_incident_directions_type_error(): | ||
| coefficients = pf.FrequencyData(np.zeros((10, 1)), [100]) | ||
| with pytest.raises( | ||
| ValueError, | ||
| match="incident_directions have to be None or Coordinates"): | ||
| utils.paris_formula(coefficients, None) | ||
|
|
||
|
|
||
| def test_paris_formula_shape_mismatch_error(): | ||
| incident_directions = pf.samplings.sph_gaussian(10) | ||
| coefficients = pf.FrequencyData(np.zeros((5, 1)), [100]) | ||
| with pytest.raises( | ||
| ValueError, | ||
| match="the last dimension of coefficients needs be same as"): | ||
| utils.paris_formula(coefficients, incident_directions) | ||
Uh oh!
There was an error while loading. Please reload this page.