5. Notebook exercise: Converting a JONSWAP spectrum into a time series#

In this interactive notebook you will try to convert a JONSWAP spectrum into a time series \(\eta(t)\). For this, you will use the MHKiT package.

Click –> Live Code on the top right corner of this screen and then wait until all cells are executed.

!pip install mhkit
import numpy as np
import matplotlib.pyplot as plt

class Cyl:
    def __init__(self, x0, z0, R, dth):
        thMat = np.arange(0, 360, dth) #deg
        cx = x0 + R*np.cos( np.deg2rad(thMat) )
        cz = z0 + R*np.sin( np.deg2rad(thMat) )
        self.R = R
        self.D = 2*R
        self.x0 = x0
        self.z0 = z0
        self.cx = cx
        self.cz = cz
        self.NodeC = [ [x, z] for x,z in zip (cx, cz) ]
        self.nNode = len(self.NodeC)
        self.Ele = [ [n1, n2] for n1,n2 in 
                    zip( range(0,self.nNode-1), range(1,self.nNode)) ]
        self.Ele.append([self.nNode-1, 0])
        self.nEle = len(self.Ele)
        self.P = np.zeros(self.nNode)
        
    def plotEle(self):
        for iEle in range(0,self.nEle):
            n1, n2 = self.Ele[iEle]
            n1 = int(round(n1))
            n2 = int(round(n2))
            plt.plot( [self.NodeC[n1][0], self.NodeC[n2][0]],
                     [self.NodeC[n1][1], self.NodeC[n2][1]],
                     lw=3, color='k')
