Source code for fridom.shallowwater.initial_conditions.equatorial_wave
"""Initial conditions for equatorial waves on the beta-plane."""
from __future__ import annotations
import numpy as np
import fridom.shallowwater as sw
[docs]
class EquatorialWave(sw.State):
r"""
Equatorial wave on the beta-plane approximation.
Parameters
----------
mset : ModelSettings
Model settings.
longitudinal_mode : int
Longitudinal mode of the wave (how many wavelengths in the x-direction).
equatorial_mode : int
Equatorial mode of the wave (Hermite polynomial order).
wave_mode : int
Mode of the wave:
0 for the wave mode with negative frequency
1 for the rossby mode (smallest absolute frequency)
2 for the wave mode with positive frequency
phase : float, optional
Phase of the wave.
Description
-----------
The equatorial wave solutions follow from solving the eigenvalue problem of
the linearized shallow water equations on the equatorial beta-plane
:math:`f = \beta y`. The frequencies of the m-th eigenmode can be obtained by
solving
.. math::
\omega_m \left( \omega_m^2 - c^2
\left( k^2 + (2m + 1) \frac{\beta}{c} \right)
\right) = k \beta c^2
for :math:`\omega_m`.
The initial condition is then given by
.. math::
\mathbf z = \begin{pmatrix} u_m \\ v_m \\ p_m \end{pmatrix}
e^{- \frac{1}{2}\tilde{y}^2} e^{i(kx + \phi)}
\quad \text{with} \quad
\tilde{y} = \frac{y}{R_e}
\quad \text{and} \quad
R_e^2 = \frac{c}{\beta}
with
.. math::
\begin{align}
u_m &= \frac{i c}{2 R_e} \left( \frac{H_{m+1}(\tilde{y})}{\omega_m - c k}
+ \frac{2 m H_{m-1}(\tilde{y})}{\omega_m + c k} \right) \\
v_m &= H_m(\tilde{y}) \\
p_m &= \frac{i c^2}{2 R_e} \left( \frac{H_{m+1}(\tilde{y})}{\omega_m - c k}
- \frac{2 m H_{m-1}(\tilde{y})}{\omega_m + c k} \right)
\end{align}
where :math:`H_m` is the m-th Hermite polynomial, given by the recursive relation
.. math::
H_m(x) = 2x H_{m-1}(x) - 2(m-1) H_{m-2}(x)
with :math:`H_{-1}(x) = 0` and :math:`H_0(x) = 1`.
"""
[docs]
def __init__(self,
mset: sw.ModelSettings,
longitudinal_mode: int,
equatorial_mode: int,
wave_mode: int,
phase: float = 0,
) -> None:
super().__init__(mset, is_spectral=False)
# Shortcuts
ncp = sw.config.ncp
grid = mset.grid
pi = ncp.pi
# Compute physical parameters
csqr = mset.csqr
beta = mset.beta
phase_velocity = csqr ** 0.5
rossby_radius = (phase_velocity / beta) ** 0.5
eigenvalue = (2 * equatorial_mode + 1) / (rossby_radius ** 2)
y0 = grid.L[1] / 2
kx = 2 * pi / grid.L[0] * longitudinal_mode
# Compute the frequencies of the equatorial wave
coeffs = [1, 0, -csqr * (kx**2 + eigenvalue), -kx * beta * csqr]
omega = np.sort(np.roots(coeffs))[wave_mode]
period = np.abs(2 * np.pi / omega)
# Helper functions for the hermite polynomials
def hermite_polynomial(order: int,
x: float,
memory: dict | None = None) -> float:
"""Calculate the Hermite polynomial of given order at x."""
if memory is None:
memory = {-1: x * 0, 0: x * 0 + 1}
if order in memory:
return memory[order]
memory[order] = ( 2 * x * hermite_polynomial(order-1, x, memory)
- 2 * (order-1) * hermite_polynomial(order-2, x, memory) )
return memory[order]
def hermite_gaussian(order: int, x: float) -> float:
"""Calculate the Hermite-Gaussian function of given order at x."""
return hermite_polynomial(order, x) * np.exp(-x**2/2)
state = sw.State(mset)
u, v, p = state
# v
x, y = v.get_mesh()
y_star = (y - y0) / rossby_radius
v_structure = hermite_gaussian(equatorial_mode, y_star)
v.arr = ( v_structure * ncp.exp(1j * (kx * x + phase)) ).real
# u
x, y = u.get_mesh()
y_star = (y - y0) / rossby_radius
psi_np1 = hermite_gaussian(equatorial_mode + 1, y_star)
psi_nm1 = hermite_gaussian(equatorial_mode - 1, y_star)
u_structure = 1j * phase_velocity / (2 * rossby_radius) * (
psi_np1 / (omega - kx * phase_velocity)
+ 2 * equatorial_mode * psi_nm1 / (omega + kx * phase_velocity))
u.arr = ( u_structure * ncp.exp(1j * (kx * x + phase)) ).real
# p
x, y = p.get_mesh()
y_star = (y - y0) / rossby_radius
psi_np1 = hermite_gaussian(equatorial_mode + 1, y_star)
psi_nm1 = hermite_gaussian(equatorial_mode - 1, y_star)
p_structure = 1j * csqr / (2 * rossby_radius) * (
psi_np1 / (omega - kx * phase_velocity)
- 2 * equatorial_mode * psi_nm1 / (omega + kx * phase_velocity))
p.arr = ( p_structure * ncp.exp(1j * (kx * x + phase)) ).real
# Normalize the state
u_amp = (u**2 + v**2).max() ** 0.5
state /= u_amp
# Set the state
self.fields = state.fields
self.omega = omega
self.period = period