839 lines
28 KiB
Python
839 lines
28 KiB
Python
"""Compute power spectra"""
|
|
|
|
from __future__ import division
|
|
|
|
import argparse
|
|
import sys
|
|
import textwrap
|
|
|
|
import numpy as np
|
|
from numpy.fft import fftn, ifft
|
|
|
|
import pymses
|
|
from pymses.analysis import amr2cube
|
|
import pymses.utils.misc
|
|
|
|
# Don't use multiprocessing, it will crash with level 10 cubes
|
|
pymses.utils.misc.NUMBER_OF_PROCESSES_LIMIT = 1
|
|
|
|
import tables as T
|
|
|
|
from utils import args
|
|
from utils import tools
|
|
|
|
__generator__ = "pspec_new.py"
|
|
__version__ = "0.2"
|
|
|
|
|
|
def degrade_cube(cube, lvl, integrate=False):
|
|
"""Degrade a data cube to a coarser resolution
|
|
|
|
Parameters:
|
|
-----------
|
|
cube
|
|
(numpy.ndarray, ndim=3) input data cube, lengths in each direction must
|
|
be equal, and a power of 2
|
|
lvl
|
|
(int) level of the output cube (2**lvl values in each direction)
|
|
integrate
|
|
(bool, default False) if True, values are added instead of averaged
|
|
|
|
Return value:
|
|
-------------
|
|
degraded_cube
|
|
(numpy.ndarray, ndim=3) output data cube, 2**lvl values in each
|
|
direction
|
|
"""
|
|
|
|
assert cube.ndim == 3
|
|
|
|
nold = cube.shape[0]
|
|
nnew = 1 << lvl
|
|
assert nnew <= nold
|
|
nsum, rem = divmod(nold, nnew)
|
|
assert rem == 0
|
|
|
|
# For each direction, we split the corresponding axis (length nold) into
|
|
# 2 axes, the first one being the axis for the output cube (length nnew),
|
|
# and the second one the axis to average or integrate over (fine cells).
|
|
|
|
# reshape creates a view, no copy is involved
|
|
v = cube.reshape((nnew, nsum, nnew, nsum, nnew, nsum), order="C")
|
|
# Even indexes are kept, odd ones are to be summed
|
|
cube_new = np.einsum("iajbkc->ijk", v)
|
|
|
|
if not integrate:
|
|
cube_new /= nsum
|
|
|
|
return cube_new
|
|
|
|
|
|
def calc_k(n, nbinsk, nbig, dkbig, dim=3, saxis=2):
|
|
"""Make cubes containing the wave vectors, a list of bins and the
|
|
associated normalization factors
|
|
|
|
The wave vector bins are first linear, then logarithmic.
|
|
|
|
Parameters:
|
|
-----------
|
|
n
|
|
(int) number of points in each direction
|
|
nbinsk
|
|
(int) number of wave vector bins
|
|
nbig
|
|
(int) number of linear bins
|
|
dkbig
|
|
(float) linear bin width
|
|
dim
|
|
(int, default 3) dimension of the Fourier transform to be performed,
|
|
should be either 2 or 3
|
|
saxis
|
|
(int, default 2) for 2D Fourier transforms, the slicing axis (i.e. the
|
|
axis where the transform is NOT done), should be 0, 1 or 2
|
|
|
|
Return value:
|
|
-------------
|
|
cubes_k
|
|
(dict) Dictionary containing the wave vector cubes:
|
|
'kx', 'ky', 'kz'
|
|
Components of the wave vectors (in 2D, the one corresponding to
|
|
saxis is always 0)
|
|
'k'
|
|
Norm of the wave vector
|
|
kbins
|
|
(numpy.array) Wave vector bins (length = nbinsk + 1): bin `i` is
|
|
between `kbins[i]` and `kbins[i+1]`.
|
|
norm
|
|
(numpy.array) Number of points of the Fourier space in each wave vector
|
|
bin (length = nbinsk)
|
|
"""
|
|
|
|
k_alias = np.arange(n, dtype=np.float64)
|
|
k = np.where(k_alias >= n // 2, k_alias - n, k_alias)
|
|
|
|
if dim == 3:
|
|
kx, ky, kz = np.meshgrid(k, k, k, indexing="ij")
|
|
else:
|
|
a = [k, k, k]
|
|
a[saxis] = np.zeros_like(k)
|
|
kx, ky, kz = np.meshgrid(a[0], a[1], a[2], indexing="ij")
|
|
|
|
cube_k = np.sqrt(kx ** 2 + ky ** 2 + kz ** 2)
|
|
|
|
cubes_k = {"kx": kx, "ky": ky, "kz": kz, "k": cube_k}
|
|
|
|
kmin = 1.0
|
|
kmax = n // 2
|
|
|
|
nbins2 = nbinsk - nbig
|
|
kmid = kmin + nbig * dkbig
|
|
|
|
kbins = np.concatenate(
|
|
(
|
|
kmin + (kmid - kmin) * (np.arange(nbig, dtype=np.float) / nbig),
|
|
kmid * (kmax / kmid) ** (np.arange(nbins2 + 1, dtype=np.float) / nbins2),
|
|
)
|
|
)
|
|
|
|
norm, _ = np.histogram(cube_k, bins=kbins)
|
|
|
|
return cubes_k, kbins, norm
|
|
|
|
|
|
def helmholtz(cubes, var, update=False):
|
|
r"""Perform a Helmholtz decomposition of a vector field
|
|
|
|
The Helmholtz decomposition of a vector field $\vec{v}$ is the couple of
|
|
vector fields $\left(\vec{v}_c, \vec{v}_s\right)$, where $\vec{\nabla}
|
|
\cdot \vec{v}_s = 0$ and $\vec{\nabla} \cdot \vec{v}_c = \vec{0}$. 'c'
|
|
stands for compressive and 's' for solenoidal.
|
|
|
|
This routine uses the Fourier-domain projection method, where
|
|
$\hat{\vec{v}}_c$ is proportional to the wave vector $\vec{k}$, and
|
|
$\hat{\vec{v}}_s$ is orthogonal to $\vec{k}$. For the compressive
|
|
component, only the projection of the field on the wave vector is computed
|
|
|
|
Parameters:
|
|
-----------
|
|
cubes
|
|
(dict) 3D Fourier space data cubes containing the vector field (see
|
|
`var`) and the wave numbers (`'kx'`, `'ky'`, `'kz'`)
|
|
var
|
|
(str) name of the vector field, the components are assumed to
|
|
correspond to the `var + dim` key, where `dim` is `'x'`, `'y'` or `'z'`
|
|
update
|
|
(bool) if True, `cubes` is updated with the data in `hcubes`
|
|
|
|
Return value:
|
|
-------------
|
|
hcubes
|
|
(dict) the components of the compressive and solenoidal fields, in
|
|
Fourier space:
|
|
var + 'c'
|
|
(numpy.ndarray, ndim=3) the projection of the compressive component
|
|
on the unit wave vector
|
|
var + 's' + dim
|
|
(numpy.ndarray, ndim=3) the solenoidal component, for each `dim` in
|
|
`['x', 'y', 'z']`
|
|
"""
|
|
|
|
knorm = np.zeros_like(cubes["k"])
|
|
vpar = np.zeros_like(cubes[var + "x"])
|
|
for d in ["x", "y", "z"]:
|
|
vpar += cubes[var + d] * cubes["k" + d]
|
|
knorm += cubes["k" + d] ** 2
|
|
knorm = np.sqrt(knorm)
|
|
|
|
# Prevent NaNs
|
|
knorm[0, 0, 0] = 1.0
|
|
vpar[0, 0, 0] = 0.0
|
|
|
|
vpar /= knorm
|
|
|
|
hcubes = {}
|
|
hcubes[var + "c"] = vpar
|
|
|
|
for d in ["x", "y", "z"]:
|
|
hcubes[var + "s" + d] = cubes[var + d] - vpar * cubes["k" + d] / knorm
|
|
|
|
if update:
|
|
cubes.update(hcubes)
|
|
|
|
return hcubes
|
|
|
|
|
|
def avg_vect(cubes, var, dim=3, saxis=2):
|
|
"""Average a vector in the cube
|
|
|
|
In 3D, the output is a (3,)-array (global average).
|
|
In 2D, it is a (n, 3)-array (average by slices).
|
|
|
|
Parameters:
|
|
-----------
|
|
cubes
|
|
(dict) 3D data cubes containing the vector field (see `var`)
|
|
var
|
|
(str) name of the vector field, the components are assumed to
|
|
correspond to the `var + dim` key, where `dim` is `'x'`, `'y'` or `'z'`
|
|
dim
|
|
(int, default 3) dimension of the Fourier transform to be performed,
|
|
should be either 2 or 3
|
|
saxis
|
|
(int, default 2) for 2D Fourier transforms, the slicing axis (i.e. the
|
|
axis where the transform is NOT done), should be 0, 1 or 2
|
|
|
|
Return value:
|
|
-------------
|
|
avg
|
|
(numpy.ndarray) averaged vector, shape is (3,) in 3D, (n, 3) in 2D (n =
|
|
number of points along the slicing axis)
|
|
"""
|
|
if dim == 3:
|
|
vavg = np.zeros(3, dtype=np.float64)
|
|
for i, d in enumerate(["x", "y", "z"]):
|
|
vavg[i] = cubes[var + d].mean()
|
|
else:
|
|
vavg = np.zeros((cubes[var + "x"].shape[saxis], 3), dtype=np.float64)
|
|
axes = [0, 1, 2]
|
|
axes.remove(saxis)
|
|
for i, d in enumerate(["x", "y", "z"]):
|
|
vavg[:, i] = cubes[var + d].mean(axis=tuple(axes))
|
|
return vavg
|
|
|
|
|
|
def proj_B(cubes_k, kbins, vec, var="", dim=3, saxis=2, update=False):
|
|
r"""Project wave vectors parallel and perpendicular to a given vector
|
|
|
|
If the mean field value is zero, the parallel component is set to 0 and the
|
|
perpendicular component is the wave vector itself.
|
|
In 2D, `vec` can be either a (n, 3) or a (3,) shaped array, the additional
|
|
dimension is taken along the slice axis.
|
|
|
|
Parameters:
|
|
-----------
|
|
cubes_k
|
|
(dict) 3D data cube containing the wave numbers (`'kx'`, `'ky'`, `'kz'`)
|
|
kbins
|
|
(numpy.array) wave vector bin edges (length nbins + 1), see `calc_k`
|
|
vec
|
|
(numpy.array) 3D: vector to project on, 2D: vector or array of vectors
|
|
var
|
|
(str) name of the vector in the output
|
|
dim
|
|
(int, default 3) dimension of the Fourier transform to be performed,
|
|
should be either 2 or 3
|
|
saxis
|
|
(int, default 2) for 2D Fourier transforms, the slicing axis (i.e. the
|
|
axis where the transform is NOT done), should be 0, 1 or 2
|
|
update
|
|
(bool, default False) if True, `cubes_k` is updated with the data in
|
|
`proj_cubes`
|
|
|
|
Return value:
|
|
-------------
|
|
proj_cubes
|
|
(dict) the magnitudes of the projected wave vectors:
|
|
'k' + var + 'par'
|
|
(numpy.ndarray, ndim=3) the projection of the wave vector parallel
|
|
to the mean field
|
|
'k' + var + 'perp'
|
|
(numpy.ndarray, ndim=3) the projection of the wave vector
|
|
perpendicular to the mean field
|
|
knorm_par
|
|
(numpy.array) number of wave vectors in each bin for the parallel
|
|
component (length nbins), see `calc_k`
|
|
knorm_perp
|
|
(numpy.array) number of wave vectors in each bin for the perpendicular
|
|
component (length nbins), see `calc_k`
|
|
"""
|
|
|
|
# we want vec_z[..., saxis] to be set to 0 without changing the argument
|
|
vec_z = vec.copy()
|
|
if dim == 2:
|
|
vec_z[..., saxis] = 0.0
|
|
|
|
# we need vec_z to be indexed correctly: we create dummy axes in the FT
|
|
# directions
|
|
vind = [np.newaxis, np.newaxis, np.newaxis]
|
|
if dim == 2:
|
|
vind[saxis] = slice(None)
|
|
vind = tuple(vind)
|
|
vec_z = vec_z[vind + (slice(None),)]
|
|
|
|
vnorm = np.sqrt(np.sum(vec_z ** 2, axis=-1))
|
|
|
|
kpar = np.zeros_like(cubes_k["k"])
|
|
for i, d in enumerate(["x", "y", "z"]):
|
|
kpar += cubes_k["k" + d] * vec_z[..., i]
|
|
|
|
mask = np.logical_not(np.isclose(vnorm, 0))
|
|
# Broadcast to the right shape for bool indexing in kpar
|
|
mask_b = np.broadcast_to(mask, kpar.shape)
|
|
vnorm_b = np.broadcast_to(vnorm, kpar.shape)
|
|
# Normalize kpar and vnorm, correctly taking care of zeros
|
|
kpar[mask_b] /= vnorm_b[mask_b]
|
|
kpar[~mask_b] = 0
|
|
vec_z[mask, :] /= vnorm[mask, np.newaxis]
|
|
vec_z[~mask] = 0
|
|
|
|
proj_cubes = {}
|
|
proj_cubes["k" + var + "par"] = kpar
|
|
|
|
kvp = "k" + var + "perp"
|
|
proj_cubes[kvp] = np.zeros_like(kpar)
|
|
for i, d in enumerate(["x", "y", "z"]):
|
|
# note that vec_z[..., saxis] is 0 by construction
|
|
proj_cubes[kvp] += (cubes_k["k" + d] - kpar * vec_z[..., i]) ** 2
|
|
proj_cubes[kvp] = np.sqrt(proj_cubes[kvp])
|
|
|
|
if update:
|
|
cubes_k.update(proj_cubes)
|
|
|
|
norm_par, _ = np.histogram(proj_cubes["k" + var + "par"], bins=kbins)
|
|
norm_perp, _ = np.histogram(proj_cubes["k" + var + "perp"], bins=kbins)
|
|
|
|
return proj_cubes, norm_par, norm_perp
|
|
|
|
|
|
def pcube(cube, *others):
|
|
"""Compute the power associated to a Fourier space data cube
|
|
|
|
The power is the sum of square magnitude of each component.
|
|
|
|
Parameters:
|
|
-----------
|
|
cube1, cube2, ...
|
|
(numpy.ndarray, ndim=3) data cubes for each component
|
|
|
|
Return value:
|
|
-------------
|
|
pcube
|
|
(numpy.ndarray, ndim=3) power at each point of the Fourier space
|
|
"""
|
|
|
|
pcube = np.real(cube * np.conj(cube))
|
|
for c in others:
|
|
pcube += np.real(c * np.conj(c))
|
|
return pcube
|
|
|
|
|
|
def pspectrum(pcube, kcube, kbins, norm, nbinsf):
|
|
"""Compute the power spectrum associated to a power cube
|
|
|
|
Parameters:
|
|
-----------
|
|
pcube
|
|
(numpy.ndarray, ndim=3) power cube, see `pcube`
|
|
kcube
|
|
(numpy.ndarray, ndim=3) wave vector magnitude, see `calc_k`
|
|
kbins
|
|
(numpy.array) wave vector bin edges (length nbins + 1), see `calc_k`
|
|
norm
|
|
(numpy.array) number of wave vectors in each bin (length nbins), see
|
|
`calc_k`
|
|
nbinsf
|
|
(int) number of power bins for a 2d-bin power spectrum, computed only
|
|
if nbinsf > 1
|
|
|
|
Return value:
|
|
-------------
|
|
pspec
|
|
(numpy.array) power spectrum (length nbins)
|
|
kbins
|
|
(numpy.array) vector bin edges (the input parameter)
|
|
pspec2
|
|
(numpy.ndarray, ndim=2 | None) 2d-bin power spectrum (shape nbins,
|
|
nbinsf), or None if `nbinsf <= 1`
|
|
fbins
|
|
(numpy.array | None) power bin edges (length nbinsf + 1), or None if
|
|
`nbinsf <= 1`
|
|
"""
|
|
|
|
# Flatten
|
|
k_pts = kcube.flatten()
|
|
f_pts = pcube.flatten()
|
|
|
|
# Drop zeros, NaNs and infinities
|
|
mask = np.logical_and(f_pts > 0.0, np.isfinite(f_pts))
|
|
k_pts = k_pts[mask]
|
|
f_pts = f_pts[mask]
|
|
|
|
fbins = None
|
|
pspec2 = None
|
|
if nbinsf > 1:
|
|
# Fourier coefficients binning
|
|
fmin = f_pts.min()
|
|
fmax = f_pts.max()
|
|
if fmin == fmax:
|
|
fmin *= 0.99
|
|
fmax *= 1.01
|
|
fbins = fmin * (fmax / fmin) ** (np.arange(nbinsf + 1, dtype=np.float) / nbinsf)
|
|
|
|
# Binned power spectrum
|
|
pspec2, _, _ = np.histogram2d(k_pts, f_pts, bins=(kbins, fbins))
|
|
pspec2 /= norm[:, np.newaxis]
|
|
|
|
# Averaged power spectrum
|
|
pow_unnorm, _ = np.histogram(k_pts, bins=kbins, weights=f_pts)
|
|
pspec = pow_unnorm / norm
|
|
|
|
return pspec, kbins, pspec2, fbins
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Command-line parser ----------------------------------------------------------
|
|
parser = argparse.ArgumentParser(
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
description="Compute 2D and 3D power spectra",
|
|
epilog=textwrap.dedent(
|
|
"""
|
|
In the output file and node name formats, you can use the formatting
|
|
fields:
|
|
* %(iout)d - output number
|
|
* %(varname)s - variable name
|
|
* %(dim)d - dimension (3 for cube, 2 for slices)
|
|
"""
|
|
),
|
|
)
|
|
parser.add_argument("repo", help="RAMSES output repository", type=str, default=".")
|
|
parser.add_argument(
|
|
"iouts", help="output numbers", type=args.selection, default=":"
|
|
)
|
|
parser.add_argument(
|
|
"outfile", help="output file format (see below for fields)", type=str
|
|
)
|
|
parser.add_argument(
|
|
"-n",
|
|
"--nodename",
|
|
help="node name format (see below for fields)",
|
|
type=str,
|
|
default="/out_%(iout)05d/d%(dim)d/%(varname)s",
|
|
)
|
|
parser.add_argument(
|
|
"-O",
|
|
"--order",
|
|
help="byte order (= for native)",
|
|
type=str,
|
|
default="=",
|
|
choices=["<", ">", "="],
|
|
)
|
|
parser.add_argument(
|
|
"-c",
|
|
"--center",
|
|
help="coordinates of the center",
|
|
type=args.center,
|
|
default=[0.5, 0.5, 0.5],
|
|
)
|
|
parser.add_argument("-s", "--size", help="cube size", type=float, default=1.0)
|
|
parser.add_argument(
|
|
"-l", "--level", help="cube level (default: levelMIN)", type=int, default=0
|
|
)
|
|
parser.add_argument(
|
|
"-k", "--kbins", help="number of wave number bins", type=int, default=100
|
|
)
|
|
parser.add_argument(
|
|
"-f",
|
|
"--fbins",
|
|
help="number of Fourier bins for k-power 2D histogram (0 to disable)",
|
|
type=int,
|
|
default=0,
|
|
)
|
|
parser.add_argument(
|
|
"-K", "--kbinsbig", help="number of big wave number bins", type=int, default=9
|
|
)
|
|
parser.add_argument(
|
|
"-d",
|
|
"--dkbig",
|
|
help="width of the big wave number bins",
|
|
type=float,
|
|
default=1.0,
|
|
)
|
|
parser.add_argument(
|
|
"-S",
|
|
"--sliceaxis",
|
|
help="slicing axis",
|
|
type=str,
|
|
choices=["x", "y", "z"],
|
|
default="z",
|
|
)
|
|
|
|
arg = parser.parse_args()
|
|
|
|
add_pspec2 = False
|
|
if arg.fbins > 0:
|
|
add_pspec2 = True
|
|
|
|
# Cube level (0 means levelmin)
|
|
clvl = arg.level
|
|
# Slicing axis for 2D data (x = 0, y = 1, z = 2)
|
|
try:
|
|
saxis = {"x": 0, "y": 1, "z": 2}[arg.sliceaxis.strip()]
|
|
except KeyError:
|
|
parser.error("Invalid slicing axis: %r" % arg.sliceaxis)
|
|
|
|
# Output numbers
|
|
iouts = tools.select_outputs(arg.repo, arg.iouts)
|
|
|
|
for iout in iouts:
|
|
print("Output %d" % iout)
|
|
print("Load data")
|
|
read_lvl = None
|
|
if True:
|
|
# Load output ------------------------------------------------------------------
|
|
ro = pymses.RamsesOutput(arg.repo, iout, order=arg.order)
|
|
amr = ro.amr_source(["rho", "vel", "Bl", "Br"])
|
|
|
|
if clvl == 0:
|
|
clvl = ro.info["levelmin"]
|
|
read_lvl = max(clvl, ro.info["levelmin"])
|
|
|
|
# Extract cubes ---------------------------------------------------------------
|
|
xmin = [x - arg.size / 2.0 for x in arg.center]
|
|
xmax = [x + arg.size / 2.0 for x in arg.center]
|
|
cube_vars = [
|
|
"rho",
|
|
lambda dset: dset["vel"][..., 0],
|
|
lambda dset: dset["vel"][..., 1],
|
|
lambda dset: dset["vel"][..., 2],
|
|
lambda dset: 0.5 * (dset["Bl"][..., 0] + dset["Br"][..., 0]),
|
|
lambda dset: 0.5 * (dset["Bl"][..., 1] + dset["Br"][..., 1]),
|
|
lambda dset: 0.5 * (dset["Bl"][..., 2] + dset["Br"][..., 2]),
|
|
]
|
|
cubes_arr = amr2cube(amr, cube_vars, xmin, xmax, read_lvl)
|
|
cubes = {}
|
|
for i, v in enumerate(["rho", "vx", "vy", "vz", "Bx", "By", "Bz"]):
|
|
cubes[v] = cubes_arr[i, ...].copy()
|
|
del cubes_arr
|
|
else:
|
|
h5f = T.open_file("cube.hdf5", "r")
|
|
read_lvl = np.asscalar(h5f.root.level.read())
|
|
cubes = {
|
|
"rho": h5f.root.rho.read(),
|
|
"vx": h5f.root.vx.read(),
|
|
"vy": h5f.root.vy.read(),
|
|
"vz": h5f.root.vz.read(),
|
|
"Bx": h5f.root.Bx.read(),
|
|
"By": h5f.root.By.read(),
|
|
"Bz": h5f.root.Bz.read(),
|
|
}
|
|
h5f.close()
|
|
|
|
if clvl > read_lvl:
|
|
print(
|
|
"WARNING: adjusting cube level (%d) to match data level (%d)"
|
|
% (clvl, read_lvl)
|
|
)
|
|
clvl = read_lvl
|
|
|
|
# Degrade cubes ----------------------------------------------------------------
|
|
if clvl < read_lvl:
|
|
print("Degrade cubes")
|
|
|
|
for v in ["rho", "vx", "vy", "vz", "Bx", "By", "Bz"]:
|
|
# Don't average, simply integrate the magnetic field
|
|
cube_tmp = degrade_cube(cubes[v], clvl, v.startswith("B"))
|
|
del cubes[v]
|
|
cubes[v] = cube_tmp
|
|
|
|
print("Calculate wave numbers")
|
|
# Wave numbers -----------------------------------------------------------------
|
|
cubes_k, kbins, knorm = calc_k(
|
|
1 << clvl, arg.kbins, arg.kbinsbig, arg.dkbig, 3, saxis
|
|
)
|
|
Bavg = avg_vect(cubes, "B", dim=3)
|
|
Bavg2 = avg_vect(cubes, "B", dim=2, saxis=saxis)
|
|
_, knorm_Bpar, knorm_Bperp = proj_B(
|
|
cubes_k, kbins, Bavg, "B", dim=3, update=True
|
|
)
|
|
|
|
print("Calculate derived quantities")
|
|
# Additional quantities --------------------------------------------------------
|
|
cubes["logrho"] = np.log(cubes["rho"])
|
|
cubes["cos_vB"] = np.zeros_like(cubes["rho"])
|
|
vv = np.zeros_like(cubes["rho"])
|
|
BB = np.zeros_like(cubes["rho"])
|
|
for a in ["x", "y", "z"]:
|
|
cubes["kr" + a] = cubes["rho"] ** (1.0 / 3.0) * cubes["v" + a]
|
|
cubes["cos_vB"] += cubes["v" + a] * cubes["B" + a]
|
|
vv += cubes["v" + a] ** 2
|
|
BB += cubes["B" + a] ** 2
|
|
cubes["cos_vB"] /= vv * BB
|
|
del vv
|
|
del BB
|
|
|
|
print("3D FFT")
|
|
# 3D Fourier transform ---------------------------------------------------------
|
|
fcubes = {}
|
|
for v in [
|
|
"rho",
|
|
"logrho",
|
|
"vx",
|
|
"vy",
|
|
"vz",
|
|
"krx",
|
|
"kry",
|
|
"krz",
|
|
"Bx",
|
|
"By",
|
|
"Bz",
|
|
"cos_vB",
|
|
]:
|
|
fcubes[v] = fftn(cubes[v])
|
|
fcubes.update(cubes_k)
|
|
|
|
# Memory cleanup ---------------------------------------------------------------
|
|
del cubes
|
|
|
|
print("Calculate Fourier-space derived quantities")
|
|
# Additional quantities (Fourier domain) ---------------------------------------
|
|
helmholtz(fcubes, "v", update=True)
|
|
helmholtz(fcubes, "kr", update=True)
|
|
|
|
print("Compute power cubes")
|
|
# Power cubes ------------------------------------------------------------------
|
|
pcubes = {}
|
|
pcubes["rho"] = pcube(fcubes["rho"])
|
|
pcubes["logrho"] = pcube(fcubes["logrho"])
|
|
pcubes["v"] = pcube(fcubes["vx"], fcubes["vy"], fcubes["vz"])
|
|
pcubes["kr"] = pcube(fcubes["krx"], fcubes["kry"], fcubes["krz"])
|
|
pcubes["vc"] = pcube(fcubes["vc"])
|
|
pcubes["vs"] = pcube(fcubes["vsx"], fcubes["vsy"], fcubes["vsz"])
|
|
pcubes["krc"] = pcube(fcubes["krc"])
|
|
pcubes["krs"] = pcube(fcubes["krsx"], fcubes["krsy"], fcubes["krsz"])
|
|
pcubes["B"] = pcube(fcubes["Bx"], fcubes["By"], fcubes["Bz"])
|
|
pcubes["cos_vB"] = pcube(fcubes["cos_vB"])
|
|
|
|
print("Compute 3D power spectra")
|
|
# 3D power spectra -------------------------------------------------------------
|
|
for v in ["rho", "logrho", "v", "kr", "vc", "vs", "krc", "krs", "B", "cos_vB"]:
|
|
pspec, kbins, pspec2, fbins = pspectrum(
|
|
pcubes[v], cubes_k["k"], kbins, knorm, arg.fbins
|
|
)
|
|
pspec_Bpar, _, _, _ = pspectrum(
|
|
pcubes[v], cubes_k["kBpar"], kbins, knorm_Bpar, 0
|
|
)
|
|
pspec_Bperp, kbins, _, _ = pspectrum(
|
|
pcubes[v], cubes_k["kBperp"], kbins, knorm_Bperp, 0
|
|
)
|
|
|
|
# Save 3D power spectra
|
|
params = {"iout": iout, "varname": v, "dim": 3}
|
|
outfile = arg.outfile % params
|
|
outpath = arg.nodename % params
|
|
|
|
h5fd = T.open_file(outfile, mode="a")
|
|
|
|
for n in [
|
|
"pspec",
|
|
"kbins",
|
|
"pspec2",
|
|
"fbins",
|
|
"norm",
|
|
"pspec_Bpar",
|
|
"pspec_Bperp",
|
|
"norm_Bpar",
|
|
"norm_Bperp",
|
|
]:
|
|
try:
|
|
h5fd.remove_node(outpath, n, recursive=True)
|
|
except T.NoSuchNodeError:
|
|
pass
|
|
|
|
h5fd.create_array(outpath, "pspec", pspec, createparents=True)
|
|
h5fd.create_array(outpath, "pspec_Bpar", pspec_Bpar, createparents=True)
|
|
h5fd.create_array(outpath, "pspec_Bperp", pspec_Bperp, createparents=True)
|
|
h5fd.create_array(outpath, "kbins", kbins, createparents=True)
|
|
if add_pspec2:
|
|
h5fd.create_array(outpath, "pspec2", pspec2, createparents=True)
|
|
h5fd.create_array(outpath, "fbins", fbins, createparents=True)
|
|
h5fd.create_array(outpath, "norm", knorm, createparents=True)
|
|
h5fd.create_array(outpath, "norm_Bpar", knorm_Bpar, createparents=True)
|
|
h5fd.create_array(outpath, "norm_Bperp", knorm_Bperp, createparents=True)
|
|
|
|
try:
|
|
h5fd.remove_node(outpath, "meta", recursive=True)
|
|
except T.NoSuchNodeError:
|
|
pass
|
|
|
|
h5fd.create_group(outpath, "meta", createparents=True)
|
|
metagrp = h5fd.get_node(outpath, "meta")
|
|
h5fd.create_array(metagrp, "generator", __generator__)
|
|
h5fd.create_array(metagrp, "version", __version__)
|
|
|
|
h5fd.close()
|
|
|
|
# Memory cleanup ---------------------------------------------------------------
|
|
del pcubes
|
|
|
|
print("Calculate 2D wave numbers")
|
|
# 2D wave numbers --------------------------------------------------------------
|
|
cubes_k, kbins, knorm = calc_k(
|
|
1 << clvl, arg.kbins, arg.kbinsbig, arg.dkbig, 2, saxis
|
|
)
|
|
_, knorm_Bpar, knorm_Bperp = proj_B(
|
|
cubes_k, kbins, Bavg2, "B", dim=2, saxis=saxis, update=True
|
|
)
|
|
|
|
print("Project 3D -> 2D")
|
|
# 3D -> 2D ---------------------------------------------------------------------
|
|
fcubes2 = {}
|
|
for v in [
|
|
"rho",
|
|
"logrho",
|
|
"vx",
|
|
"vy",
|
|
"vz",
|
|
"krx",
|
|
"kry",
|
|
"krz",
|
|
"vc",
|
|
"vsx",
|
|
"vsy",
|
|
"vsz",
|
|
"krc",
|
|
"krsx",
|
|
"krsy",
|
|
"krsz",
|
|
"Bx",
|
|
"By",
|
|
"Bz",
|
|
"cos_vB",
|
|
]:
|
|
fcubes2[v] = ifft(fcubes[v], axis=saxis)
|
|
|
|
# Memory cleanup ---------------------------------------------------------------
|
|
del fcubes
|
|
|
|
print("Compute 2D power cubes")
|
|
# 2D power cubes ---------------------------------------------------------------
|
|
pcubes2 = {}
|
|
pcubes2["rho"] = pcube(fcubes2["rho"])
|
|
pcubes2["logrho"] = pcube(fcubes2["logrho"])
|
|
pcubes2["v"] = pcube(fcubes2["vx"], fcubes2["vy"], fcubes2["vz"])
|
|
pcubes2["kr"] = pcube(fcubes2["krx"], fcubes2["kry"], fcubes2["krz"])
|
|
pcubes2["vc"] = pcube(fcubes2["vc"])
|
|
pcubes2["vs"] = pcube(fcubes2["vsx"], fcubes2["vsy"], fcubes2["vsz"])
|
|
pcubes2["krc"] = pcube(fcubes2["krc"])
|
|
pcubes2["krs"] = pcube(fcubes2["krsx"], fcubes2["krsy"], fcubes2["krsz"])
|
|
pcubes2["B"] = pcube(fcubes2["Bx"], fcubes2["By"], fcubes2["Bz"])
|
|
pcubes2["cos_vB"] = pcube(fcubes2["cos_vB"])
|
|
|
|
print("Compute 2D power spectra")
|
|
# 2D power spectra -------------------------------------------------------------
|
|
ns = 2 ** clvl
|
|
f = "_%%(i)0%dd" % (np.floor(np.log10(ns)) + 1)
|
|
for v in ["rho", "logrho", "v", "kr", "vc", "vs", "krc", "krs", "B", "cos_vB"]:
|
|
for i in xrange(ns):
|
|
pspec, kbins, pspec2, fbins = pspectrum(
|
|
pcubes2[v][:, :, i], cubes_k["k"][:, :, i], kbins, knorm, arg.fbins
|
|
)
|
|
pspec_Bpar, _, _, _ = pspectrum(
|
|
pcubes2[v][:, :, i], cubes_k["kBpar"][:, :, i], kbins, knorm_Bpar, 0
|
|
)
|
|
pspec_Bperp, kbins, _, _ = pspectrum(
|
|
pcubes2[v][:, :, i],
|
|
cubes_k["kBperp"][:, :, i],
|
|
kbins,
|
|
knorm_Bperp,
|
|
0,
|
|
)
|
|
|
|
# Save 2D power spectra
|
|
suff = f % {"i": i}
|
|
params = {"iout": iout, "varname": v, "dim": 2}
|
|
outfile = arg.outfile % params
|
|
outpath = arg.nodename % params
|
|
|
|
h5fd = T.open_file(outfile, mode="a")
|
|
|
|
for n in [
|
|
"pspec",
|
|
"kbins",
|
|
"pspec2",
|
|
"fbins",
|
|
"norm",
|
|
"pspec_Bpar",
|
|
"pspec_Bperp",
|
|
"norm_Bpar",
|
|
"norm_Bperp",
|
|
]:
|
|
try:
|
|
h5fd.remove_node(outpath, n + suff, recursive=True)
|
|
except T.NoSuchNodeError:
|
|
pass
|
|
|
|
h5fd.create_array(outpath, "pspec" + suff, pspec, createparents=True)
|
|
h5fd.create_array(
|
|
outpath, "pspec_Bpar" + suff, pspec_Bpar, createparents=True
|
|
)
|
|
h5fd.create_array(
|
|
outpath, "pspec_Bperp" + suff, pspec_Bperp, createparents=True
|
|
)
|
|
h5fd.create_array(outpath, "kbins" + suff, kbins, createparents=True)
|
|
if add_pspec2:
|
|
h5fd.create_array(
|
|
outpath, "pspec2" + suff, pspec2, createparents=True
|
|
)
|
|
h5fd.create_array(
|
|
outpath, "fbins" + suff, fbins, createparents=True
|
|
)
|
|
h5fd.create_array(outpath, "norm" + suff, knorm, createparents=True)
|
|
h5fd.create_array(
|
|
outpath, "norm_Bpar" + suff, knorm_Bpar, createparents=True
|
|
)
|
|
h5fd.create_array(
|
|
outpath, "norm_Bperp" + suff, knorm_Bperp, createparents=True
|
|
)
|
|
|
|
try:
|
|
h5fd.remove_node(outpath, "meta", recursive=True)
|
|
except T.NoSuchNodeError:
|
|
pass
|
|
|
|
h5fd.create_group(outpath, "meta", createparents=True)
|
|
metagrp = h5fd.get_node(outpath, "meta")
|
|
h5fd.create_array(metagrp, "generator", __generator__)
|
|
h5fd.create_array(metagrp, "version", __version__)
|
|
|
|
h5fd.close()
|