Calculate response spectrum#

This section will show you how to calculate a wave-induced first-order motion response spectrum using waveresponse. Linear theory and stationary conditions are assumed; then, the vessel’s frequency-domain response is described by a (motion) response amplitude operator (RAO) and a 2-D wave spectrum. Calculation of the response spectrum is governed by the following equation:

\[S_x(\omega) = \int H_x(\omega, \theta)H_x^{*}(\omega, \theta) S_{\zeta}(\omega, \theta) d\theta\]

where \(S_{\zeta}(\omega, \theta)\) is the 2-D wave spectrum, \(H_x(\omega, \theta)\) is the degree-of-freedom’s transfer function (i.e., RAO), \(H_x^{*}(\omega, \theta)\) is the complex conjugate version of the transfer function, and \(S_x(\omega)\) is the response spectrum. \(\theta\) is the relative wave direction, and \(\omega\) is the angular frequency.

Note

Keep in mind that the wave spectrum and the RAO must be given in compatible units when calculating response according to the above equation. E.g., for roll (\(\alpha\)):

\[S_{\alpha}(\omega) \left[\frac{rad^2}{Hz}\right] = \int_0^{2\pi} H_{\alpha}(\omega, \theta) \left[\frac{rad}{m}\right] \cdot H_{\alpha}^{*}(\omega, \theta) \left[\frac{rad}{m}\right] \cdot S_{\zeta}(\omega, \theta) \left[\frac{m^2}{Hz \cdot rad}\right] d\theta\]

With waveresponse it is easy to estimate a vessel’s response spectrum once you have an RAO object and a WaveSpectrum object available. A convenience function for calculating response is provided by calculate_response():

import waveresponse as wr


heading = 45.0   # degrees
response = wr.calculate_response(rao, wave, heading, heading_degrees=True)

This function is roughly equivalent to:

import waveresponse as wr


def calculate_response(rao, wave, heading, heading_degrees=False):
    """
    Calculate response spectrum.

    Parameters
    ----------
    rao : RAO
        Response amplitude operator (RAO).
    wave : WaveSpectrum
        Wave spectrum.
    heading : float
        Heading of vessel relative to wave spectrum reference frame.
    heading_degrees : bool
        Whether the heading is given in 'degrees'. If ``False``, 'radians' is assumed.

    Returns
    -------
    DirectionalSpectrum :
        Response spectrum.
    """

    # Rotate wave spectrum to 'body' frame
    wave_body = wave.rotate(heading, degrees=heading_degrees)

    # Ensure that ``rao`` and ``wave`` has the same 'wave convention'
    wave_body.set_wave_convention(**rao.wave_convention)

    # 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:
    #    https://cradpdf.drdc-rddc.gc.ca/PDFS/unc341/p811241_A1b.pdf
    freq = wave_body.freq(freq_hz=False)
    dirs = wave_body.dirs(degrees=False)
    rao_squared = (rao * rao.conjugate()).real
    rao_squared = rao_squared.reshape(freq, dirs, freq_hz=False, degrees=False)

    return wr.multiply(rao_squared, wave_body, output_type="DirectionalSpectrum")

The response is returned as a DirectionalSpectrum object, and provides useful spectrum operations, such as:

# Integrate over direction to get the 'non-directional' response spectrum
freq, response_spectrum = response.spectrum1d(axis=1)

# Calculate response variance
var = response.var()

# Calculate response standard deviation
std = response.std()

# Etc.

Note

calculate_response() returns the response as a two-dimentional spectrum calculated according to:

\[S_x(\omega, \theta) = H_x(\omega, \theta)H_x^{*}(\omega, \theta) S_{\zeta}(\omega, \theta)\]

To obtain the one-dimentional spectrum, you need to integrate over direction:

\[S_x(\omega) = \int S_x(\omega, \theta) d\theta\]

The response spectrum does not make much physical sense before it is integrated and converted to a 1-D non-directional spectrum. However, the 2-D version can indicate which wave directions are most important for the total response.