Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
yt-project
GitHub Repository: yt-project/yt
Path: blob/main/doc/source/cookbook/power_spectrum_example.py
928 views
1
import matplotlib.pyplot as plt
2
import numpy as np
3
4
import yt
5
6
"""
7
Make a turbulent KE power spectrum. Since we are stratified, we use
8
a rho**(1/3) scaling to the velocity to get something that would
9
look Kolmogorov (if the turbulence were fully developed).
10
11
Ultimately, we aim to compute:
12
13
1 ^ ^*
14
E(k) = integral - V(k) . V(k) dS
15
2
16
17
n ^
18
where V = rho U is the density-weighted velocity field, and V is the
19
FFT of V.
20
21
(Note: sometimes we normalize by 1/volume to get a spectral
22
energy density spectrum).
23
24
25
"""
26
27
28
def doit(ds):
29
# a FFT operates on uniformly gridded data. We'll use the yt
30
# covering grid for this.
31
32
max_level = ds.index.max_level
33
34
ref = int(np.prod(ds.ref_factors[0:max_level]))
35
36
low = ds.domain_left_edge
37
dims = ds.domain_dimensions * ref
38
39
nx, ny, nz = dims
40
41
nindex_rho = 1.0 / 3.0
42
43
Kk = np.zeros((nx // 2 + 1, ny // 2 + 1, nz // 2 + 1))
44
45
for vel in [("gas", "velocity_x"), ("gas", "velocity_y"), ("gas", "velocity_z")]:
46
Kk += 0.5 * fft_comp(
47
ds, ("gas", "density"), vel, nindex_rho, max_level, low, dims
48
)
49
50
# wavenumbers
51
L = (ds.domain_right_edge - ds.domain_left_edge).d
52
53
kx = np.fft.rfftfreq(nx) * nx / L[0]
54
ky = np.fft.rfftfreq(ny) * ny / L[1]
55
kz = np.fft.rfftfreq(nz) * nz / L[2]
56
57
# physical limits to the wavenumbers
58
kmin = np.min(1.0 / L)
59
kmax = np.min(0.5 * dims / L)
60
61
kbins = np.arange(kmin, kmax, kmin)
62
N = len(kbins)
63
64
# bin the Fourier KE into radial kbins
65
kx3d, ky3d, kz3d = np.meshgrid(kx, ky, kz, indexing="ij")
66
k = np.sqrt(kx3d**2 + ky3d**2 + kz3d**2)
67
68
whichbin = np.digitize(k.flat, kbins)
69
ncount = np.bincount(whichbin)
70
71
E_spectrum = np.zeros(len(ncount) - 1)
72
73
for n in range(1, len(ncount)):
74
E_spectrum[n - 1] = np.sum(Kk.flat[whichbin == n])
75
76
k = 0.5 * (kbins[0 : N - 1] + kbins[1:N])
77
E_spectrum = E_spectrum[1:N]
78
79
index = np.argmax(E_spectrum)
80
kmax = k[index]
81
Emax = E_spectrum[index]
82
83
plt.loglog(k, E_spectrum)
84
plt.loglog(k, Emax * (k / kmax) ** (-5.0 / 3.0), ls=":", color="0.5")
85
86
plt.xlabel(r"$k$")
87
plt.ylabel(r"$E(k)dk$")
88
89
plt.savefig("spectrum.png")
90
91
92
def fft_comp(ds, irho, iu, nindex_rho, level, low, delta):
93
cube = ds.covering_grid(level, left_edge=low, dims=delta, fields=[irho, iu])
94
95
rho = cube[irho].d
96
u = cube[iu].d
97
98
nx, ny, nz = rho.shape
99
100
# do the FFTs -- note that since our data is real, there will be
101
# too much information here. fftn puts the positive freq terms in
102
# the first half of the axes -- that's what we keep. Our
103
# normalization has an '8' to account for this clipping to one
104
# octant.
105
ru = np.fft.fftn(rho**nindex_rho * u)[
106
0 : nx // 2 + 1, 0 : ny // 2 + 1, 0 : nz // 2 + 1
107
]
108
ru = 8.0 * ru / (nx * ny * nz)
109
110
return np.abs(ru) ** 2
111
112
113
ds = yt.load("maestro_xrb_lores_23437")
114
doit(ds)
115
116