Spectral Data Cubes from Galaxy Particle Distributions¶
In this example we show the main high-level workflow for creating a spectral data cube from particle data.
We first load some demo CAMELS data and a grid, generate per-particle spectra, construct an IntegratedFieldUnit, and then call the galaxy cube getter. A lower-level SpectralCube example is shown afterwards for custom workflows.
[1]:
import time
import numpy as np
from astropy.cosmology import Planck18 as cosmo
from unyt import angstrom, kpc
from synthesizer import TEST_DATA_DIR
from synthesizer.emission_models import IntrinsicEmission
from synthesizer.grid import Grid
from synthesizer.imaging import SpectralCube
from synthesizer.instruments import IntegratedFieldUnit
from synthesizer.kernel_functions import Kernel
from synthesizer.load_data.load_camels import load_CAMELS_IllustrisTNG
# Define the grid
grid_name = "test_grid"
grid = Grid(grid_name)
# Create galaxy object
gal = load_CAMELS_IllustrisTNG(
TEST_DATA_DIR,
snap_name="camels_snap.hdf5",
group_name="camels_subhalo.hdf5",
physical=True,
)[0]
# Calculate the stellar rest frame SEDs for all particles in erg / s / Hz
model = IntrinsicEmission(grid, fesc=0.1, per_particle=True)
sed = gal.stars.get_spectra(model)
# Calculate the observed SED in nJy
sed.get_fnu(cosmo, gal.redshift)
print(gal.stars)
+--------------------------------------------------------------------------------------+
| STARS |
+-------------------------------+------------------------------------------------------+
| Attribute | Value |
+-------------------------------+------------------------------------------------------+
| redshift | 0.10 |
+-------------------------------+------------------------------------------------------+
| nparticles | 278 |
+-------------------------------+------------------------------------------------------+
| metallicity_floor | 1.00e-05 |
+-------------------------------+------------------------------------------------------+
| name | 'Stars' |
+-------------------------------+------------------------------------------------------+
| component_type | 'Stars' |
+-------------------------------+------------------------------------------------------+
| fesc | 0.00e+00 |
+-------------------------------+------------------------------------------------------+
| fesc_ly_alpha | 1.00 |
+-------------------------------+------------------------------------------------------+
| resampled | False |
+-------------------------------+------------------------------------------------------+
| young_stars_parametrisation | False |
+-------------------------------+------------------------------------------------------+
| nstars | 278 |
+-------------------------------+------------------------------------------------------+
| is_parametric | False |
+-------------------------------+------------------------------------------------------+
| is_particle | True |
+-------------------------------+------------------------------------------------------+
| coordinates (278, 3) | 4.28e-02 Mpc -> 1.58e+01 Mpc (Mean: 7.85e+00 Mpc) |
+-------------------------------+------------------------------------------------------+
| masses (278,) | 6.21e+06 Msun -> 2.26e+07 Msun (Mean: 1.40e+07 Msun) |
+-------------------------------+------------------------------------------------------+
| centre | [ 0.07979594 15.80022 7.6674128 ] Mpc |
+-------------------------------+------------------------------------------------------+
| ages (278,) | 5.70e+08 yr -> 1.16e+10 yr (Mean: 6.14e+09 yr) |
+-------------------------------+------------------------------------------------------+
| metallicities (278,) | 1.00e-05 -> 2.13e-02 (Mean: 9.67e-03) |
+-------------------------------+------------------------------------------------------+
| log10metallicities (278,) | -5.00e+00 -> -1.67e+00 (Mean: -2.15e+00) |
+-------------------------------+------------------------------------------------------+
| log10ages (278,) | 8.76e+00 -> 1.01e+01 (Mean: 9.75e+00) |
+-------------------------------+------------------------------------------------------+
| initial_masses (278,) | 1.02e+07 Msun -> 3.76e+07 Msun (Mean: 2.33e+07 Msun) |
+-------------------------------+------------------------------------------------------+
| smoothing_lengths (278,) | 1.97e-03 Mpc -> 2.41e-02 Mpc (Mean: 3.97e-03 Mpc) |
+-------------------------------+------------------------------------------------------+
| current_masses (278,) | 6.21e+06 Msun -> 2.26e+07 Msun (Mean: 1.40e+07 Msun) |
+-------------------------------+------------------------------------------------------+
| s_oxygen (278,) | 1.00e-10 -> 1.07e-02 (Mean: 5.23e-03) |
+-------------------------------+------------------------------------------------------+
| s_hydrogen (278,) | 7.28e-01 -> 7.60e-01 (Mean: 7.46e-01) |
+-------------------------------+------------------------------------------------------+
| centered_coordinates (278, 3) | -3.70e-02 Mpc -> 2.24e-02 Mpc (Mean: -1.04e-04 Mpc) |
+-------------------------------+------------------------------------------------------+
| total_mass | 3902548855.2376695 Msun |
+-------------------------------+------------------------------------------------------+
| particle_spectra | incident: Sed |
| | nebular_continuum: Sed |
| | full_transmitted: Sed |
| | escaped: Sed |
| | nebular_line: Sed |
| | transmitted: Sed |
| | nebular: Sed |
| | _intrinsic_reprocessed: Sed |
| | intrinsic: Sed |
+-------------------------------+------------------------------------------------------+
| spectra | incident: Sed |
| | nebular_continuum: Sed |
| | full_transmitted: Sed |
| | escaped: Sed |
| | nebular_line: Sed |
| | transmitted: Sed |
| | nebular: Sed |
| | _intrinsic_reprocessed: Sed |
| | intrinsic: Sed |
+-------------------------------+------------------------------------------------------+
| model_param_cache | incident: dict |
| | _nebular_line_no_fesc: dict |
| | nebular_continuum: dict |
| | full_transmitted: dict |
| | escaped: dict |
| | nebular_line: dict |
| | transmitted: dict |
| | nebular: dict |
| | _intrinsic_reprocessed: dict |
| | intrinsic: dict |
+-------------------------------+------------------------------------------------------+
Spectral Data Cube Creation¶
We now have most of the ingredients we need to generate a spectral data cube from our galaxy. The remaining ingredients are the wavelength array, the spatial resolution, the field of view, and a kernel for smoothing particles over. We will define those below and then construct an IntegratedFieldUnit to drive the high-level cube generation path.
[2]:
# Define the width of the image
width = 30 * kpc
# Define image resolution (here we arbitrarily set it to 100
# pixels along an axis)
resolution = width / 200
# Define the wavelength array
lam = np.linspace(10**3.5, 10**4.5, 1000)
print(
"Data cube spatial width is %.2f kpc with a %.2f kpc spaxel resolution"
% (width.value, resolution.value)
)
# Get the SPH kernel
kernel = Kernel()
kernel_data = kernel.get_kernel()
Data cube spatial width is 30.00 kpc with a 0.15 kpc spaxel resolution
Synthesizer allows you to make either a histogram data cube, where particles are sorted into individual spaxels, or a smoothed data cube, where particles are distributed over their SPH kernels. We will begin with the recommended high-level path on the galaxy, where an IntegratedFieldUnit defines the wavelength sampling and spatial resolution.
The possible cube quantities are "lnu", "luminosity" or "llam" for rest-frame luminosities, or "fnu", "flam" or "flux" for fluxes. Here we will make a cube populated with "flux".
[3]:
cube_start = time.time()
# Construct the IFU that defines the cube geometry
ifu = IntegratedFieldUnit(
label="DemoIFU",
resolution=resolution,
lam=lam * angstrom,
)
# Generate the cube via the high-level galaxy helper
cube = gal.get_data_cube(
fov=width,
instrument=ifu,
cube_type="smoothed",
stellar_spectra="intrinsic",
kernel=kernel_data,
kernel_threshold=1,
quantity="flux",
)
print("Spectral data cube created, took:", time.time() - cube_start)
Spectral data cube created, took: 1.230470895767212
And that’s it. We now have a spectral data cube to analyse. We can visualise the data cube by making an animation.
[4]:
# Animate the data cube
ani = cube.animate_data_cube(fps=240, show=True)
Direct SpectralCube interface¶
If you need more direct control, you can also work with the lower-level SpectralCube object. In that path you construct the cube yourself and call its generate_data_cube_* methods explicitly.
[5]:
cube = SpectralCube(resolution=resolution, lam=lam * angstrom, fov=width)
cube.generate_data_cube_smoothed(
sed,
coordinates=gal.stars.centered_coordinates,
smoothing_lengths=gal.stars.smoothing_lengths,
kernel=kernel_data,
kernel_threshold=1,
quantity="fnu",
)
[5]:
<synthesizer.imaging.spectral_cube.SpectralCube at 0x7f328d9bc100>