Skip to content

Commit 03791a1

Browse files
committed
Added spectrum computation to 3D RBC
1 parent 495d5a5 commit 03791a1

File tree

2 files changed

+111
-14
lines changed

2 files changed

+111
-14
lines changed

pySDC/implementations/problem_classes/RayleighBenard3D.py

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ class RayleighBenard3D(GenericSpectralLinear):
2828
2929
The domain, vertical boundary conditions and pressure gauge are
3030
31-
Omega = [0, 8) x (-1, 1)
31+
Omega = [0, Lx) x [0, Ly] x (0, Lz)
3232
T(z=+1) = 0
33-
T(z=-1) = 2
33+
T(z=-1) = Lz
3434
u(z=+-1) = v(z=+-1) = 0
3535
integral over p = 0
3636
@@ -53,16 +53,16 @@ class RayleighBenard3D(GenericSpectralLinear):
5353
def __init__(
5454
self,
5555
Prandtl=1,
56-
Rayleigh=2e6,
57-
nx=256,
58-
ny=256,
59-
nz=64,
56+
Rayleigh=1e6,
57+
nx=64,
58+
ny=64,
59+
nz=32,
6060
BCs=None,
6161
dealiasing=1.5,
6262
comm=None,
6363
Lz=1,
64-
Lx=1,
65-
Ly=1,
64+
Lx=4,
65+
Ly=4,
6666
useGPU=False,
6767
**kwargs,
6868
):
@@ -79,7 +79,6 @@ def __init__(
7979
comm (mpi4py.Intracomm): Space communicator
8080
Lx (float): Horizontal length of the domain
8181
"""
82-
# TODO: documentation
8382
BCs = {} if BCs is None else BCs
8483
BCs = {
8584
'T_top': 0,
@@ -328,7 +327,7 @@ def compute_Nusselt_numbers(self, u):
328327
_me[0] = u_pad[iw] * u_pad[iT]
329328
wT_hat = self.transform(_me, padding=padding)[0]
330329

331-
nusselt_hat = wT_hat - DzT_hat
330+
nusselt_hat = (wT_hat / self.kappa - DzT_hat) * self.axes[-1].L
332331

333332
if not hasattr(self, '_zInt'):
334333
self._zInt = zAxis.get_integration_matrix()
@@ -354,3 +353,55 @@ def compute_Nusselt_numbers(self, u):
354353
't': Nusselt_t,
355354
'b': Nusselt_b,
356355
}
356+
357+
def get_frequency_spectrum(self, u):
358+
xp = self.xp
359+
indices = slice(0, 2)
360+
361+
# transform the solution to be in frequency space in x and y, but real space in z
362+
if self.spectral_space:
363+
u_hat = self.itransform(u, axes=(-1,))
364+
else:
365+
u_hat = self.transform(
366+
u,
367+
axes=(
368+
-3,
369+
-2,
370+
),
371+
)
372+
u_hat = self.spectral.redistribute(u_hat, axis=2, forward_output=False)
373+
374+
# compute "energy density" as absolute square of the velocity modes
375+
energy = (u_hat[indices] * xp.conjugate(u_hat[indices])).real / (self.axes[0].N ** 2 * self.axes[1].N ** 2)
376+
377+
# prepare wave numbers at which to compute the spectrum
378+
abs_kx = xp.abs(self.Kx[:, :, 0])
379+
abs_ky = xp.abs(self.Ky[:, :, 0])
380+
381+
unique_k = xp.unique(xp.append(xp.unique(abs_kx), xp.unique(abs_ky)))
382+
n_k = len(unique_k)
383+
384+
# compute local spectrum
385+
local_spectrum = self.xp.empty(shape=(2, energy.shape[3], n_k))
386+
for i, k in zip(range(n_k), unique_k):
387+
mask = xp.logical_or(abs_kx == k, abs_ky == k)
388+
local_spectrum[..., i] = xp.sum(energy[indices, mask, :], axis=1)
389+
390+
# assemble global spectrum from local spectra
391+
k_all = self.comm.allgather(unique_k)
392+
unique_k_all = []
393+
for k in k_all:
394+
unique_k_all = xp.unique(xp.append(unique_k_all, xp.unique(k)))
395+
n_k_all = len(unique_k_all)
396+
397+
spectra = self.comm.allgather(local_spectrum)
398+
spectrum = self.xp.zeros(shape=(2, self.axes[2].N, n_k_all))
399+
for ks, _spectrum in zip(k_all, spectra):
400+
ks = list(ks)
401+
unique_k_all = list(unique_k_all)
402+
for k in ks:
403+
index_global = unique_k_all.index(k)
404+
index_local = ks.index(k)
405+
spectrum[..., index_global] += _spectrum[..., index_local]
406+
407+
return xp.array(unique_k_all), spectrum

pySDC/tests/test_problems/test_RayleighBenard3D.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def test_eval_f(nx, nz, direction, spectral_space):
1111
import numpy as np
1212
from pySDC.implementations.problem_classes.RayleighBenard3D import RayleighBenard3D
1313

14-
P = RayleighBenard3D(nx=nx, ny=nx, nz=nz, Rayleigh=1, spectral_space=spectral_space)
14+
P = RayleighBenard3D(nx=nx, ny=nx, nz=nz, Rayleigh=1, spectral_space=spectral_space, Lx=1, Ly=1, Lz=1)
1515
iu, iv, iw, ip, iT = P.index(['u', 'v', 'w', 'p', 'T'])
1616
X, Y, Z = P.X, P.Y, P.Z
1717
cos, sin = np.cos, np.sin
@@ -297,7 +297,7 @@ def test_heterogeneous_implementation():
297297
def test_Nusselt_number_computation(w, N=4):
298298
from pySDC.implementations.problem_classes.RayleighBenard3D import RayleighBenard3D
299299

300-
prob = RayleighBenard3D(nx=N, ny=N, nz=N, dealiasing=1.0, spectral_space=False)
300+
prob = RayleighBenard3D(nx=N, ny=N, nz=N, dealiasing=1.0, spectral_space=False, Rayleigh=1.0, Prandtl=1.0)
301301
xp = prob.xp
302302
iw, iT = prob.index(['w', 'T'])
303303

@@ -341,12 +341,58 @@ def test_Nusselt_number_computation(w, N=4):
341341
assert xp.isclose(Nu[key], w), f'Expected Nu_{key}={w}, but got {Nu[key]} with constant T and perturbed w!'
342342

343343

344+
@pytest.mark.mpi4py
345+
@pytest.mark.mpi(ranks=[1, 2, 5])
346+
def test_spectrum_computation(mpi_ranks):
347+
from pySDC.implementations.problem_classes.RayleighBenard3D import RayleighBenard3D
348+
349+
N = 5
350+
prob = RayleighBenard3D(nx=N, ny=N, nz=2, dealiasing=1.0, spectral_space=False, Rayleigh=1.0)
351+
xp = prob.xp
352+
iu, iv = prob.index(['u', 'v'])
353+
354+
num_k = int(N // 2) + 1
355+
356+
u = prob.u_exact() * 0
357+
u[iu] = 1
358+
ks, spectrum = prob.get_frequency_spectrum(u)
359+
360+
expect_spectrum = xp.zeros((prob.nz, num_k))
361+
expect_spectrum[:, 0] = 1
362+
363+
assert len(ks) == num_k
364+
assert xp.allclose(spectrum[iv], 0)
365+
assert xp.allclose(spectrum[iu], expect_spectrum)
366+
367+
assert N >= 5
368+
u = prob.u_exact() * 0
369+
u[iu] = xp.sin(prob.Y * 2 * xp.pi / prob.axes[1].L)
370+
ks, spectrum = prob.get_frequency_spectrum(u)
371+
assert xp.allclose(spectrum[iu, :, 2:], 0)
372+
assert not xp.allclose(spectrum[iu, :, :2], 0)
373+
374+
assert N >= 5
375+
u = prob.u_exact() * 0
376+
u[iu] = xp.sin(prob.X * 2 * xp.pi / prob.axes[0].L) * xp.sin(prob.Y * 2 * xp.pi / prob.axes[1].L)
377+
ks, spectrum = prob.get_frequency_spectrum(u)
378+
assert xp.allclose(spectrum[iu, :, 2:], 0)
379+
assert xp.allclose(spectrum[iu, :, 0], 0)
380+
assert not xp.allclose(spectrum[iu, :, 1], 0)
381+
382+
assert N >= 5
383+
u = prob.u_exact() * 0
384+
u[iu] = xp.sin(prob.X * 4 * xp.pi / prob.axes[0].L) * xp.sin(prob.Y * 2 * xp.pi / prob.axes[1].L)
385+
ks, spectrum = prob.get_frequency_spectrum(u)
386+
assert not xp.allclose(spectrum[iu, :, 1:], 0)
387+
assert xp.allclose(spectrum[iu, :, 0], 0)
388+
389+
344390
if __name__ == '__main__':
345-
# test_eval_f(2**2, 2**1, 'x', False)
391+
test_eval_f(2**2, 2**1, 'x', False)
346392
# test_libraries()
347393
# test_Poisson_problems(4, 'u')
348394
# test_Poisson_problem_w()
349395
# test_solver_convergence('bicgstab+ilu', 32, False, True)
350396
# test_banded_matrix(False)
351397
# test_heterogeneous_implementation()
352-
test_Nusselt_number_computation(N=4, w=3.14)
398+
# test_Nusselt_number_computation(N=4, w=3.14)

0 commit comments

Comments
 (0)