Source code for fridom.nonhydro.initial_conditions.geostrophic_spectra

import fridom.nonhydro as nh

[docs] def geostrophic_energy_spectrum(kx, ky, kz, d=7, k0=6, c=2): """ Geostrophic energy spectrum. Description ----------- The geostrophic energy spectrum is separated into horizontal and vertical components. Following the work of Masur & Oliver [2020], the horizontal energy spectrum :math:`S_h` is given by: .. math:: S_h = \\frac{k^7}{\\left(k^2 + a k_0^2\\right)^{2b}} where :math:`k = \sqrt{k_x^2 + k_y^2}` is the horizontal wavenumber, :math:`a` and :math:`b` are constants: .. math:: a = \\frac{4}{7}b - 1, \quad b = \\frac{7+d}{4} where :math:`d` is the power law exponent for large horizontal wavenumbers (:math:`S_h(k) \sim k^{-d}` for :math:`k \\to \\infty`). The parameter :math:`k_0` is the wavenumber with the maximum energy. The vertical component :math:`S_v` is given by: .. math:: S_v = \\exp(-c|k_z|) where :math:`k_z` is the vertical wavenumber and :math:`c` is a constant. The total energy spectrum is given by :math:`S = S_h S_v`. Parameters ---------- `kx` : `float` The horizontal wavenumber in the x-direction. `ky` : `float` The horizontal wavenumber in the y-direction. `kz` : `float` The vertical wavenumber. `d` : `float`, optional (default=7) The power law exponent for large horizontal wavenumbers (:math:`S_h(k) \sim k^{-d}` for :math:`k \\to \\infty`). `k0` : `float`, optional (default=6) The wavenumber with the maximum energy. `c` : `float`, optional (default=2) The decay rate of the vertical energy spectrum. """ ncp = nh.config.ncp # horizontal spectra kh = ncp.sqrt(kx**2 + ky**2) b = (7.+d)/4. a = (4./7.)*b-1 h_spetra = kh**7/(kh**2 + a*k0**2)**(2*b) # vertical spectra v_spectra = ncp.exp(-c*ncp.abs(kz)) return h_spetra * v_spectra
[docs] class RandomGeostrophicSpectra(nh.State): """ Random geostrophic state with a given spectral energy density. Parameters ---------- `mset` : `ModelSettings` The model settings (need to be set up). `seed` : `int` Seed for the random number generator (for the phase) `spectral_energy_density` : `callable(kx, ky, kz)` Callable that returns the spectral energy density as a function of the wavenumbers `kx`, `ky`, and `kz`. Examples -------- .. code-block:: python import fridom.nonhydro as nh # Set up the model settings grid = nh.grid.cartesian.Grid( N=(128, 128, 32), L=(10, 10, 1), periodic_bounds=(False, True, False)) mset = nh.ModelSettings(grid=grid, f0=1, N2=1.0, dsqr=0.2**2) mset.time_stepper.dt = 0.1 mset.setup() # Create the initial conditions ic = nh.initial_conditions def spectra(kx, ky, kz): return ic.geostrophic_energy_spectrum(kx, ky, kz, c=0.2, k0=1.4, d=7) z = ic.RandomGeostrophicSpectra(mset, spectral_energy_density=spectra) * 0.1 # Create and run the model model = nh.Model(mset) model.z = z model.run(runlen=200.0) """
[docs] def __init__(self, mset: nh.ModelSettings, seed=12345, spectral_energy_density=geostrophic_energy_spectrum): super().__init__(mset, is_spectral=False) ncp = nh.config.ncp kx, ky, kz = mset.grid.K shape = kx.shape # construct the geostrophic eigenvectors q = mset.grid.vec_q(s=0, use_discrete=True) # scale the geostrophic eigenvector such that they have energy 1 abs = ncp.absolute dsqr = self.mset.dsqr N2 = self.mset.N2 # calculate spectral energy using Parseval's theorem energy = 0.5 * ( abs(q.u.arr)**2 + abs(q.v.arr)**2 + abs(q.w.arr)**2 * dsqr + abs(q.b.arr)**2 / N2 ) energy = ncp.where(energy == 0, 1, energy) q /= ncp.sqrt(energy) # construct a random phase p1 = nh.utils.random_array(shape, seed) p2 = nh.utils.random_array(shape, 2*seed) r = p1 + 1j * p2 # construct the spectral energy density spectra = spectral_energy_density(kx, ky, kz) # construct the geostrophic state z = q * r * ncp.sqrt(spectra) # transform to physical space and normalize the state such that the # maximum velocity is 1 z = z.ifft() scal = 1 / ncp.amax(z.u.arr) z *= scal # set the state self.fields = z.fields return