Requirement already satisfied: mhkit in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (0.9.0)
Requirement already satisfied: numpy>=2.0.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2.2.5)
Requirement already satisfied: pandas>=2.2.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2.2.3)
Requirement already satisfied: scipy>=1.14.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (1.15.2)
Requirement already satisfied: xarray>=2024.6.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2025.4.0)
Requirement already satisfied: matplotlib>=3.9.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (3.10.1)
Requirement already satisfied: scikit-learn>=1.5.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (1.6.1)
Requirement already satisfied: h5py>=3.11.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (3.13.0)
Requirement already satisfied: h5pyd>=0.18.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (0.21.0)
Requirement already satisfied: netCDF4>=1.7.1.post1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (1.7.2)
Requirement already satisfied: statsmodels>=0.14.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (0.14.4)
Requirement already satisfied: requests in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2.32.3)
Requirement already satisfied: pecos>=0.3.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (1.0.0)
Requirement already satisfied: fatpack in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (0.7.8)
Requirement already satisfied: NREL-rex>=0.2.63 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (0.3.2)
Requirement already satisfied: pytz in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2025.2)
Requirement already satisfied: beautifulsoup4 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (4.13.4)
Requirement already satisfied: numexpr>=2.10.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (2.10.2)
Requirement already satisfied: lxml in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (5.4.0)
Requirement already satisfied: bottleneck in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from mhkit) (1.4.2)
Requirement already satisfied: requests_unixsocket in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from h5pyd>=0.18.0->mhkit) (0.4.1)
Requirement already satisfied: pyjwt in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from h5pyd>=0.18.0->mhkit) (2.10.1)
Requirement already satisfied: packaging in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from h5pyd>=0.18.0->mhkit) (24.2)
Requirement already satisfied: contourpy>=1.0.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (1.3.2)
Requirement already satisfied: cycler>=0.10 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (0.12.1)
Requirement already satisfied: fonttools>=4.22.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (4.57.0)
Requirement already satisfied: kiwisolver>=1.3.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (1.4.8)
Requirement already satisfied: pillow>=8 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (11.1.0)
Requirement already satisfied: pyparsing>=2.3.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (3.2.3)
Requirement already satisfied: python-dateutil>=2.7 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from matplotlib>=3.9.1->mhkit) (2.9.0.post0)
Requirement already satisfied: cftime in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from netCDF4>=1.7.1.post1->mhkit) (1.6.4)
Requirement already satisfied: certifi in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from netCDF4>=1.7.1.post1->mhkit) (2025.11.12)
Requirement already satisfied: click<9,>=8.1.8 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (8.1.8)
Requirement already satisfied: fsspec<2025,>=2021.09.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (2023.12.2)
Requirement already satisfied: dask<2025,>=2024.8.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (2024.12.1)
Requirement already satisfied: psutil<8,>=7.0.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (7.0.0)
Requirement already satisfied: PyYAML<7,>=6.0.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (6.0.2)
Requirement already satisfied: s3fs<2024,>=2023.6.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (2023.12.2)
Requirement already satisfied: toml<0.11,>=0.10.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from NREL-rex>=0.2.63->mhkit) (0.10.2)
Requirement already satisfied: tzdata>=2022.7 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from pandas>=2.2.2->mhkit) (2025.2)
Requirement already satisfied: jinja2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from pecos>=0.3.0->mhkit) (3.1.6)
Requirement already satisfied: pytest in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from pecos>=0.3.0->mhkit) (8.3.5)
Requirement already satisfied: joblib>=1.2.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from scikit-learn>=1.5.1->mhkit) (1.5.0)
Requirement already satisfied: threadpoolctl>=3.1.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from scikit-learn>=1.5.1->mhkit) (3.6.0)
Requirement already satisfied: patsy>=0.5.6 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from statsmodels>=0.14.2->mhkit) (1.0.1)
Requirement already satisfied: soupsieve>1.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from beautifulsoup4->mhkit) (2.7)
Requirement already satisfied: typing-extensions>=4.0.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from beautifulsoup4->mhkit) (4.13.2)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from requests->mhkit) (3.4.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from requests->mhkit) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from requests->mhkit) (2.4.0)
Requirement already satisfied: cloudpickle>=3.0.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from dask<2025,>=2024.8.0->NREL-rex>=0.2.63->mhkit) (3.1.1)
Requirement already satisfied: partd>=1.4.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from dask<2025,>=2024.8.0->NREL-rex>=0.2.63->mhkit) (1.4.2)
Requirement already satisfied: toolz>=0.10.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from dask<2025,>=2024.8.0->NREL-rex>=0.2.63->mhkit) (1.0.0)
Requirement already satisfied: six>=1.5 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from python-dateutil>=2.7->matplotlib>=3.9.1->mhkit) (1.17.0)
Requirement already satisfied: aiobotocore<3.0.0,>=2.5.4 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (2.22.0)
Requirement already satisfied: aiohttp!=4.0.0a0,!=4.0.0a1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (3.11.18)
Requirement already satisfied: MarkupSafe>=2.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from jinja2->pecos>=0.3.0->mhkit) (3.0.2)
Requirement already satisfied: iniconfig in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from pytest->pecos>=0.3.0->mhkit) (2.1.0)
Requirement already satisfied: pluggy<2,>=1.5 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from pytest->pecos>=0.3.0->mhkit) (1.5.0)
Requirement already satisfied: aioitertools<1.0.0,>=0.5.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiobotocore<3.0.0,>=2.5.4->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (0.12.0)
Requirement already satisfied: botocore<1.37.4,>=1.37.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiobotocore<3.0.0,>=2.5.4->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.37.3)
Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiobotocore<3.0.0,>=2.5.4->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.0.1)
Requirement already satisfied: multidict<7.0.0,>=6.0.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiobotocore<3.0.0,>=2.5.4->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (6.4.3)
Requirement already satisfied: wrapt<2.0.0,>=1.10.10 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiobotocore<3.0.0,>=2.5.4->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.17.2)
Requirement already satisfied: aiohappyeyeballs>=2.3.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (2.6.1)
Requirement already satisfied: aiosignal>=1.1.2 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.3.2)
Requirement already satisfied: attrs>=17.3.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (25.3.0)
Requirement already satisfied: frozenlist>=1.1.1 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.6.0)
Requirement already satisfied: propcache>=0.2.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (0.3.1)
Requirement already satisfied: yarl<2.0,>=1.17.0 in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from aiohttp!=4.0.0a0,!=4.0.0a1->s3fs<2024,>=2023.6.0->NREL-rex>=0.2.63->mhkit) (1.20.0)
Requirement already satisfied: locket in /opt/miniconda3/envs/prob_design_25/lib/python3.12/site-packages (from partd>=1.4.0->dask<2025,>=2024.8.0->NREL-rex>=0.2.63->mhkit) (1.0.0)

