enh: vectorise spectral integrals and speed up spectrum construction - #86
Open
tvare wants to merge 1 commit into
Open
enh: vectorise spectral integrals and speed up spectrum construction#86tvare wants to merge 1 commit into
tvare wants to merge 1 commit into
Conversation
Bit-for-bit identical results, verified with np.array_equal on the full spectral-integration API before and after. - _freq_spectrum / _dir_spectrum: one trapezoid(..., axis=-1) call on a contiguous copy instead of a per-row Python loop. The copy keeps the summation order of the per-row loop, so results are unchanged. - tz: integrate out direction once for both moments (f**0 * s is exactly s, so this equals moment(0) / moment(2) bit for bit). - from_spectrum1d: reduce the direction offset once per direction instead of once per grid point; spread_fun is still evaluated per grid point with the same arguments. - _robust_modulus: scalar fast path with the same arithmetic, return type and error behaviour. On a 84x16 RAO grid with 300 wave frequencies and 60 sea states: building spectra 2.8 s -> 1.1 s, response std/tz for 12 components 3.5 s -> 0.8 s, 21 cross-spectral RAO pairs x 20 sea states 2.0 s -> 0.4 s. Tests pin equality against the per-row loop on a non-uniform direction grid, tz against the moment ratio, from_spectrum1d with a frequency-dependent spreading and wrapping peak direction, and the _robust_modulus scalar/array/non-finite behaviour. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
Hi, thanks for the contribution! I will have a look at this when I get the time. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four small internal speed-ups on the spectral-integration path. None of them changes any result: every patched routine is bit-for-bit identical to the current implementation (verified with
np.array_equal, notallclose), and the added tests pin that.DirectionalSpectrum._freq_spectrum/_SpectrumMixin._dir_spectrum— integrate all rows with onetrapezoid(..., axis=-1)call instead of a Python list comprehension callingtrapezoidonce per row. A contiguous copy of the (strided) interpolated array keeps NumPy's summation order identical to the per-row loop, so the result is unchanged to the last bit. This is the hot spot whenmoment(),var(),std(),tzorspectrum1d()are evaluated for many sea states: for a 300 × 16 response spectrum the row loop is ~0.35 ms per call; the vectorised form is ~0.09 ms._SpectrumMixin.tz— integrate out the directional domain once and take both moments from it, instead of callingmoment()twice.f**0 * sis exactlys, sotzequalssqrt(moment(0) / moment(2))bit for bit (pinned by a test).DirectionalSpectrum.from_spectrum1d— the direction offsettheta - theta_pdoes not depend on frequency, so it is reduced once per direction instead of once per grid point. The spreading function is still called per grid point with the same arguments, so results are unchanged for anyspread_fun, frequency-dependent or not._robust_modulus— scalar fast path (plain%,isfinite, conditionalnextafter) that avoidsasarray_chkfinite(...).copy()and thewhere=/out=machinery for 0-d inputs. Same arithmetic, same return type (0-dndarray), sameValueErroron non-finite input. This function is called once per grid point fromfrom_spectrum1dand fromBaseSpreading.__call__, so it dominated the cost of building spectra.Measured effect
Response analysis of one vessel (84 × 16 RAO grid, 300 wave frequencies, 60 sea states, 12 response components, serial):
WaveSpectrumviafrom_spectrum1d(cos² spreading)calculate_response+std()/tzfor 12 components × 60 sea statesAll 288 output arrays from that run (wave spectra,
std,tz,var,moment(1),moment(2, freq_hz=True),spectrum1d(axis=0/1), for bothWaveSpectrumandWaveBinSpectruminput) arenp.array_equalbefore and after.Tests
test_spectrum1d_axis1_equals_rowwise_integration/test_spectrum1d_axis0_equals_rowwise_integration(DirectionalSpectrumandDirectionalBinSpectrum): vectorised resultassert_array_equalto the per-rowtrapezoidloop on a non-uniform direction grid.test_tz_equals_moment_ratio(both spectrum classes):tz == sqrt(moment(0) / moment(2))exactly.test_from_spectrum1d_nonuniform_dirs_wrapping_dirp: frequency-dependent spreading, non-uniform directions, peak direction that wraps past 360°._robust_modulus: 0-d return type, 0-d array input, non-finite scalar and array inputs raiseValueError.Full suite: 690 passed;
black --checkandisort --check --profile blackclean.