From be0269c799668aef6640e372676214f3376bf603 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 08:56:38 +0200 Subject: [PATCH 01/35] some changes --- src/waveresponse/_core.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 24d3389..36a5f6c 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2310,16 +2310,24 @@ def calculate_response( """ Calculate response spectrum. + The response spectrum is calculated according to: + + S_x(w, theta) = H(w, theta) * H*(w, theta) * + Parameters ---------- - rao : obj - Response amplitude operator (RAO) as a :class:`~waveresponse.RAO` object. - wave : obj - 2-D wave spectrum as a :class:`~waveresponse.WaveSpectrum` object. + rao : RAO + Response amplitude operator (RAO). + wave : WaveSpectrum or WaveBinSpectrum + 2-D wave spectrum. heading : float Heading of vessel relative to wave spectrum coordinate system. heading_degrees : bool Whether the heading is given in 'degrees'. If ``False``, 'radians' is assumed. + reshape : {'rao', 'rao_squared'}, default 'rao_squared' + Determines whether to reshape the RAO or the squared RAO before pairing with + the wave spectrum. Linear interpolation is performed to match the frequency + and direction coordinates of the wave spectrum. coord_freq : str, optional Frequency coordinates for interpolation. Should be 'wave' or 'rao'. Determines if it is the wave spectrum or the RAO that should dictate which frequencies From c1dcf2ce4384cf42ae7dff633b3b62c5d508da5d Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 08:59:48 +0200 Subject: [PATCH 02/35] docstring --- src/waveresponse/_core.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 36a5f6c..3eba86d 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2312,7 +2312,11 @@ def calculate_response( The response spectrum is calculated according to: - S_x(w, theta) = H(w, theta) * H*(w, theta) * + ``S_x(w, theta) = H(w, theta) * H*(w, theta) * S_w(w, theta)`` + + where ``S_x(w, theta)`` is the response spectrum, ``H(w, theta)`` is the RAO, + ``H*(w, theta)`` is the conjugate of the RAO, and ``S_w(w, theta)`` is the wave + spectrum. Parameters ---------- From 81f954df04e2f85e7fc0a1509094f27de2efdf08 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 09:07:21 +0200 Subject: [PATCH 03/35] docstring --- src/waveresponse/_core.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 3eba86d..79530a6 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2305,7 +2305,7 @@ def dirm(self, degrees=None): def calculate_response( - rao, wave, heading, heading_degrees=False, coord_freq="wave", coord_dirs="wave" + rao, wave, heading, heading_degrees=False, reshape="rao_squared", coord_freq=None, coord_dirs=None ): """ Calculate response spectrum. @@ -2318,6 +2318,10 @@ def calculate_response( ``H*(w, theta)`` is the conjugate of the RAO, and ``S_w(w, theta)`` is the wave spectrum. + The frequency and direction coordinates are dictatated by the wave spectrum. + The RAO (or the squared verison of it) is interpolated to match the grid coordinates + of the wave spectrum. + Parameters ---------- rao : RAO @@ -2333,24 +2337,26 @@ def calculate_response( the wave spectrum. Linear interpolation is performed to match the frequency and direction coordinates of the wave spectrum. coord_freq : str, optional - Frequency coordinates for interpolation. Should be 'wave' or 'rao'. Determines - if it is the wave spectrum or the RAO that should dictate which frequencies - to use in response calculation. The other object will be interpolated to - match these frequencies. + Deprecated; use `reshape` instead. Frequency coordinates for interpolation. + Should be 'wave' or 'rao'. Determines if it is the wave spectrum or the + RAO that should dictate which frequencies to use in response calculation. + The other object will be interpolated to match these frequencies. coord_dirs : str, optional - Direction coordinates for interpolation. Should be 'wave' or 'rao'. Determines - if it is the wave spectrum or the RAO that should dictate which directions - to use in response calculation. The other object will be interpolated to - match these directions. + Deprecated; use `reshape` instead. Direction coordinates for interpolation. + Should be 'wave' or 'rao'. Determines if it is the wave spectrum or the + RAO that should dictate which directions to use in response calculation. + The other object will be interpolated to match these directions. Returns ------- - obj : - Response spectrum as :class:`DirectionalSpectrum` object. + DirectionalSpectrum or DirectionalBinSpectrum : + Response spectrum. """ wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) + if + if coord_freq.lower() == "wave": freq = wave_body._freq elif coord_freq.lower() == "rao": From 6f73e34d3930f09f50c35ce61cef606e8df79d4b Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 09:20:01 +0200 Subject: [PATCH 04/35] fix calculate_response --- src/waveresponse/_core.py | 64 +++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 79530a6..bfd29e4 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2355,27 +2355,53 @@ def calculate_response( wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) - if - - if coord_freq.lower() == "wave": - freq = wave_body._freq - elif coord_freq.lower() == "rao": - freq = rao._freq - else: - raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") - - if coord_dirs.lower() == "wave": - dirs = wave_body._dirs - elif coord_dirs.lower() == "rao": - dirs = rao._dirs + # if coord_freq is not None and coord_dirs is not None: + # warnings.warn( + # "The `coord_freq` and `coord_dirs` parameters are deprecated. Use the `reshape` parameter instead.", + # DeprecationWarning, + # ) + # if coord_freq.lower() == "wave": + # freq = wave_body._freq + # elif coord_freq.lower() == "rao": + # freq = rao._freq + # else: + # raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") + + # if coord_dirs.lower() == "wave": + # dirs = wave_body._dirs + # elif coord_dirs.lower() == "rao": + # dirs = rao._dirs + # else: + # raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") + # elif coord_freq is not None or coord_dirs is not None: + # raise ValueError( + # "Both `coord_freq` and `coord_dirs` must be provided together, or neither should be provided." + # ) + + # if coord_freq.lower() == "wave": + # freq = wave_body._freq + # elif coord_freq.lower() == "rao": + # freq = rao._freq + # else: + # raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") + + # if coord_dirs.lower() == "wave": + # dirs = wave_body._dirs + # elif coord_dirs.lower() == "rao": + # dirs = rao._dirs + # else: + # raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") + + if reshape.lower() == "rao": + rao = rao.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) + rao_squared = (rao * rao.conjugate()).real + elif reshape.lower() == "rao_squared": + rao_squared = (rao * rao.conjugate()).real + rao_squared = rao_squared.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) else: - raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") - - rao_squared = (rao * rao.conjugate()).real - rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) - wave_body = wave_body.reshape(freq, dirs, freq_hz=False, degrees=False) + raise ValueError("Invalid `reshape` value. Should be 'rao' or 'rao_squared'.") - return multiply(rao_squared, wave_body, output_type="directional_spectrum") + return multiply(rao_squared, wave_body, output_type=type(wave_body)) class BaseSpreading(ABC): From da9dea5f377a432f40db224ec2807e5619209286 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 09:27:12 +0200 Subject: [PATCH 05/35] delete commented out coe --- src/waveresponse/_core.py | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index bfd29e4..acbd7ea 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2355,43 +2355,6 @@ def calculate_response( wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) - # if coord_freq is not None and coord_dirs is not None: - # warnings.warn( - # "The `coord_freq` and `coord_dirs` parameters are deprecated. Use the `reshape` parameter instead.", - # DeprecationWarning, - # ) - # if coord_freq.lower() == "wave": - # freq = wave_body._freq - # elif coord_freq.lower() == "rao": - # freq = rao._freq - # else: - # raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") - - # if coord_dirs.lower() == "wave": - # dirs = wave_body._dirs - # elif coord_dirs.lower() == "rao": - # dirs = rao._dirs - # else: - # raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") - # elif coord_freq is not None or coord_dirs is not None: - # raise ValueError( - # "Both `coord_freq` and `coord_dirs` must be provided together, or neither should be provided." - # ) - - # if coord_freq.lower() == "wave": - # freq = wave_body._freq - # elif coord_freq.lower() == "rao": - # freq = rao._freq - # else: - # raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") - - # if coord_dirs.lower() == "wave": - # dirs = wave_body._dirs - # elif coord_dirs.lower() == "rao": - # dirs = rao._dirs - # else: - # raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") - if reshape.lower() == "rao": rao = rao.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) rao_squared = (rao * rao.conjugate()).real From da5828a368f087476669ea1e6595b785758633c1 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 10:04:09 +0200 Subject: [PATCH 06/35] deprecation warning --- src/waveresponse/_core.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index acbd7ea..798e248 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2355,6 +2355,33 @@ def calculate_response( wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) + # TODO: Remove this once the deprecation period is over + if coord_freq and coord_dirs: + warnings.warn( + "The `coord_freq` and `coord_dirs` parameters are deprecated and will be removed in a future release." + "Use the `reshape` parameter instead.", + DeprecationWarning, + stacklevel=2, + ) + if coord_freq == "wave": + freq = wave_body._freq + elif coord_freq == "rao": + freq = rao._freq + else: + raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") + if coord_dirs == "wave": + dirs = wave_body._dirs + elif coord_dirs == "rao": + dirs = rao._dirs + else: + raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") + rao_squared = (rao * rao.conjugate()).real + rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) + wave_body = wave_body.reshape(freq, dirs, freq_hz=False, degrees=False) + return multiply(rao_squared, wave_body, output_type="DirectionalSpectrum") + elif coord_freq or coord_dirs: + raise ValueError("Both `coord_freq` and `coord_dirs` must be provided.") + if reshape.lower() == "rao": rao = rao.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) rao_squared = (rao * rao.conjugate()).real From f1baa3cfa66dcde880197866e4a21080caf77a52 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 10:05:52 +0200 Subject: [PATCH 07/35] deprecation warning --- src/waveresponse/_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 798e248..92379ff 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2355,7 +2355,7 @@ def calculate_response( wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) - # TODO: Remove this once the deprecation period is over + # TODO: Remove this once the deprecation period is over (deprecated 2025-04-14) if coord_freq and coord_dirs: warnings.warn( "The `coord_freq` and `coord_dirs` parameters are deprecated and will be removed in a future release." From e95c3d7521759d51654470a238fb3fa3ae97ca0c Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 10:36:51 +0200 Subject: [PATCH 08/35] test --- tests/test_core.py | 627 ++++++++++++++++++++++++++------------------- 1 file changed, 370 insertions(+), 257 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index fd82005..9fb7686 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5409,275 +5409,388 @@ def test_dirm_rad(self, d, s, mean_dir_rad): class Test_calculate_response: - def test_calculate_response(self, rao, wave): - response = calculate_response(rao, wave, 0.0) - assert isinstance(response, DirectionalSpectrum) - assert response._clockwise == rao._clockwise - assert response._waves_coming_from == rao._waves_coming_from - assert response._freq_hz is False - assert response._degrees is False - - def test_calculate_response_raises_coord_freq(self, rao, wave): - with pytest.raises(ValueError): - calculate_response(rao, wave, 0.0, coord_freq="invalid-value") - - def test_calculate_response_raises_coord_dirs(self, rao, wave): - with pytest.raises(ValueError): - calculate_response(rao, wave, 0.0, coord_freq="invalid-value") - - def test_calculate_response_coord_wave(self): - freq_rao = np.array([0.0, 0.5, 1.0]) - dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - vals_rao = np.array( - [ - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - ] - ) # all amplitudes are 1 - rao = RAO( - freq_rao, - dirs_rao, - vals_rao, - freq_hz=True, - degrees=True, - clockwise=False, - waves_coming_from=False, - ) + @pytest.fixture + def rao(self): + freq = 2.0 * np.pi * np.array([0.0, 0.5, 1.0]) # rad/s + dirs = np.pi / 180.0 * np.array([45.0, 135.0, 225.0, 315.0]) # rad + vals_amp = 2.0 * np.ones((len(freq), len(dirs))) + vals_phase = np.zeros((len(freq), len(dirs))) - freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) - dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) - vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - vals_wave = np.array( - [ - [1.0, 2.0, 3.0, 4.0, 5.0], - [6.0, 7.0, 8.0, 9.0, 10.0], - [11.0, 12.0, 13.0, 14.0, 15.0], - [16.0, 17.0, 18.0, 19.0, 20.0], - ] - ) - wave = WaveSpectrum( - freq_wave, - dirs_wave, - vals_wave, - freq_hz=True, - degrees=True, - clockwise=True, + rao = RAO.from_amp_phase( + freq, + dirs, + vals_amp, + vals_phase, + freq_hz=False, + degrees=False, waves_coming_from=True, - ) - - response = calculate_response( - rao, wave, 0.0, coord_freq="wave", coord_dirs="wave" - ) - - assert response._clockwise == rao._clockwise - assert response._waves_coming_from == rao._waves_coming_from - - response.set_wave_convention(**wave.wave_convention) - - freq_expect = wave._freq - dirs_expect = wave._dirs - vals_expect = ( - 1.0 - / (2.0 * np.pi * np.pi / 180.0) - * np.array( - [ - [1.0, 2.0, 3.0, 4.0, 5.0], - [6.0, 7.0, 8.0, 9.0, 10.0], - [11.0, 12.0, 13.0, 14.0, 15.0], - [16.0, 17.0, 18.0, 19.0, 20.0], - ] - ) - ) - - assert isinstance(response, DirectionalSpectrum) - assert response._freq_hz is False - assert response._degrees is False - np.testing.assert_array_almost_equal(response._freq, freq_expect) - np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - np.testing.assert_array_almost_equal(response._vals, vals_expect) - - def test_calculate_response_coord_rao(self): - freq_rao = np.array([0.0, 0.5, 1.0]) - dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - vals_rao = np.array( - [ - [1.0 + 0.0j, 0.0 + 2.0j, 3.0 + 0.0j, 0.0 + 4.0j], - [5.0 + 0.0j, 0.0 + 6.0j, 7.0 + 0.0j, 0.0 + 8.0j], - [9.0 + 0.0j, 0.0 + 10.0j, 11.0 + 0.0j, 0.0 + 12.0j], - ] - ) # all amplitudes are 1 - rao = RAO( - freq_rao, - dirs_rao, - vals_rao, - freq_hz=True, - degrees=True, - clockwise=False, - waves_coming_from=False, - ) - - freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) - vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - wave = WaveSpectrum( - freq_wave, - dirs_wave, - vals_wave, - freq_hz=True, - degrees=True, clockwise=True, - waves_coming_from=True, - ) - - response = calculate_response( - rao, wave, 0.0, coord_freq="rao", coord_dirs="rao" - ) - - freq_expect = rao._freq - dirs_expect = rao._dirs - vals_expect = ( - 1.0 - / (2.0 * np.pi * np.pi / 180.0) - * np.array( - [ - [1.0, 2.0, 3.0, 4.0], - [5.0, 6.0, 7.0, 8.0], - [0.0, 0.0, 0.0, 0.0], # extrapolated - ] - ) - ** 2 - ) - - assert isinstance(response, DirectionalSpectrum) - assert response._freq_hz is False - assert response._degrees is False - np.testing.assert_array_almost_equal(response._freq, freq_expect) - np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - np.testing.assert_array_almost_equal(response._vals, vals_expect) - assert response._clockwise == rao._clockwise - assert response._waves_coming_from == rao._waves_coming_from - - def test_calculate_response_heading_degrees(self): - freq_rao = np.array([0.0, 0.5, 1.0]) - dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - vals_rao = np.array( - [ - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - ] - ) # all amplitudes are 1 - rao = RAO( - freq_rao, - dirs_rao, - vals_rao, - freq_hz=True, - degrees=True, ) + return rao + + pytest.fixture + def wave(self): + rng = np.random.default_rng(2) + freq = 2.0 * np.pi * np.array([0.0, 0.3, 0.6, 0.9]) # rad/s + dirs = np.pi / 180.0 * np.array([90.0, 180.0, 270.0, 359.0]) # rad + vals = rng.random((len(freq), len(dirs))) - freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) - vals_wave = np.ones((len(freq_wave), len(dirs_wave))) wave = WaveSpectrum( - freq_wave, - dirs_wave, - vals_wave, - freq_hz=True, - degrees=True, - ) - - response = calculate_response( - rao, wave, 45.0, heading_degrees=True, coord_freq="wave", coord_dirs="wave" - ) - response.set_wave_convention(**wave.wave_convention) - - freq_expect = wave._freq - dirs_expect = wave._dirs - (np.pi / 4.0) - vals_expect = ( - 1.0 - / (2.0 * np.pi * np.pi / 180.0) - * np.array( - [ - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - ] - ) - ) - - assert isinstance(response, DirectionalSpectrum) - assert response._freq_hz is False - assert response._degrees is False - np.testing.assert_array_almost_equal(response._freq, freq_expect) - np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - np.testing.assert_array_almost_equal(response._vals, vals_expect) - assert response._clockwise == rao._clockwise - assert response._waves_coming_from == rao._waves_coming_from - - def test_calculate_response_heading_radians(self): - freq_rao = np.array([0.0, 0.5, 1.0]) - dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - vals_rao = np.array( - [ - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - ] - ) # all amplitudes are 1 - rao = RAO( - freq_rao, - dirs_rao, - vals_rao, - freq_hz=True, - degrees=True, + freq, + dirs, + vals, + freq_hz=False, + degrees=False, + waves_coming_from=True, + clockwise=True, ) + return wave - freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) - vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - wave = WaveSpectrum( - freq_wave, - dirs_wave, - vals_wave, - freq_hz=True, - degrees=True, - ) - response = calculate_response( - rao, - wave, - np.pi / 4.0, - heading_degrees=False, - coord_freq="wave", - coord_dirs="wave", - ) - response.set_wave_convention(**wave.wave_convention) + def test_calculate_response(self, rao, wave): + response = calculate_response(rao, wave, 0.0) - freq_expect = wave._freq - dirs_expect = wave._dirs - (np.pi / 4.0) - vals_expect = ( - 1.0 - / (2.0 * np.pi * np.pi / 180.0) - * np.array( - [ - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - ] - ) - ) + vals_expect = wave._vals * 4.0 assert isinstance(response, DirectionalSpectrum) - assert response._freq_hz is False - assert response._degrees is False - np.testing.assert_array_almost_equal(response._freq, freq_expect) - np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - np.testing.assert_array_almost_equal(response._vals, vals_expect) - assert response._clockwise == rao._clockwise - assert response._waves_coming_from == rao._waves_coming_from + np.testing.assert_allclose(response._vals, vals_expect) + + + + + + # freq_rao = np.array([0.0, 0.5, 1.0]) + # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + # vals_rao = np.array( + # [ + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # ] + # ) # all amplitudes are 1 + # rao = RAO( + # freq_rao, + # dirs_rao, + # vals_rao, + # freq_hz=True, + # degrees=True, + # ) + + # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed + # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) + # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + # wave = WaveSpectrum( + # freq_wave, + # dirs_wave, + # vals_wave, + # freq_hz=True, + # degrees=True, + # ) + + # response = calculate_response( + # rao, + # wave, + # np.pi / 4.0, + # heading_degrees=False, + # coord_freq="wave", + # coord_dirs="wave", + # ) + # response.set_wave_convention(**wave.wave_convention) + + # freq_expect = wave._freq + # dirs_expect = wave._dirs - (np.pi / 4.0) + # vals_expect = ( + # 1.0 + # / (2.0 * np.pi * np.pi / 180.0) + # * np.array( + # [ + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # ] + # ) + # ) + + # assert isinstance(response, DirectionalSpectrum) + # assert response._freq_hz is False + # assert response._degrees is False + # np.testing.assert_array_almost_equal(response._freq, freq_expect) + # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + # np.testing.assert_array_almost_equal(response._vals, vals_expect) + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from + + + # def test_calculate_response(self, rao, wave): + # response = calculate_response(rao, wave, 0.0) + # assert isinstance(response, DirectionalSpectrum) + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from + # assert response._freq_hz is False + # assert response._degrees is False + + # def test_calculate_response_raises_coord_freq(self, rao, wave): + # with pytest.raises(ValueError): + # calculate_response(rao, wave, 0.0, coord_freq="invalid-value") + + # def test_calculate_response_raises_coord_dirs(self, rao, wave): + # with pytest.raises(ValueError): + # calculate_response(rao, wave, 0.0, coord_freq="invalid-value") + + # def test_calculate_response_coord_wave(self): + # freq_rao = np.array([0.0, 0.5, 1.0]) + # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + # vals_rao = np.array( + # [ + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # ] + # ) # all amplitudes are 1 + # rao = RAO( + # freq_rao, + # dirs_rao, + # vals_rao, + # freq_hz=True, + # degrees=True, + # clockwise=False, + # waves_coming_from=False, + # ) + + # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) + # dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) + # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + # vals_wave = np.array( + # [ + # [1.0, 2.0, 3.0, 4.0, 5.0], + # [6.0, 7.0, 8.0, 9.0, 10.0], + # [11.0, 12.0, 13.0, 14.0, 15.0], + # [16.0, 17.0, 18.0, 19.0, 20.0], + # ] + # ) + # wave = WaveSpectrum( + # freq_wave, + # dirs_wave, + # vals_wave, + # freq_hz=True, + # degrees=True, + # clockwise=True, + # waves_coming_from=True, + # ) + + # response = calculate_response( + # rao, wave, 0.0, coord_freq="wave", coord_dirs="wave" + # ) + + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from + + # response.set_wave_convention(**wave.wave_convention) + + # freq_expect = wave._freq + # dirs_expect = wave._dirs + # vals_expect = ( + # 1.0 + # / (2.0 * np.pi * np.pi / 180.0) + # * np.array( + # [ + # [1.0, 2.0, 3.0, 4.0, 5.0], + # [6.0, 7.0, 8.0, 9.0, 10.0], + # [11.0, 12.0, 13.0, 14.0, 15.0], + # [16.0, 17.0, 18.0, 19.0, 20.0], + # ] + # ) + # ) + + # assert isinstance(response, DirectionalSpectrum) + # assert response._freq_hz is False + # assert response._degrees is False + # np.testing.assert_array_almost_equal(response._freq, freq_expect) + # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + # np.testing.assert_array_almost_equal(response._vals, vals_expect) + + # def test_calculate_response_coord_rao(self): + # freq_rao = np.array([0.0, 0.5, 1.0]) + # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + # vals_rao = np.array( + # [ + # [1.0 + 0.0j, 0.0 + 2.0j, 3.0 + 0.0j, 0.0 + 4.0j], + # [5.0 + 0.0j, 0.0 + 6.0j, 7.0 + 0.0j, 0.0 + 8.0j], + # [9.0 + 0.0j, 0.0 + 10.0j, 11.0 + 0.0j, 0.0 + 12.0j], + # ] + # ) # all amplitudes are 1 + # rao = RAO( + # freq_rao, + # dirs_rao, + # vals_rao, + # freq_hz=True, + # degrees=True, + # clockwise=False, + # waves_coming_from=False, + # ) + + # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed + # dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) + # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + # wave = WaveSpectrum( + # freq_wave, + # dirs_wave, + # vals_wave, + # freq_hz=True, + # degrees=True, + # clockwise=True, + # waves_coming_from=True, + # ) + + # response = calculate_response( + # rao, wave, 0.0, coord_freq="rao", coord_dirs="rao" + # ) + + # freq_expect = rao._freq + # dirs_expect = rao._dirs + # vals_expect = ( + # 1.0 + # / (2.0 * np.pi * np.pi / 180.0) + # * np.array( + # [ + # [1.0, 2.0, 3.0, 4.0], + # [5.0, 6.0, 7.0, 8.0], + # [0.0, 0.0, 0.0, 0.0], # extrapolated + # ] + # ) + # ** 2 + # ) + + # assert isinstance(response, DirectionalSpectrum) + # assert response._freq_hz is False + # assert response._degrees is False + # np.testing.assert_array_almost_equal(response._freq, freq_expect) + # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + # np.testing.assert_array_almost_equal(response._vals, vals_expect) + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from + + # def test_calculate_response_heading_degrees(self): + # freq_rao = np.array([0.0, 0.5, 1.0]) + # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + # vals_rao = np.array( + # [ + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # ] + # ) # all amplitudes are 1 + # rao = RAO( + # freq_rao, + # dirs_rao, + # vals_rao, + # freq_hz=True, + # degrees=True, + # ) + + # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed + # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) + # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + # wave = WaveSpectrum( + # freq_wave, + # dirs_wave, + # vals_wave, + # freq_hz=True, + # degrees=True, + # ) + + # response = calculate_response( + # rao, wave, 45.0, heading_degrees=True, coord_freq="wave", coord_dirs="wave" + # ) + # response.set_wave_convention(**wave.wave_convention) + + # freq_expect = wave._freq + # dirs_expect = wave._dirs - (np.pi / 4.0) + # vals_expect = ( + # 1.0 + # / (2.0 * np.pi * np.pi / 180.0) + # * np.array( + # [ + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # ] + # ) + # ) + + # assert isinstance(response, DirectionalSpectrum) + # assert response._freq_hz is False + # assert response._degrees is False + # np.testing.assert_array_almost_equal(response._freq, freq_expect) + # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + # np.testing.assert_array_almost_equal(response._vals, vals_expect) + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from + + # def test_calculate_response_heading_radians(self): + # freq_rao = np.array([0.0, 0.5, 1.0]) + # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + # vals_rao = np.array( + # [ + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + # ] + # ) # all amplitudes are 1 + # rao = RAO( + # freq_rao, + # dirs_rao, + # vals_rao, + # freq_hz=True, + # degrees=True, + # ) + + # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed + # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) + # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + # wave = WaveSpectrum( + # freq_wave, + # dirs_wave, + # vals_wave, + # freq_hz=True, + # degrees=True, + # ) + + # response = calculate_response( + # rao, + # wave, + # np.pi / 4.0, + # heading_degrees=False, + # coord_freq="wave", + # coord_dirs="wave", + # ) + # response.set_wave_convention(**wave.wave_convention) + + # freq_expect = wave._freq + # dirs_expect = wave._dirs - (np.pi / 4.0) + # vals_expect = ( + # 1.0 + # / (2.0 * np.pi * np.pi / 180.0) + # * np.array( + # [ + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # [1.0, 1.0, 1.0, 1.0], + # ] + # ) + # ) + + # assert isinstance(response, DirectionalSpectrum) + # assert response._freq_hz is False + # assert response._degrees is False + # np.testing.assert_array_almost_equal(response._freq, freq_expect) + # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + # np.testing.assert_array_almost_equal(response._vals, vals_expect) + # assert response._clockwise == rao._clockwise + # assert response._waves_coming_from == rao._waves_coming_from class Test__check_is_similar: From fed69a693dd312fef2eb301720f726e6ee41180e Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 17:45:28 +0200 Subject: [PATCH 09/35] test --- src/waveresponse/_core.py | 18 ++++++++++++++---- tests/test_core.py | 32 +++++++++++++++++--------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 92379ff..27b77e9 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2305,7 +2305,13 @@ def dirm(self, degrees=None): def calculate_response( - rao, wave, heading, heading_degrees=False, reshape="rao_squared", coord_freq=None, coord_dirs=None + rao, + wave, + heading, + heading_degrees=False, + reshape="rao_squared", + coord_freq=None, + coord_dirs=None, ): """ Calculate response spectrum. @@ -2365,7 +2371,7 @@ def calculate_response( ) if coord_freq == "wave": freq = wave_body._freq - elif coord_freq == "rao": + elif coord_freq == "rao": freq = rao._freq else: raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") @@ -2383,11 +2389,15 @@ def calculate_response( raise ValueError("Both `coord_freq` and `coord_dirs` must be provided.") if reshape.lower() == "rao": - rao = rao.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) + rao = rao.reshape( + wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False + ) rao_squared = (rao * rao.conjugate()).real elif reshape.lower() == "rao_squared": rao_squared = (rao * rao.conjugate()).real - rao_squared = rao_squared.reshape(wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False) + rao_squared = rao_squared.reshape( + wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False + ) else: raise ValueError("Invalid `reshape` value. Should be 'rao' or 'rao_squared'.") diff --git a/tests/test_core.py b/tests/test_core.py index 9fb7686..2c1d55b 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5411,9 +5411,11 @@ def test_dirm_rad(self, d, s, mean_dir_rad): class Test_calculate_response: @pytest.fixture def rao(self): - freq = 2.0 * np.pi * np.array([0.0, 0.5, 1.0]) # rad/s - dirs = np.pi / 180.0 * np.array([45.0, 135.0, 225.0, 315.0]) # rad - vals_amp = 2.0 * np.ones((len(freq), len(dirs))) + freq = 2.0 * np.pi * np.array([0.01, 0.2, 0.4, 0.6, 1.2]) # rad/s + dirs = np.pi / 180.0 * np.array([0.0, 45.0, 135.0, 225.0, 315.0]) # rad + + a, b, c = 2.0, 3.0, 4.0 + vals_amp = a * freq[:, np.newaxis] + b * dirs[np.newaxis, :] + c vals_phase = np.zeros((len(freq), len(dirs))) rao = RAO.from_amp_phase( @@ -5427,12 +5429,12 @@ def rao(self): clockwise=True, ) return rao - - pytest.fixture + + @pytest.fixture def wave(self): rng = np.random.default_rng(2) - freq = 2.0 * np.pi * np.array([0.0, 0.3, 0.6, 0.9]) # rad/s - dirs = np.pi / 180.0 * np.array([90.0, 180.0, 270.0, 359.0]) # rad + freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s + dirs = np.pi / 180.0 * np.array([0.0, 90.0, 180.0, 270.0]) # rad vals = rng.random((len(freq), len(dirs))) wave = WaveSpectrum( @@ -5446,18 +5448,19 @@ def wave(self): ) return wave - def test_calculate_response(self, rao, wave): - response = calculate_response(rao, wave, 0.0) - - vals_expect = wave._vals * 4.0 - - assert isinstance(response, DirectionalSpectrum) - np.testing.assert_allclose(response._vals, vals_expect) + response = calculate_response(rao, wave, 0.0, reshape="rao") + a, b, c = 2.0, 3.0, 4.0 + vals_amp = a * wave._freq[:, np.newaxis] + b * wave._dirs[np.newaxis, :] + c + vals_expect = wave._vals * vals_amp**2 + assert isinstance(response, wr.DirectionalSpectrum) + np.testing.assert_allclose(response._vals, vals_expect) + assert isinstance(response, DirectionalSpectrum) + np.testing.assert_allclose(response._vals, vals_expect) # freq_rao = np.array([0.0, 0.5, 1.0]) # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) @@ -5521,7 +5524,6 @@ def test_calculate_response(self, rao, wave): # assert response._clockwise == rao._clockwise # assert response._waves_coming_from == rao._waves_coming_from - # def test_calculate_response(self, rao, wave): # response = calculate_response(rao, wave, 0.0) # assert isinstance(response, DirectionalSpectrum) From e6be8f3558b4807c458490f229559979b42e1d6d Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 17:47:16 +0200 Subject: [PATCH 10/35] test fix --- tests/test_core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 2c1d55b..a2cb995 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5452,7 +5452,8 @@ def test_calculate_response(self, rao, wave): response = calculate_response(rao, wave, 0.0, reshape="rao") a, b, c = 2.0, 3.0, 4.0 - vals_amp = a * wave._freq[:, np.newaxis] + b * wave._dirs[np.newaxis, :] + c + f, theta = wave._freq[:, np.newaxis], wave._dirs[np.newaxis, :] + vals_amp = a * f + b * theta + c vals_expect = wave._vals * vals_amp**2 From bc4b3f82c1cd3851b32357b6bb1f2f1df906aee9 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 17:55:33 +0200 Subject: [PATCH 11/35] testing --- tests/test_core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index a2cb995..b6f1999 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5458,10 +5458,12 @@ def test_calculate_response(self, rao, wave): vals_expect = wave._vals * vals_amp**2 assert isinstance(response, wr.DirectionalSpectrum) + assert response._clockwise is rao._clockwise + assert response._waves_coming_from is rao._waves_coming_from np.testing.assert_allclose(response._vals, vals_expect) - assert isinstance(response, DirectionalSpectrum) - np.testing.assert_allclose(response._vals, vals_expect) + # assert isinstance(response, DirectionalSpectrum) + # np.testing.assert_allclose(response._vals, vals_expect) # freq_rao = np.array([0.0, 0.5, 1.0]) # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) From 051c0b93cae8e951a739576f0a2b03fbc4793e8b Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 18:00:11 +0200 Subject: [PATCH 12/35] small fix --- tests/test_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index b6f1999..e4f67ad 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5432,7 +5432,7 @@ def rao(self): @pytest.fixture def wave(self): - rng = np.random.default_rng(2) + rng = np.random.default_rng(123) freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s dirs = np.pi / 180.0 * np.array([0.0, 90.0, 180.0, 270.0]) # rad vals = rng.random((len(freq), len(dirs))) From 8a37d60e83dec034c15854fe3d5e3161caf2b296 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 18:10:20 +0200 Subject: [PATCH 13/35] doc --- docs/user_guide/calculate_response.rst | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/user_guide/calculate_response.rst b/docs/user_guide/calculate_response.rst index 10b580a..b43187c 100644 --- a/docs/user_guide/calculate_response.rst +++ b/docs/user_guide/calculate_response.rst @@ -52,10 +52,10 @@ This function is roughly equivalent to: Parameters ---------- - rao : obj - ``RAO`` object. - wave : obj - ``WaveSpectrum`` object. + rao : RAO + Response amplitude operator (RAO). + wave : WaveSpectrum + Wave spectrum. heading : float Heading of vessel relative to wave spectrum coordinate system. heading_degrees : bool @@ -63,7 +63,7 @@ This function is roughly equivalent to: Returns ------- - obj : + DirectionalSpectrum : Response spectrum. """ @@ -85,9 +85,8 @@ This function is roughly equivalent to: dirs = wave_body.dirs(degrees=False) rao_squared = (rao * rao.conjugate()).real rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) - wave_body = wave_body.reshape(freq, dirs, freq_hz=False, degrees=False) - return wr.multiply(rao_squared, wave_body, output_type="directional_spectrum") + return wr.multiply(rao_squared, wave_body, output_type="DirectionalSpectrum") The response is returned as a :class:`~waveresponse.DirectionalSpectrum` object, and provides useful spectrum operations, such as: From 773ee0ed22ddf1f2e04b142e403bd05b1a322bb4 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Mon, 14 Apr 2025 18:52:52 +0200 Subject: [PATCH 14/35] type --- src/waveresponse/_core.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 27b77e9..cf9adcb 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2401,7 +2401,16 @@ def calculate_response( else: raise ValueError("Invalid `reshape` value. Should be 'rao' or 'rao_squared'.") - return multiply(rao_squared, wave_body, output_type=type(wave_body)) + if isinstance(wave, WaveSpectrum): + type_ = "DirectionalSpectrum" + elif isinstance(wave, WaveBinSpectrum): + type_ = "DirectionalBinSpectrum" + else: + raise ValueError( + "Invalid `wave` type. Should be 'WaveSpectrum' or 'WaveBinSpectrum'." + ) + + return multiply(rao_squared, wave_body, output_type=type_) class BaseSpreading(ABC): From 39820d91ad4a3fefdee5bc3f8c17b72d39dd0de0 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 07:50:13 +0200 Subject: [PATCH 15/35] testing --- tests/test_core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index e4f67ad..69edb1d 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5460,6 +5460,10 @@ def test_calculate_response(self, rao, wave): assert isinstance(response, wr.DirectionalSpectrum) assert response._clockwise is rao._clockwise assert response._waves_coming_from is rao._waves_coming_from + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, wave._freq) + np.testing.assert_allclose(response._dirs, wave._dirs) np.testing.assert_allclose(response._vals, vals_expect) # assert isinstance(response, DirectionalSpectrum) From e889dc2bf32bda5e6436f5dd3d7c78fd7094f07b Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:05:44 +0200 Subject: [PATCH 16/35] doc --- docs/user_guide/calculate_response.rst | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/user_guide/calculate_response.rst b/docs/user_guide/calculate_response.rst index b43187c..4df8da0 100644 --- a/docs/user_guide/calculate_response.rst +++ b/docs/user_guide/calculate_response.rst @@ -57,7 +57,7 @@ This function is roughly equivalent to: wave : WaveSpectrum Wave spectrum. heading : float - Heading of vessel relative to wave spectrum coordinate system. + Heading of vessel relative to wave spectrum reference frame. heading_degrees : bool Whether the heading is given in 'degrees'. If ``False``, 'radians' is assumed. @@ -73,10 +73,7 @@ This function is roughly equivalent to: # Ensure that ``rao`` and ``wave`` has the same 'wave convention' wave_body.set_wave_convention(**rao.wave_convention) - # Reshape ``rao`` and ``wave`` so that they share the same frequency/direction - # coordinates. In this example, ``wave`` will dictate the coordinates, and - # the ``rao`` object will be interpolated to match these coordinates. - # + # Reshape the RAO to match the wave spectrum's frequency/direction coordinates. # It is recommended to reshape (i.e., interpolate) the magnitude-squared # version of the RAO when estimating response, since this has shown best # results: From 2b57590195e57134c6f6770fb4f26702fb7d7b22 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:07:27 +0200 Subject: [PATCH 17/35] docstring --- src/waveresponse/_core.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index cf9adcb..3384fb9 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2322,11 +2322,11 @@ def calculate_response( where ``S_x(w, theta)`` is the response spectrum, ``H(w, theta)`` is the RAO, ``H*(w, theta)`` is the conjugate of the RAO, and ``S_w(w, theta)`` is the wave - spectrum. + spectrum (expressed in the same coordinate system as the RAO). The frequency and direction coordinates are dictatated by the wave spectrum. - The RAO (or the squared verison of it) is interpolated to match the grid coordinates - of the wave spectrum. + The RAO (or the magnitude-squared verison of it) is interpolated to match the + grid coordinates of the wave spectrum. Parameters ---------- From f72266ec21250ccd3e14588c165a14c41117aa72 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:28:05 +0200 Subject: [PATCH 18/35] docstring --- src/waveresponse/_core.py | 67 +++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 3384fb9..01a2e1a 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2304,6 +2304,29 @@ def dirm(self, degrees=None): return dirm +def _calculate_response_deprecated(rao, wave_body, coord_freq, coord_dirs): + # TODO: Deprecated. Remove this function in a future release. + + if coord_freq == "wave": + freq = wave_body._freq + elif coord_freq == "rao": + freq = rao._freq + else: + raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") + if coord_dirs == "wave": + dirs = wave_body._dirs + elif coord_dirs == "rao": + dirs = rao._dirs + else: + raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") + + rao_squared = (rao * rao.conjugate()).real + rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) + wave_body = wave_body.reshape(freq, dirs, freq_hz=False, degrees=False) + + return multiply(rao_squared, wave_body, output_type="DirectionalSpectrum") + + def calculate_response( rao, wave, @@ -2318,15 +2341,15 @@ def calculate_response( The response spectrum is calculated according to: - ``S_x(w, theta) = H(w, theta) * H*(w, theta) * S_w(w, theta)`` + S_x(w, theta) = H(w, theta) * H*(w, theta) * S_w(w, theta) - where ``S_x(w, theta)`` is the response spectrum, ``H(w, theta)`` is the RAO, - ``H*(w, theta)`` is the conjugate of the RAO, and ``S_w(w, theta)`` is the wave - spectrum (expressed in the same coordinate system as the RAO). + where S_x(w, theta) denotes the response spectrum, H(w, theta) denotes the RAO, + H*(w, theta) denotes the RAO conjugate, and S_w(w, theta) denotes the wave + spectrum (expressed in the RAO's reference frame). The frequency and direction coordinates are dictatated by the wave spectrum. - The RAO (or the magnitude-squared verison of it) is interpolated to match the - grid coordinates of the wave spectrum. + I.e., the RAO (or the magnitude-squared verison of it) is interpolated to match + the grid coordinates of the wave spectrum. Parameters ---------- @@ -2361,7 +2384,7 @@ def calculate_response( wave_body = wave.rotate(heading, degrees=heading_degrees) wave_body.set_wave_convention(**rao.wave_convention) - # TODO: Remove this once the deprecation period is over (deprecated 2025-04-14) + # TODO: Remove once the deprecation period is over if coord_freq and coord_dirs: warnings.warn( "The `coord_freq` and `coord_dirs` parameters are deprecated and will be removed in a future release." @@ -2369,35 +2392,17 @@ def calculate_response( DeprecationWarning, stacklevel=2, ) - if coord_freq == "wave": - freq = wave_body._freq - elif coord_freq == "rao": - freq = rao._freq - else: - raise ValueError("Invalid `coord_freq` value. Should be 'wave' or 'rao'.") - if coord_dirs == "wave": - dirs = wave_body._dirs - elif coord_dirs == "rao": - dirs = rao._dirs - else: - raise ValueError("Invalid `coord_dirs` value. Should be 'wave' or 'rao'.") - rao_squared = (rao * rao.conjugate()).real - rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) - wave_body = wave_body.reshape(freq, dirs, freq_hz=False, degrees=False) - return multiply(rao_squared, wave_body, output_type="DirectionalSpectrum") + return _calculate_response_deprecated(rao, wave_body, coord_freq, coord_dirs) elif coord_freq or coord_dirs: raise ValueError("Both `coord_freq` and `coord_dirs` must be provided.") - if reshape.lower() == "rao": - rao = rao.reshape( - wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False - ) + freq, dirs = wave_body._freq, wave_body._dirs + if reshape == "rao": + rao = rao.reshape(freq, dirs, freq_hz=False, degrees=False) rao_squared = (rao * rao.conjugate()).real - elif reshape.lower() == "rao_squared": + elif reshape == "rao_squared": rao_squared = (rao * rao.conjugate()).real - rao_squared = rao_squared.reshape( - wave_body._freq, wave_body._dirs, freq_hz=False, degrees=False - ) + rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False) else: raise ValueError("Invalid `reshape` value. Should be 'rao' or 'rao_squared'.") From 2d5b12c49e0de184902fd89d855fab0d895b4beb Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:31:53 +0200 Subject: [PATCH 19/35] docstring --- src/waveresponse/_core.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/waveresponse/_core.py b/src/waveresponse/_core.py index 01a2e1a..d3df80a 100644 --- a/src/waveresponse/_core.py +++ b/src/waveresponse/_core.py @@ -2362,9 +2362,10 @@ def calculate_response( heading_degrees : bool Whether the heading is given in 'degrees'. If ``False``, 'radians' is assumed. reshape : {'rao', 'rao_squared'}, default 'rao_squared' - Determines whether to reshape the RAO or the squared RAO before pairing with - the wave spectrum. Linear interpolation is performed to match the frequency - and direction coordinates of the wave spectrum. + Determines whether to reshape the RAO or the magnitude-squared version of + the RAO before pairing with the wave spectrum. Linear interpolation is + performed to match the frequency and direction coordinates of the wave + spectrum. coord_freq : str, optional Deprecated; use `reshape` instead. Frequency coordinates for interpolation. Should be 'wave' or 'rao'. Determines if it is the wave spectrum or the @@ -2406,11 +2407,14 @@ def calculate_response( else: raise ValueError("Invalid `reshape` value. Should be 'rao' or 'rao_squared'.") - if isinstance(wave, WaveSpectrum): - type_ = "DirectionalSpectrum" - elif isinstance(wave, WaveBinSpectrum): - type_ = "DirectionalBinSpectrum" - else: + TYPE_MAP = { + WaveSpectrum: "DirectionalSpectrum", + WaveBinSpectrum: "DirectionalBinSpectrum", + } + + try: + type_ = TYPE_MAP[type(wave)] + except KeyError: raise ValueError( "Invalid `wave` type. Should be 'WaveSpectrum' or 'WaveBinSpectrum'." ) From 6775f41091f74600ff42f625cb452333b489176f Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:38:16 +0200 Subject: [PATCH 20/35] add tests of deprecated params --- tests/test_core.py | 260 +++++++++++++++++++++++---------------------- 1 file changed, 132 insertions(+), 128 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 69edb1d..d9d3040 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5539,146 +5539,150 @@ def test_calculate_response(self, rao, wave): # assert response._freq_hz is False # assert response._degrees is False - # def test_calculate_response_raises_coord_freq(self, rao, wave): - # with pytest.raises(ValueError): - # calculate_response(rao, wave, 0.0, coord_freq="invalid-value") - - # def test_calculate_response_raises_coord_dirs(self, rao, wave): - # with pytest.raises(ValueError): - # calculate_response(rao, wave, 0.0, coord_freq="invalid-value") + def test_calculate_response_raises_coord_freq(self, rao, wave): + # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + with pytest.raises(ValueError): + calculate_response(rao, wave, 0.0, coord_freq="invalid-value") - # def test_calculate_response_coord_wave(self): - # freq_rao = np.array([0.0, 0.5, 1.0]) - # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - # vals_rao = np.array( - # [ - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # ] - # ) # all amplitudes are 1 - # rao = RAO( - # freq_rao, - # dirs_rao, - # vals_rao, - # freq_hz=True, - # degrees=True, - # clockwise=False, - # waves_coming_from=False, - # ) + def test_calculate_response_raises_coord_dirs(self, rao, wave): + # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + with pytest.raises(ValueError): + calculate_response(rao, wave, 0.0, coord_freq="invalid-value") - # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) - # dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) - # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - # vals_wave = np.array( - # [ - # [1.0, 2.0, 3.0, 4.0, 5.0], - # [6.0, 7.0, 8.0, 9.0, 10.0], - # [11.0, 12.0, 13.0, 14.0, 15.0], - # [16.0, 17.0, 18.0, 19.0, 20.0], - # ] - # ) - # wave = WaveSpectrum( - # freq_wave, - # dirs_wave, - # vals_wave, - # freq_hz=True, - # degrees=True, - # clockwise=True, - # waves_coming_from=True, - # ) + def test_calculate_response_coord_wave(self): + # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + freq_rao = np.array([0.0, 0.5, 1.0]) + dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + vals_rao = np.array( + [ + [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], + ] + ) # all amplitudes are 1 + rao = RAO( + freq_rao, + dirs_rao, + vals_rao, + freq_hz=True, + degrees=True, + clockwise=False, + waves_coming_from=False, + ) - # response = calculate_response( - # rao, wave, 0.0, coord_freq="wave", coord_dirs="wave" - # ) + freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) + dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) + vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + vals_wave = np.array( + [ + [1.0, 2.0, 3.0, 4.0, 5.0], + [6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 13.0, 14.0, 15.0], + [16.0, 17.0, 18.0, 19.0, 20.0], + ] + ) + wave = WaveSpectrum( + freq_wave, + dirs_wave, + vals_wave, + freq_hz=True, + degrees=True, + clockwise=True, + waves_coming_from=True, + ) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from + response = calculate_response( + rao, wave, 0.0, coord_freq="wave", coord_dirs="wave" + ) - # response.set_wave_convention(**wave.wave_convention) + assert response._clockwise == rao._clockwise + assert response._waves_coming_from == rao._waves_coming_from - # freq_expect = wave._freq - # dirs_expect = wave._dirs - # vals_expect = ( - # 1.0 - # / (2.0 * np.pi * np.pi / 180.0) - # * np.array( - # [ - # [1.0, 2.0, 3.0, 4.0, 5.0], - # [6.0, 7.0, 8.0, 9.0, 10.0], - # [11.0, 12.0, 13.0, 14.0, 15.0], - # [16.0, 17.0, 18.0, 19.0, 20.0], - # ] - # ) - # ) + response.set_wave_convention(**wave.wave_convention) - # assert isinstance(response, DirectionalSpectrum) - # assert response._freq_hz is False - # assert response._degrees is False - # np.testing.assert_array_almost_equal(response._freq, freq_expect) - # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - # np.testing.assert_array_almost_equal(response._vals, vals_expect) + freq_expect = wave._freq + dirs_expect = wave._dirs + vals_expect = ( + 1.0 + / (2.0 * np.pi * np.pi / 180.0) + * np.array( + [ + [1.0, 2.0, 3.0, 4.0, 5.0], + [6.0, 7.0, 8.0, 9.0, 10.0], + [11.0, 12.0, 13.0, 14.0, 15.0], + [16.0, 17.0, 18.0, 19.0, 20.0], + ] + ) + ) - # def test_calculate_response_coord_rao(self): - # freq_rao = np.array([0.0, 0.5, 1.0]) - # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - # vals_rao = np.array( - # [ - # [1.0 + 0.0j, 0.0 + 2.0j, 3.0 + 0.0j, 0.0 + 4.0j], - # [5.0 + 0.0j, 0.0 + 6.0j, 7.0 + 0.0j, 0.0 + 8.0j], - # [9.0 + 0.0j, 0.0 + 10.0j, 11.0 + 0.0j, 0.0 + 12.0j], - # ] - # ) # all amplitudes are 1 - # rao = RAO( - # freq_rao, - # dirs_rao, - # vals_rao, - # freq_hz=True, - # degrees=True, - # clockwise=False, - # waves_coming_from=False, - # ) + assert isinstance(response, DirectionalSpectrum) + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_array_almost_equal(response._freq, freq_expect) + np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + np.testing.assert_array_almost_equal(response._vals, vals_expect) + + def test_calculate_response_coord_rao(self): + # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + freq_rao = np.array([0.0, 0.5, 1.0]) + dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) + vals_rao = np.array( + [ + [1.0 + 0.0j, 0.0 + 2.0j, 3.0 + 0.0j, 0.0 + 4.0j], + [5.0 + 0.0j, 0.0 + 6.0j, 7.0 + 0.0j, 0.0 + 8.0j], + [9.0 + 0.0j, 0.0 + 10.0j, 11.0 + 0.0j, 0.0 + 12.0j], + ] + ) # all amplitudes are 1 + rao = RAO( + freq_rao, + dirs_rao, + vals_rao, + freq_hz=True, + degrees=True, + clockwise=False, + waves_coming_from=False, + ) - # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - # dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) - # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - # wave = WaveSpectrum( - # freq_wave, - # dirs_wave, - # vals_wave, - # freq_hz=True, - # degrees=True, - # clockwise=True, - # waves_coming_from=True, - # ) + freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed + dirs_wave = np.array([0.0, 90.0, 180.0, 270.0, 359.0]) + vals_wave = np.ones((len(freq_wave), len(dirs_wave))) + wave = WaveSpectrum( + freq_wave, + dirs_wave, + vals_wave, + freq_hz=True, + degrees=True, + clockwise=True, + waves_coming_from=True, + ) - # response = calculate_response( - # rao, wave, 0.0, coord_freq="rao", coord_dirs="rao" - # ) + response = calculate_response( + rao, wave, 0.0, coord_freq="rao", coord_dirs="rao" + ) - # freq_expect = rao._freq - # dirs_expect = rao._dirs - # vals_expect = ( - # 1.0 - # / (2.0 * np.pi * np.pi / 180.0) - # * np.array( - # [ - # [1.0, 2.0, 3.0, 4.0], - # [5.0, 6.0, 7.0, 8.0], - # [0.0, 0.0, 0.0, 0.0], # extrapolated - # ] - # ) - # ** 2 - # ) + freq_expect = rao._freq + dirs_expect = rao._dirs + vals_expect = ( + 1.0 + / (2.0 * np.pi * np.pi / 180.0) + * np.array( + [ + [1.0, 2.0, 3.0, 4.0], + [5.0, 6.0, 7.0, 8.0], + [0.0, 0.0, 0.0, 0.0], # extrapolated + ] + ) + ** 2 + ) - # assert isinstance(response, DirectionalSpectrum) - # assert response._freq_hz is False - # assert response._degrees is False - # np.testing.assert_array_almost_equal(response._freq, freq_expect) - # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - # np.testing.assert_array_almost_equal(response._vals, vals_expect) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from + assert isinstance(response, DirectionalSpectrum) + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_array_almost_equal(response._freq, freq_expect) + np.testing.assert_array_almost_equal(response._dirs, dirs_expect) + np.testing.assert_array_almost_equal(response._vals, vals_expect) + assert response._clockwise == rao._clockwise + assert response._waves_coming_from == rao._waves_coming_from # def test_calculate_response_heading_degrees(self): # freq_rao = np.array([0.0, 0.5, 1.0]) From b72be23003eae919a2dd6e5cf7e390b3874c4e35 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:51:53 +0200 Subject: [PATCH 21/35] test heading --- tests/test_core.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index d9d3040..68438c3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5466,6 +5466,14 @@ def test_calculate_response(self, rao, wave): np.testing.assert_allclose(response._dirs, wave._dirs) np.testing.assert_allclose(response._vals, vals_expect) + def calculate_response_heading_degrees(self, rao, wave): + response = calculate_response(rao, wave, 45.0, heading_degrees=True) + np.testing.assert_allclose(response._dirs, wave._dirs - np.pi / 4.0) + + def calculate_response_heading_radians(self, rao, wave): + response = calculate_response(rao, wave, np.pi / 4.0, heading_degrees=False) + np.testing.assert_allclose(response._dirs, wave._dirs - np.pi / 4.0) + # assert isinstance(response, DirectionalSpectrum) # np.testing.assert_allclose(response._vals, vals_expect) From f503c964e32b46b1aa3e1a247d0f203a28030a8e Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 08:59:12 +0200 Subject: [PATCH 22/35] fix heading test --- tests/test_core.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 68438c3..074e924 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5412,7 +5412,7 @@ class Test_calculate_response: @pytest.fixture def rao(self): freq = 2.0 * np.pi * np.array([0.01, 0.2, 0.4, 0.6, 1.2]) # rad/s - dirs = np.pi / 180.0 * np.array([0.0, 45.0, 135.0, 225.0, 315.0]) # rad + dirs = np.pi / 180.0 * np.array([10.0, 45.0, 135.0, 225.0, 315.0]) # rad a, b, c = 2.0, 3.0, 4.0 vals_amp = a * freq[:, np.newaxis] + b * dirs[np.newaxis, :] + c @@ -5434,7 +5434,7 @@ def rao(self): def wave(self): rng = np.random.default_rng(123) freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s - dirs = np.pi / 180.0 * np.array([0.0, 90.0, 180.0, 270.0]) # rad + dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad vals = rng.random((len(freq), len(dirs))) wave = WaveSpectrum( @@ -5466,13 +5466,22 @@ def test_calculate_response(self, rao, wave): np.testing.assert_allclose(response._dirs, wave._dirs) np.testing.assert_allclose(response._vals, vals_expect) - def calculate_response_heading_degrees(self, rao, wave): - response = calculate_response(rao, wave, 45.0, heading_degrees=True) - np.testing.assert_allclose(response._dirs, wave._dirs - np.pi / 4.0) + def test_calculate_response_heading_degrees(self, rao, wave): + response = calculate_response(rao, wave, 5, heading_degrees=True) + np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) - def calculate_response_heading_radians(self, rao, wave): + def test_calculate_response_heading_radians(self, rao, wave): + response = calculate_response(rao, wave, np.radians(5), heading_degrees=False) + np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) + + def test_calculate_response_wave_convention(self, rao, wave): + rao_convention = {"waves_coming_from": True, "clockwise": False} + wave_convention = {"waves_coming_from": False, "clockwise": True} + rao.set_wave_convention(**rao_convention) + wave.set_wave_convention(**wave_convention) response = calculate_response(rao, wave, np.pi / 4.0, heading_degrees=False) - np.testing.assert_allclose(response._dirs, wave._dirs - np.pi / 4.0) + assert response._clockwise is False + assert response._waves_coming_from is True # assert isinstance(response, DirectionalSpectrum) # np.testing.assert_allclose(response._vals, vals_expect) From 3753bf3ebfbfcf13915d79c3116b9b63d49795ba Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:03:16 +0200 Subject: [PATCH 23/35] delete old commented out tests --- tests/test_core.py | 194 --------------------------------------------- 1 file changed, 194 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 074e924..e714451 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5483,79 +5483,6 @@ def test_calculate_response_wave_convention(self, rao, wave): assert response._clockwise is False assert response._waves_coming_from is True - # assert isinstance(response, DirectionalSpectrum) - # np.testing.assert_allclose(response._vals, vals_expect) - - # freq_rao = np.array([0.0, 0.5, 1.0]) - # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - # vals_rao = np.array( - # [ - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # ] - # ) # all amplitudes are 1 - # rao = RAO( - # freq_rao, - # dirs_rao, - # vals_rao, - # freq_hz=True, - # degrees=True, - # ) - - # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) - # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - # wave = WaveSpectrum( - # freq_wave, - # dirs_wave, - # vals_wave, - # freq_hz=True, - # degrees=True, - # ) - - # response = calculate_response( - # rao, - # wave, - # np.pi / 4.0, - # heading_degrees=False, - # coord_freq="wave", - # coord_dirs="wave", - # ) - # response.set_wave_convention(**wave.wave_convention) - - # freq_expect = wave._freq - # dirs_expect = wave._dirs - (np.pi / 4.0) - # vals_expect = ( - # 1.0 - # / (2.0 * np.pi * np.pi / 180.0) - # * np.array( - # [ - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # ] - # ) - # ) - - # assert isinstance(response, DirectionalSpectrum) - # assert response._freq_hz is False - # assert response._degrees is False - # np.testing.assert_array_almost_equal(response._freq, freq_expect) - # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - # np.testing.assert_array_almost_equal(response._vals, vals_expect) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from - - # def test_calculate_response(self, rao, wave): - # response = calculate_response(rao, wave, 0.0) - # assert isinstance(response, DirectionalSpectrum) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from - # assert response._freq_hz is False - # assert response._degrees is False - def test_calculate_response_raises_coord_freq(self, rao, wave): # TODO: coord_freq and coord_dirs deprecated. Remove test in future. with pytest.raises(ValueError): @@ -5701,127 +5628,6 @@ def test_calculate_response_coord_rao(self): assert response._clockwise == rao._clockwise assert response._waves_coming_from == rao._waves_coming_from - # def test_calculate_response_heading_degrees(self): - # freq_rao = np.array([0.0, 0.5, 1.0]) - # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - # vals_rao = np.array( - # [ - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # ] - # ) # all amplitudes are 1 - # rao = RAO( - # freq_rao, - # dirs_rao, - # vals_rao, - # freq_hz=True, - # degrees=True, - # ) - - # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) - # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - # wave = WaveSpectrum( - # freq_wave, - # dirs_wave, - # vals_wave, - # freq_hz=True, - # degrees=True, - # ) - - # response = calculate_response( - # rao, wave, 45.0, heading_degrees=True, coord_freq="wave", coord_dirs="wave" - # ) - # response.set_wave_convention(**wave.wave_convention) - - # freq_expect = wave._freq - # dirs_expect = wave._dirs - (np.pi / 4.0) - # vals_expect = ( - # 1.0 - # / (2.0 * np.pi * np.pi / 180.0) - # * np.array( - # [ - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # ] - # ) - # ) - - # assert isinstance(response, DirectionalSpectrum) - # assert response._freq_hz is False - # assert response._degrees is False - # np.testing.assert_array_almost_equal(response._freq, freq_expect) - # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - # np.testing.assert_array_almost_equal(response._vals, vals_expect) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from - - # def test_calculate_response_heading_radians(self): - # freq_rao = np.array([0.0, 0.5, 1.0]) - # dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) - # vals_rao = np.array( - # [ - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # [1.0 + 0.0j, 0.0 + 1.0j, 1.0 + 0.0j, 0.0 + 1.0j], - # ] - # ) # all amplitudes are 1 - # rao = RAO( - # freq_rao, - # dirs_rao, - # vals_rao, - # freq_hz=True, - # degrees=True, - # ) - - # freq_wave = np.array([0.0, 0.3, 0.6, 0.9]) # extrapolation needed - # dirs_wave = np.array([90.0, 180.0, 270.0, 359.0]) - # vals_wave = np.ones((len(freq_wave), len(dirs_wave))) - # wave = WaveSpectrum( - # freq_wave, - # dirs_wave, - # vals_wave, - # freq_hz=True, - # degrees=True, - # ) - - # response = calculate_response( - # rao, - # wave, - # np.pi / 4.0, - # heading_degrees=False, - # coord_freq="wave", - # coord_dirs="wave", - # ) - # response.set_wave_convention(**wave.wave_convention) - - # freq_expect = wave._freq - # dirs_expect = wave._dirs - (np.pi / 4.0) - # vals_expect = ( - # 1.0 - # / (2.0 * np.pi * np.pi / 180.0) - # * np.array( - # [ - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # [1.0, 1.0, 1.0, 1.0], - # ] - # ) - # ) - - # assert isinstance(response, DirectionalSpectrum) - # assert response._freq_hz is False - # assert response._degrees is False - # np.testing.assert_array_almost_equal(response._freq, freq_expect) - # np.testing.assert_array_almost_equal(response._dirs, dirs_expect) - # np.testing.assert_array_almost_equal(response._vals, vals_expect) - # assert response._clockwise == rao._clockwise - # assert response._waves_coming_from == rao._waves_coming_from - class Test__check_is_similar: def test_check_is_similar(self): From 3979277fcc0f35d456e6f7538379aed370f0ce73 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:47:52 +0200 Subject: [PATCH 24/35] refactor test --- tests/test_core.py | 62 ++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index e714451..949a1f9 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5426,7 +5426,7 @@ def rao(self): freq_hz=False, degrees=False, waves_coming_from=True, - clockwise=True, + clockwise=False, ) return rao @@ -5443,45 +5443,47 @@ def wave(self): vals, freq_hz=False, degrees=False, - waves_coming_from=True, + waves_coming_from=False, clockwise=True, ) return wave def test_calculate_response(self, rao, wave): - response = calculate_response(rao, wave, 0.0, reshape="rao") - - a, b, c = 2.0, 3.0, 4.0 - f, theta = wave._freq[:, np.newaxis], wave._dirs[np.newaxis, :] - vals_amp = a * f + b * theta + c + response = calculate_response(rao, wave, np.radians(45.0)) - vals_expect = wave._vals * vals_amp**2 + # Expected response + wave_body = wave.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_squared_expect = (rao * rao.conjugate()).real + rao_squared_expect = rao_squared_expect.reshape(freq_expect, dirs_expect, freq_hz=False, degrees=False) + response_expect = wr.multiply(rao_squared_expect, wave_body, "DirectionalSpectrum") assert isinstance(response, wr.DirectionalSpectrum) - assert response._clockwise is rao._clockwise - assert response._waves_coming_from is rao._waves_coming_from - assert response._freq_hz is False - assert response._degrees is False - np.testing.assert_allclose(response._freq, wave._freq) - np.testing.assert_allclose(response._dirs, wave._dirs) - np.testing.assert_allclose(response._vals, vals_expect) - - def test_calculate_response_heading_degrees(self, rao, wave): - response = calculate_response(rao, wave, 5, heading_degrees=True) - np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) - - def test_calculate_response_heading_radians(self, rao, wave): - response = calculate_response(rao, wave, np.radians(5), heading_degrees=False) - np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) - - def test_calculate_response_wave_convention(self, rao, wave): - rao_convention = {"waves_coming_from": True, "clockwise": False} - wave_convention = {"waves_coming_from": False, "clockwise": True} - rao.set_wave_convention(**rao_convention) - wave.set_wave_convention(**wave_convention) - response = calculate_response(rao, wave, np.pi / 4.0, heading_degrees=False) assert response._clockwise is False assert response._waves_coming_from is True + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + + # def test_calculate_response_heading_degrees(self, rao, wave): + # response = calculate_response(rao, wave, 5, heading_degrees=True) + # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) + + # def test_calculate_response_heading_radians(self, rao, wave): + # response = calculate_response(rao, wave, np.radians(5), heading_degrees=False) + # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) + + # def test_calculate_response_wave_convention(self, rao, wave): + # rao_convention = {"waves_coming_from": True, "clockwise": False} + # wave_convention = {"waves_coming_from": False, "clockwise": True} + # rao.set_wave_convention(**rao_convention) + # wave.set_wave_convention(**wave_convention) + # response = calculate_response(rao, wave, np.pi / 4.0, heading_degrees=False) + # assert response._clockwise is False + # assert response._waves_coming_from is True def test_calculate_response_raises_coord_freq(self, rao, wave): # TODO: coord_freq and coord_dirs deprecated. Remove test in future. From d3b3aba4f9aad190a191129590dd9804c1559f27 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:49:19 +0200 Subject: [PATCH 25/35] refactor test --- tests/test_core.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 949a1f9..2e62e9a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5416,7 +5416,9 @@ def rao(self): a, b, c = 2.0, 3.0, 4.0 vals_amp = a * freq[:, np.newaxis] + b * dirs[np.newaxis, :] + c - vals_phase = np.zeros((len(freq), len(dirs))) + vals_phase = np.linspace(0, 2 * np.pi, len(freq) * len(dirs)).reshape( + len(freq), len(dirs) + ) rao = RAO.from_amp_phase( freq, @@ -5456,8 +5458,12 @@ def test_calculate_response(self, rao, wave): wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) freq_expect, dirs_expect = wave_body._freq, wave_body._dirs rao_squared_expect = (rao * rao.conjugate()).real - rao_squared_expect = rao_squared_expect.reshape(freq_expect, dirs_expect, freq_hz=False, degrees=False) - response_expect = wr.multiply(rao_squared_expect, wave_body, "DirectionalSpectrum") + rao_squared_expect = rao_squared_expect.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalSpectrum" + ) assert isinstance(response, wr.DirectionalSpectrum) assert response._clockwise is False From 385d128fcdcee2ff0493bd9dcdccbe811ab1059c Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:53:03 +0200 Subject: [PATCH 26/35] test calculate_response bin --- tests/test_core.py | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/tests/test_core.py b/tests/test_core.py index 2e62e9a..4ffb607 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5434,7 +5434,7 @@ def rao(self): @pytest.fixture def wave(self): - rng = np.random.default_rng(123) + rng = np.random.default_rng(1) freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad vals = rng.random((len(freq), len(dirs))) @@ -5450,6 +5450,24 @@ def wave(self): ) return wave + @pytest.fixture + def wavebin(self): + rng = np.random.default_rng(2) + freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s + dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad + vals = rng.random((len(freq), len(dirs))) + + wave = WaveBinSpectrum( + freq, + dirs, + vals, + freq_hz=False, + degrees=False, + waves_coming_from=False, + clockwise=True, + ) + return wave + def test_calculate_response(self, rao, wave): response = calculate_response(rao, wave, np.radians(45.0)) @@ -5474,6 +5492,30 @@ def test_calculate_response(self, rao, wave): np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) + def test_calculate_response_bin(self, rao, wavebin): + response = calculate_response(rao, wavebin, np.radians(45.0)) + + # Expected response + wave_body = wavebin.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_squared_expect = (rao * rao.conjugate()).real + rao_squared_expect = rao_squared_expect.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalBinSpectrum" + ) + + assert isinstance(response, wr.DirectionalBinSpectrum) + assert response._clockwise is False + assert response._waves_coming_from is True + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + # def test_calculate_response_heading_degrees(self, rao, wave): # response = calculate_response(rao, wave, 5, heading_degrees=True) # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) From e0e8b2c6cfd1dd0091249c8482346b18d03d34cb Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:56:33 +0200 Subject: [PATCH 27/35] more tests --- tests/test_core.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 4ffb607..ddf21a4 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5516,6 +5516,78 @@ def test_calculate_response_bin(self, rao, wavebin): np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) + def test_calculate_response_heading_degrees(self, rao, wave): + response = calculate_response(rao, wave, 45, heading_degrees=True) + + # Expected response + wave_body = wave.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_squared_expect = (rao * rao.conjugate()).real + rao_squared_expect = rao_squared_expect.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalSpectrum" + ) + + assert isinstance(response, wr.DirectionalSpectrum) + assert response._clockwise is False + assert response._waves_coming_from is True + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + + def test_calculate_response_heading_radians(self, rao, wave): + response = calculate_response(rao, wave, np.radians(45), heading_degrees=False) + + # Expected response + wave_body = wave.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_squared_expect = (rao * rao.conjugate()).real + rao_squared_expect = rao_squared_expect.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalSpectrum" + ) + + assert isinstance(response, wr.DirectionalSpectrum) + assert response._clockwise is False + assert response._waves_coming_from is True + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + + def test_calculate_response_reshape_rao_squared(self, rao, wave): + response = calculate_response(rao, wave, np.radians(45), reshape="rao_squared") + + # Expected response + wave_body = wave.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_squared_expect = (rao * rao.conjugate()).real + rao_squared_expect = rao_squared_expect.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalSpectrum" + ) + + assert isinstance(response, wr.DirectionalSpectrum) + assert response._clockwise is False + assert response._waves_coming_from is True + assert response._freq_hz is False + assert response._degrees is False + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + # def test_calculate_response_heading_degrees(self, rao, wave): # response = calculate_response(rao, wave, 5, heading_degrees=True) # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) From ae79e1ee988a2d219b15b95b583c7182929ef8dd Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:57:51 +0200 Subject: [PATCH 28/35] testing --- tests/test_core.py | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index ddf21a4..3b9b6cc 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5531,11 +5531,6 @@ def test_calculate_response_heading_degrees(self, rao, wave): rao_squared_expect, wave_body, "DirectionalSpectrum" ) - assert isinstance(response, wr.DirectionalSpectrum) - assert response._clockwise is False - assert response._waves_coming_from is True - assert response._freq_hz is False - assert response._degrees is False np.testing.assert_allclose(response._freq, response_expect._freq) np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) @@ -5555,11 +5550,6 @@ def test_calculate_response_heading_radians(self, rao, wave): rao_squared_expect, wave_body, "DirectionalSpectrum" ) - assert isinstance(response, wr.DirectionalSpectrum) - assert response._clockwise is False - assert response._waves_coming_from is True - assert response._freq_hz is False - assert response._degrees is False np.testing.assert_allclose(response._freq, response_expect._freq) np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) @@ -5579,11 +5569,6 @@ def test_calculate_response_reshape_rao_squared(self, rao, wave): rao_squared_expect, wave_body, "DirectionalSpectrum" ) - assert isinstance(response, wr.DirectionalSpectrum) - assert response._clockwise is False - assert response._waves_coming_from is True - assert response._freq_hz is False - assert response._degrees is False np.testing.assert_allclose(response._freq, response_expect._freq) np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) From 287023e12551258b36f0734948697b1b6145a946 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 09:59:26 +0200 Subject: [PATCH 29/35] testing --- tests/test_core.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 3b9b6cc..2b1f931 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5564,7 +5564,26 @@ def test_calculate_response_reshape_rao_squared(self, rao, wave): rao_squared_expect = (rao * rao.conjugate()).real rao_squared_expect = rao_squared_expect.reshape( freq_expect, dirs_expect, freq_hz=False, degrees=False + ) # reshape squared RAO + response_expect = wr.multiply( + rao_squared_expect, wave_body, "DirectionalSpectrum" ) + + np.testing.assert_allclose(response._freq, response_expect._freq) + np.testing.assert_allclose(response._dirs, response_expect._dirs) + np.testing.assert_allclose(response._vals, response_expect._vals) + + def test_calculate_response_reshape_rao(self, rao, wave): + response = calculate_response(rao, wave, np.radians(45), reshape="rao") + + # Expected response + wave_body = wave.rotate(45.0, degrees=True) + wave_body.set_wave_convention(waves_coming_from=True, clockwise=False) + freq_expect, dirs_expect = wave_body._freq, wave_body._dirs + rao_expect = rao.reshape( + freq_expect, dirs_expect, freq_hz=False, degrees=False + ) # reshape RAO + rao_squared_expect = (rao_expect * rao_expect.conjugate()).real response_expect = wr.multiply( rao_squared_expect, wave_body, "DirectionalSpectrum" ) From 0d24067ec251c3c1c29282fcfc3887f2fbb3e807 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:01:29 +0200 Subject: [PATCH 30/35] test raaises reshape --- tests/test_core.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_core.py b/tests/test_core.py index 2b1f931..2704018 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5592,6 +5592,10 @@ def test_calculate_response_reshape_rao(self, rao, wave): np.testing.assert_allclose(response._dirs, response_expect._dirs) np.testing.assert_allclose(response._vals, response_expect._vals) + def test_calculate_response_reshape_raises(self, rao, wave): + with pytest.raises(ValueError): + calculate_response(rao, wave, np.radians(45), reshape="invalid-value") + # def test_calculate_response_heading_degrees(self, rao, wave): # response = calculate_response(rao, wave, 5, heading_degrees=True) # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) From 3cfe5968b9dcf09794801a38d394b48eb45094ad Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:01:54 +0200 Subject: [PATCH 31/35] delete old commented out tests --- tests/test_core.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 2704018..1a48d48 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5596,23 +5596,6 @@ def test_calculate_response_reshape_raises(self, rao, wave): with pytest.raises(ValueError): calculate_response(rao, wave, np.radians(45), reshape="invalid-value") - # def test_calculate_response_heading_degrees(self, rao, wave): - # response = calculate_response(rao, wave, 5, heading_degrees=True) - # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) - - # def test_calculate_response_heading_radians(self, rao, wave): - # response = calculate_response(rao, wave, np.radians(5), heading_degrees=False) - # np.testing.assert_allclose(response._dirs, wave._dirs - np.radians(5)) - - # def test_calculate_response_wave_convention(self, rao, wave): - # rao_convention = {"waves_coming_from": True, "clockwise": False} - # wave_convention = {"waves_coming_from": False, "clockwise": True} - # rao.set_wave_convention(**rao_convention) - # wave.set_wave_convention(**wave_convention) - # response = calculate_response(rao, wave, np.pi / 4.0, heading_degrees=False) - # assert response._clockwise is False - # assert response._waves_coming_from is True - def test_calculate_response_raises_coord_freq(self, rao, wave): # TODO: coord_freq and coord_dirs deprecated. Remove test in future. with pytest.raises(ValueError): From fe9da53e574a49029675b9edbb46609e84dfde86 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:02:49 +0200 Subject: [PATCH 32/35] small fix --- tests/test_core.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 1a48d48..55a8f1c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5434,10 +5434,9 @@ def rao(self): @pytest.fixture def wave(self): - rng = np.random.default_rng(1) freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad - vals = rng.random((len(freq), len(dirs))) + vals = np.random.default_rng(1).random((len(freq), len(dirs))) wave = WaveSpectrum( freq, From a471c301c518c8d3ff73219b23cb6b10a0d5dc31 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:05:52 +0200 Subject: [PATCH 33/35] deprecated comment --- tests/test_core.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 55a8f1c..7027364 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5596,17 +5596,17 @@ def test_calculate_response_reshape_raises(self, rao, wave): calculate_response(rao, wave, np.radians(45), reshape="invalid-value") def test_calculate_response_raises_coord_freq(self, rao, wave): - # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + # TODO: Deprecated functionality. Remove test in future. with pytest.raises(ValueError): calculate_response(rao, wave, 0.0, coord_freq="invalid-value") def test_calculate_response_raises_coord_dirs(self, rao, wave): - # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + # TODO: Deprecated functionality. Remove test in future. with pytest.raises(ValueError): calculate_response(rao, wave, 0.0, coord_freq="invalid-value") def test_calculate_response_coord_wave(self): - # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + # TODO: Deprecated functionality. Remove test in future. freq_rao = np.array([0.0, 0.5, 1.0]) dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) vals_rao = np.array( @@ -5679,7 +5679,7 @@ def test_calculate_response_coord_wave(self): np.testing.assert_array_almost_equal(response._vals, vals_expect) def test_calculate_response_coord_rao(self): - # TODO: coord_freq and coord_dirs deprecated. Remove test in future. + # TODO: Deprecated functionality. Remove test in future. freq_rao = np.array([0.0, 0.5, 1.0]) dirs_rao = np.array([45.0, 135.0, 225.0, 315.0]) vals_rao = np.array( From 873e2b00a55b14e67845dd3ca93519ce00c6e8ac Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:24:19 +0200 Subject: [PATCH 34/35] refactor fixtures --- tests/test_core.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 7027364..f351a4c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5411,11 +5411,11 @@ def test_dirm_rad(self, d, s, mean_dir_rad): class Test_calculate_response: @pytest.fixture def rao(self): - freq = 2.0 * np.pi * np.array([0.01, 0.2, 0.4, 0.6, 1.2]) # rad/s - dirs = np.pi / 180.0 * np.array([10.0, 45.0, 135.0, 225.0, 315.0]) # rad + freq = np.linspace(0.01, 2.0 * np.pi, 10, endpoint=False) # rad/s + dirs = np.linspace(0.0, 2.0 * np.pi, 15, endpoint=False) # rad - a, b, c = 2.0, 3.0, 4.0 - vals_amp = a * freq[:, np.newaxis] + b * dirs[np.newaxis, :] + c + # random function: 2.0 * f + 3.0 * theta + 4.0 + vals_amp = 2.0 * freq[:, np.newaxis] + 3.0 * dirs[np.newaxis, :] + 4.0 vals_phase = np.linspace(0, 2 * np.pi, len(freq) * len(dirs)).reshape( len(freq), len(dirs) ) @@ -5434,9 +5434,11 @@ def rao(self): @pytest.fixture def wave(self): - freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s - dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad - vals = np.random.default_rng(1).random((len(freq), len(dirs))) + freq = np.linspace(0.005, 2.0 * np.pi, 10, endpoint=False) # rad/s + dirs = np.linspace(0.5, 2.0 * np.pi, 15, endpoint=False) # rad + + # random function: f + sqrt(theta) + 7.0 + vals = freq[:, np.newaxis]**2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 wave = WaveSpectrum( freq, @@ -5451,10 +5453,11 @@ def wave(self): @pytest.fixture def wavebin(self): - rng = np.random.default_rng(2) - freq = 2.0 * np.pi * np.array([0.01, 0.3, 0.6, 0.9]) # rad/s - dirs = np.pi / 180.0 * np.array([10.0, 90.0, 180.0, 270.0]) # rad - vals = rng.random((len(freq), len(dirs))) + freq = np.linspace(0.005, 2.0 * np.pi, 10, endpoint=False) # rad/s + dirs = np.linspace(0.5, 2.0 * np.pi, 15, endpoint=False) # rad + + # random function: f + sqrt(theta) + 7.0 + vals = freq[:, np.newaxis]**2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 wave = WaveBinSpectrum( freq, From 30e7a2329f9b230d1d6410544fb14237d80f4150 Mon Sep 17 00:00:00 2001 From: "Vegard R. Solum" Date: Tue, 15 Apr 2025 10:28:36 +0200 Subject: [PATCH 35/35] black --- tests/test_core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index f351a4c..566d417 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5438,7 +5438,7 @@ def wave(self): dirs = np.linspace(0.5, 2.0 * np.pi, 15, endpoint=False) # rad # random function: f + sqrt(theta) + 7.0 - vals = freq[:, np.newaxis]**2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 + vals = freq[:, np.newaxis] ** 2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 wave = WaveSpectrum( freq, @@ -5457,7 +5457,7 @@ def wavebin(self): dirs = np.linspace(0.5, 2.0 * np.pi, 15, endpoint=False) # rad # random function: f + sqrt(theta) + 7.0 - vals = freq[:, np.newaxis]**2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 + vals = freq[:, np.newaxis] ** 2 + np.sqrt(dirs[np.newaxis, :]) + 7.0 wave = WaveBinSpectrum( freq,