5.1. 1. Generate a wave spectrum using MHKiT#

Hint: use the mhkit.wave.resource.jonswap_spectrum() package

def get_spec(w, Tp, Hs):
    q = # your code here
    return q.to_numpy()/2/ np.pi
  Cell In[2], line 2
    q = # your code here
        ^
SyntaxError: invalid syntax
Hm0 = 8.0  # Significant wave height [m]
Tp = 10.0  # Peak period [s]

omega = # your code here
S = # your code here

plt.plot(omega, S)
plt.show()

5.2. 2. Get the amplitude spectrum for the power spectral density function#

Definition of single-sided power spectral density function is given by $\( \sum_f^{f+\Delta f} \frac{1}{2} a_n^2 = S_n(f) \Delta f \quad \)\( PSDF has a unit of \)m^2 \cdot s$

The single-sided amplitude spectrum is given by $\( a_n(f) = \sqrt{2 S_n(f) \Delta f} \)$

Here S.args\(= \omega\) and S.data= Spectral density value

def getAmpSPEC(omega, E, iseed=None):
    df = # your code here  # Frequency band width 
    A = # your code here
    n_omega = len(omega)

    # Seed for random phase    
    if iseed is not None:
        try:
            np.random.set_state(iseed)
        except (KeyError, TypeError):
            np.random.seed(iseed)
    ph = np.random.rand(n_omega) * 2 * np.pi - np.pi
    return A[2:], omega[2:], ph[2:]
  Cell In[7], line 2
    df = # your code here  # Frequency band width
         ^
SyntaxError: invalid syntax
A, omega, ph = getAmpSpec(omega, S, iseed=123)
# A, w, ph = getAmpSpec(spec, ns//2+1, iseed=123)

fig, ax = plt.subplots(1,1)
plt.plot(omega, A, color='r')
plt.title('Statistical amplitude spectrum')
plt.xlabel('$\omega$ (rad/s)')
plt.ylabel('$\eta_0$ (m)')
plt.grid('on')
plt.show()
<>:7: SyntaxWarning: invalid escape sequence '\o'
<>:8: SyntaxWarning: invalid escape sequence '\e'
<>:7: SyntaxWarning: invalid escape sequence '\o'
<>:8: SyntaxWarning: invalid escape sequence '\e'
/var/folders/4r/myg3jzhx1yd72kbqs5r4rv2w0000gn/T/ipykernel_66684/782426865.py:7: SyntaxWarning: invalid escape sequence '\o'
  plt.xlabel('$\omega$ (rad/s)')
/var/folders/4r/myg3jzhx1yd72kbqs5r4rv2w0000gn/T/ipykernel_66684/782426865.py:8: SyntaxWarning: invalid escape sequence '\e'
  plt.ylabel('$\eta_0$ (m)')
/var/folders/4r/myg3jzhx1yd72kbqs5r4rv2w0000gn/T/ipykernel_66684/782426865.py:7: SyntaxWarning: invalid escape sequence '\o'
  plt.xlabel('$\omega$ (rad/s)')
/var/folders/4r/myg3jzhx1yd72kbqs5r4rv2w0000gn/T/ipykernel_66684/782426865.py:8: SyntaxWarning: invalid escape sequence '\e'
  plt.ylabel('$\eta_0$ (m)')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[5], line 1
----> 1 A, omega, ph = getAmpSpec(omega, S, iseed=123)
      2 # A, w, ph = getAmpSpec(spec, ns//2+1, iseed=123)
      4 fig, ax = plt.subplots(1,1)

NameError: name 'getAmpSpec' is not defined

5.3. 3. Calculate the elevation profile#

We use LinearWave2D class from LinearWave package for defining the linear wave and the associated particle velocity, acceleration and pressure fields.

The linear wave theory only calculate the pressure and velocities for \(z<=0\).
In order to calculate the values for \(z>0\) we use

  • For Pressure: Taylor series expansion, limited to first order

  • For Velocities: Wheeler stretching link

We initiate LineaerWave object using amplitudes from JONSWAP spectrum and random phase. This will be used for obtaining the elevation, velocity and acceleration time-series for calculating the forces.

