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)
../../_images/observables_spectral_data_cubes_particle_data_cube_7_0.png

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>