We have two classes

  • LinearWave2D: Class to define wave in shallow water

    • wv = LinearWave2D(rhoW, g, d, T, H)

    • rhoW: Density of water

    • g: acceleration due to gravity

    • d: still-water depth

    • T: Wave time-period

    • H: Wave-height

  • LinearWaveDeep2D: Class to define wave in deep water

    • wv = LinearWave2D(rhoW, g, T, H)

    • rhoW: Density of water

    • g: acceleration due to gravity

    • T: Wave time-period

    • H: Wave-height

Location in 2D space is defined as (x,z) The time instant is t

  • wv.waveElevation(t,x) : Wave elevation at a given x

  • wv.pressureTot(t,x,z) : Total pressure (static + dynamic pressure)

  • wv.pressureDyn(t,x,z) : Dynamic pressure

  • wv.particleVelPoi(t,x,z) : Wave particle vel at (x,z) at time-instant t

  • wv.particleAccPoi(t,x,z) : Wave particle acceleration at (x,z) at time-instant t

  • wv.particleVelMax(t,x,z) : Maximum of wave particle vel at location (x,z)

  • wv.particleAccMax(t,x,z) : Maximum of wave particle acceleration at location (x,z)

Here wv can be an object of LinearWave2D or LinearWaveDeep2D

from LinearWave import LinearWave2D
from LinearWave import LinearWaveDeep2D

# Wave(g, d, T, H, phi=0, x0=0)
d = 187 #m (still-water depth)
rhoW = 1025
g = 9.81
cyl1 = Cyl(10, -20, 5, 0.1)

wvAll = [ LinearWaveDeep2D(rhoW, g, 2*np.pi/wi, 2*Ai, phi, msg=False) for wi,Ai,phi in zip(w,A,ph) ]
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[8], line 1
----> 1 from LinearWave import LinearWave2D
      2 from LinearWave import LinearWaveDeep2D
      4 # Wave(g, d, T, H, phi=0, x0=0)

ModuleNotFoundError: No module named 'LinearWave'
def spec2ts(wvListIn, x0, z0, t):
    nt = len(t)
    et_t = np.zeros(nt) # wave elevation time series
    vx_t = np.zeros(nt) # horizontal particle velocity time series
    vz_t = np.zeros(nt) # vertical particle velocity time series
    vm_t = np.zeros(nt) # Velocity magnitude time series
    ax_t = np.zeros(nt) # Horizontal particle acceleration time series
    az_t = np.zeros(nt) # Vertical particle acceleration time series
    
    for i, ti in enumerate(t):
        et_t[i] = sum([wv.waveElevation(ti, x0) for wv in wvListIn])
        vel = np.array([wv.particleVelPoi(ti, x0, z0) for wv in wvListIn])
        vx_t[i] = np.sum(vel[:, 0])
        vz_t[i] = np.sum(vel[:, 1])
        vm_t[i] = np.sqrt( vx_t[i]**2 + vz_t[i]**2 )
        acc = np.array([wv.particleAccPoi(ti, x0, z0) for wv in wvListIn])
        ax_t[i] = np.sum(acc[:, 0])
        az_t[i] = np.sum(acc[:, 1])
    
    return et_t, vx_t, vz_t, vm_t, ax_t, az_t
fig, ax = plt.subplots(1,1)
plt.plot(t_t, et_t, color='r')
plt.title('Wave Elevation')
plt.xlabel('t (s)')
plt.ylabel('$\eta$ (m)')
plt.grid('on')

fig, ax = plt.subplots(1,1)
plt.plot(t_t, vx_t, color='r', label='vx')
plt.plot(t_t, vz_t, color='b', label='vz')
plt.plot(t_t, vm_t, color='g', label='vMag')
plt.title('Vel')
plt.xlabel('t (s)')
plt.ylabel('v (m/s)')
plt.grid('on')
plt.legend()

fig, ax = plt.subplots(1,1)
plt.plot(t_t, ax_t, color='r', label='ax')
plt.plot(t_t, az_t, color='b', label='az')
plt.title('Acc')
plt.xlabel('t (s)')
plt.ylabel('$\dot{v}$ (m/s2)')
plt.grid('on')
plt.legend()

plt.show()