Core Functionality#
Synthetic IFU cube generator (toy model).
This module implements a compact, self-contained pipeline to create toy
n_vel × ny × nx spectral cubes that mimic IFU observations of disk
galaxies. It focuses on clarity and inspectability over performance, and
encapsulates the following responsibilities:
- Build a 3D light distribution from a Sérsic radial profile combined with an
exponential vertical profile (see
GalCubeCraft.sersic_flux_density_3d()).
- Create a simple analytical rotation curve and assign tangential velocities to
the 3D grid (see
GalCubeCraft.milky_way_rot_curve_analytical()).
- Rotate the full 3D flux and velocity fields to simulate arbitrary viewing
angles and project galaxy emission into velocity bins to form a spectral cube (see
GalCubeCraft.rotated_system()andGalCubeCraft.make_spectral_cube()).
- Optionally convolve the final cube with a telescope beam and save cubes to
disk (see
GalCubeCraft.generate_cubes()).
Design notes#
- Coordinates: internal grids are defined in pixels and converted to physical
units (kpc) using a pixel scale stored per cube.
- Velocities: rotation is computed analytically and random Gaussian scatter is
added per voxel to mimic dispersion.
- Output: spectral cubes are produced in units of flux per pixel and are
optionally downsampled/averaged in the spectral axis to simulate channel binning/oversampling.
Usage and API#
- The primary user-facing class is
GalCubeCraft. Call g.generate_cubes()to produce one or more cubes; the method appends the generated outputs tog.resultsand also returns that list. Each entry ing.resultsis typically a tuple(spectral_cube, params)wherespectral_cubehas shape(n_spectral, ny, nx)andparamscontains metadata (beam info, pixel scale, velocity axis, etc.). The GUI and utilities in this repository expect this layout.
- The primary user-facing class is
- Visualisation helpers are provided as top-level functions in
GalCubeCraft.visualise(moment0,moment1,spectrum,slice_view). Plotting is intentionally kept separate from the generation core to avoid UI/plotting dependencies here.
This file provides the GalCubeCraft helper class which encapsulates
parameters, sampling choices, and the generation pipeline.
- class GalCubeCraft.core.GalCubeCraft(n_gals=None, n_cubes=1, resolution='all', offset_gals=5, beam_info=[4, 4, 0], grid_size=125, n_spectral_slices=40, n_sersic=None, save=False, fname=None, verbose=True, seed=None)[source]#
Bases:
objectGenerator for ensembles of synthetic IFU spectral cubes (pixel units).
High-level behaviour#
For each requested cube the class samples (or accepts) one or more galaxy components. Each component has a 3D Sérsic + exponential vertical light distribution and a simple analytical rotation field.
The components are rotated to a viewing geometry and placed into a larger spatial grid. Voxels are binned by line-of-sight velocity to produce a 3D spectral cube (n_spectral x ny x nx).
The pipeline supports a resolution parameter that controls the effective physical size of galaxies relative to the beam; this is used to vary surface brightness and sampling across generated cubes.
Constructor arguments closely mirror the fields used throughout the implementation (see the __init__ signature). Key internal attributes are lists storing per-cube parameters (
all_Re,all_Se,all_pix_spatial_scales, etc.) and the final results are appended toself.resultsas tuples of(spectral_cube, params).Implementation details (what the methods do)#
milky_way_rot_curve_analytical()computes an analytic circular velocity as a function of radius using a simple power-law approximation scaled by a characteristic velocityv_0.sersic_flux_density_3d()returns a 3D flux field for a Sérsic profile in the disk plane multiplied by an exponential vertical profile.rotated_system()constructs the 3D flux and velocity cubes on a small grid, assigns tangential velocities, and applies geometric rotations (both image rotations and vector rotations) to yield a rotated flux cube and a rotated line-of-sight velocity cube.make_spectral_cube()places rotated components into the final grid, computes velocity bin masks for each spectral channel, projects emission along the line of sight, and returns the assembled spectral cube together with metadata (average velocities, beam info, pixel scale, etc.).
The class operates primarily in pixel units and samples parameter values (e.g., sizes, brightness, orientations) from uniform ranges over physically viable values to produce diverse, unique systems. It intentionally focuses on clarity and inspectability rather than performance; nested Python loops are used to build fields which is adequate for moderate grid sizes used in examples.
- generate_cubes()[source]#
Run the full pipeline and generate the requested spectral cubes.
This is the high-level convenience method that iterates over the pre-sampled per-cube parameters (
self.all_Re,self.all_Seetc.), constructs each component viarotated_system(), assembles the spectral cube withmake_spectral_cube(), applies beam convolution and light smoothing, optionally saves the cube(s) to disk whensave=True(thefnameargument controls the destination directory), and returns a list of(cube, params)tuples stored inself.results.- Returns:
results – A list with one entry per generated cube. Each entry is a tuple
(spectral_cube_array, params_dict)wherespectral_cube_arrayhas shape(n_channels, grid_size, grid_size).- Return type:
list
Example
>>> g = GalCubeCraft(n_cubes=1, seed=42, verbose=False) >>> results = g.generate_cubes() >>> cube, meta = results[0] >>> cube.shape (40, 125, 125)
Notes
The method performs several stochastic choices (positions, angles, flux scalings). Use
seedin the constructor to reproduce results.For large numbers of cubes or larger grids, consider refactoring the inner loops to use vectorized operations or offload heavy parts to compiled code for speed.
- make_spectral_cube(rotated_disks, rotated_vel_z_cubes, pix_spatial_scale)[source]#
Assemble rotated component cubes into a final spectral cube.
Projects multiple rotated galaxy components into a larger spatial grid, bins voxels by line-of-sight velocity into spectral channels, and returns a spectral cube together with metadata describing the configuration.
- Parameters:
rotated_disks (list of ndarray) – List of 3D flux cubes (from
rotated_system()) for each component. Each array shape must match(init_grid_size,)*3.rotated_vel_z_cubes (list of ndarray) – Corresponding list of 3D LOS velocity fields (km/s) for each component.
pix_spatial_scale (float) – Physical scale (kpc/pixel) used for this cube; required for computing relative Hubble-flow offsets when placing multiple galaxies along the LOS.
- Returns:
spectral_cube_Jy_px (ndarray) – Spectral cube with shape
(n_channels, grid_size, grid_size)where the spectral axis corresponds to velocity-binned slices.params_gen (dict) – Metadata with keys: ‘galaxy_centers’, ‘average_vels’, ‘beam_info’, ‘n_gals’, and ‘pix_spatial_scale’.
Notes
The method internally defines a symmetric velocity range (default -600 to +600 km/s) and creates
self.n_spectral_slicesfine bins. The code then averages groups of 5 fine bins to mimic spectral binning (i.e., 5x oversampling).Components are placed at randomized centers near the cube centre and optionally offset along the LOS; small offsets are converted to a Hubble-flow velocity and added to that component’s velocity cube.
Example
>>> cube, meta = g.make_spectral_cube([disk1, disk2], [vel1, vel2], 0.1) >>> cube.shape (40, 125, 125)
- static milky_way_rot_curve_analytical(R, v_0, R_e, n)[source]#
Analytical rotation-curve approximation.
Computes an analytic circular velocity approximation used to assign tangential velocities to voxels in the toy galaxy model. The form is a shallow power-law scaled by a characteristic velocity v_0 and a scale radius R_0 derived from the Sérsic effective radius.
- Parameters:
R (float) – Galactocentric radius (kpc). Can be a scalar or NumPy array.
v_0 (float) – Characteristic rotation velocity (km/s). Typical values ~200 km/s.
R_e (float) – Sérsic effective radius (kpc).
n (float) – Sérsic index (dimensionless) used to derive the profile shape.
- Returns:
vel – Circular rotation velocity (km/s) evaluated at R.
- Return type:
float or ndarray
Notes
The function first computes the Sérsic constant b_n using a series expansion and then derives a scale radius R_0 ~ 2 * R_e / b_n^n.
The working formula is: v(R) = v_0 * 1.022 * (R / R_0)**0.0803
The exponent 0.0803 produces a gently rising/flat curve typical of disk galaxies over the radial range used here.
Edge cases#
For R == 0 the returned velocity will be 0 (handled naturally by the power-law when R is 0).
Very small R_e or extreme n values may produce R_0 values that are physically unrealistic; validate inputs when using this function.
Example
>>> GalCubeCraft.milky_way_rot_curve_analytical(np.array([0.1,1,10]), 200, 5.0, 1.0) array([...]) # velocities in km/s
References
See discussion in Lahiry et al. and empirical approximations used for compact rotation-curve modelling.
- rotated_system(params_gal_rot)[source]#
Create a rotated 3D galaxy flux cube and LOS velocity cube.
This routine constructs an isolated galaxy on a small cubic grid of size
self.init_grid_sizeand performs the following steps:Build a 3D flux density using
sersic_flux_density_3d().Compute a tangential velocity magnitude at each (x,y) using
milky_way_rot_curve_analytical()and assign vector components in the local tangent direction.Add a Gaussian random LOS velocity component with standard deviation
gal_vz_sigmato mimic dispersion.Rotate both the scalar flux cube and the velocity vector field to the requested viewing angles (inclination and position angle).
- Parameters:
params_gal_rot (dict) – Dictionary with the following keys (units in parentheses): - ‘pix_spatial_scale’ (kpc/pixel) - ‘Re’ (pixels) - ‘hz’ (pixels) - ‘Se’ (flux units) - ‘n’ (dimensionless, Sérsic index) - ‘gal_x_angle’ (degrees, inclination) - ‘gal_y_angle’ (degrees, position angle) - ‘gal_vz_sigma’ (km/s, LOS dispersion) - ‘v_0’ (km/s, characteristic rotation velocity)
- Returns:
rotated_disk_xy (ndarray) – 3D flux cube after rotations (shape
(init_grid_size,)*3).rotated_vel_z_cube_xy (ndarray) – 3D line-of-sight velocity cube (same shape) giving LOS velocity (km/s) at each voxel after rotation and projection.
Performance and memory
———————-
- The method uses explicit Python loops to assign velocities and to – rotate vectors voxel-by-voxel; this is clear but not optimal for very large grids.
self.init_grid_sizeis chosen to be small to keep runtime reasonable for examples.
Example
>>> params = {'pix_spatial_scale':0.1, 'Re':20, 'hz':2, 'Se':0.1, 'n':1.0, ... 'gal_x_angle':45, 'gal_y_angle':30, 'gal_vz_sigma':40, 'v_0':200} >>> disk, vel = g.rotated_system(params) >>> disk.shape, vel.shape ((31,31,31), (31,31,31))
Notes
The method currently assumes circular disks (axis ratio q=1) and a simple form for the rotation curve. Replace parts of the pipeline if you need more physical realism.
- static sersic_flux_density_3d(x, y, z, Se, Re, n, hz)[source]#
Compute the 3D Sérsic + exponential vertical flux density.
The returned array represents the intrinsic 3D flux distribution of a disk galaxy in physical units (same units as the coordinate grids).
- Parameters:
x (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').y (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').z (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').Se (float) – Flux density at the effective radius (arbitrary flux units).
Re (float) – Effective (half-light) radius in kpc.
n (float) – Sérsic index; lower values produce disk-like profiles.
hz (float) – Vertical exponential scale height in kpc.
- Returns:
S – 3D array with the same shape as the input coordinate grids giving flux density at each voxel.
- Return type:
ndarray
Notes
The radial Sérsic profile is evaluated using the standard series expansion for the constant b_n.
The profile assumes circular symmetry in the disk plane (axis ratio q = 1). To model elliptical disks, scale one of the axes before calling this routine.
The vertical structure is a symmetric exponential: exp(-|z|/hz).
Example
>>> nx = ny = nz = 21 >>> x = np.arange(nx) - (nx-1)/2 >>> X,Y,Z = np.meshgrid(x,x,x,indexing='ij') >>> S = GalCubeCraft.sersic_flux_density_3d(X, Y, Z, Se=0.1, Re=5.0, n=1.0, hz=0.5) >>> S.shape (21, 21, 21)
References
Sérsic (1963) and standard approximations for b_n (see Ciotti & Bertin, 1999 for derivations and series expansions).
- class GalCubeCraft.core.GalCubeCraft_Phy(n_gals=None, n_cubes=1, spatial_resolution=4.5, spectral_resolution=10, offset_gals=20, beam_info=[18, 18, 0], fov=125, n_sersic=None, save=False, fname=None, verbose=True, seed=None)[source]#
Bases:
objectGenerator for single-system synthetic IFU spectral cubes (physical units).
High-level behaviour#
- Uses explicit physical-unit parametrisation (e.g. kpc, km s⁻¹) to
construct highly specific galaxies, typically a single system intended for interactive use (GUI). Each component has a 3D Sérsic + exponential vertical light distribution and a simple analytical rotation field.
- Components are rotated to a viewing geometry and placed into a larger
spatial grid. Voxels are binned by line-of-sight velocity to produce a 3D spectral cube.
Constructor arguments closely mirror the fields used throughout the implementation (see the __init__ signature). Key internal attributes are lists storing per-cube parameters (
all_Re,all_Se,all_pix_spatial_scales, etc.) and the final results are appended toself.resultsas tuples of(spectral_cube, params).Implementation details (what the methods do)#
milky_way_rot_curve_analytical()computes an analytic circular velocity as a function of radius using a simple power-law approximation scaled by a characteristic velocityv_0.sersic_flux_density_3d()returns a 3D flux field for a Sérsic profile in the disk plane multiplied by an exponential vertical profile.rotated_system()constructs the 3D flux and velocity cubes on a small grid, assigns tangential velocities, and applies geometric rotations (both image rotations and vector rotations) to yield a rotated flux cube and a rotated line-of-sight velocity cube.make_spectral_cube()places rotated components into the final grid, computes velocity bin masks for each spectral channel, projects emission along the line of sight, and returns the assembled spectral cube together with metadata (average velocities, beam info, pixel scale, etc.).
Parameters are provided explicitly (not randomly sampled) to enable controlled, reproducible experiments. The implementation prioritises clarity and inspectability over performance.
- all_gal_v_0#
- if isinstance(self.resolution, float) and n_cubes==1:
# Directly use the float as r r = np.full(n_cubes, self.resolution)
- else:
- if self.resolution == ‘all’:
r_min, r_max = 0.25, 4
- elif self.resolution == ‘unresolved’:
r_min, r_max = 0.25, 1
- elif self.resolution == ‘resolved’:
r_min, r_max = 1, 4
log_r = np.random.uniform(np.log10(r_min), np.log10(r_max), size=n_cubes) r = 10 ** log_r
# Convert resolution ratio to effective radius in pixels # Re = r * (beam minor axis / 2) gives effective radius Re_central = r * (self.beam_info[0]/self.spatial_resolution) /2
- generate_cubes()[source]#
Run the full pipeline and generate the requested spectral cubes.
This is the high-level convenience method that iterates over the pre-sampled per-cube parameters (
self.all_Re,self.all_Seetc.), constructs each component viarotated_system(), assembles the spectral cube withmake_spectral_cube(), applies beam convolution and light smoothing, optionally saves the cube(s) to disk whensave=True(thefnameargument controls the destination directory), and returns a list of(cube, params)tuples stored inself.results.- Returns:
results – A list with one entry per generated cube. Each entry is a tuple
(spectral_cube_array, params_dict)wherespectral_cube_arrayhas shape(n_channels, grid_size, grid_size).- Return type:
list
Example
>>> g = GalCubeCraft(n_cubes=1, seed=42, verbose=False) >>> results = g.generate_cubes() >>> cube, meta = results[0] >>> cube.shape (40, 125, 125)
Notes
- The method performs several stochastic choices (positions, angles,
flux scalings). Use
seedin the constructor to reproduce results.
- For large numbers of cubes or larger grids, consider refactoring the
inner loops to use vectorized operations or offload heavy parts to compiled code for speed.
- make_spectral_cube(rotated_disks, rotated_vel_z_cubes, pix_spatial_scale)[source]#
Assemble rotated component cubes into a final spectral cube.
Projects multiple rotated galaxy components into a larger spatial grid, bins voxels by line-of-sight velocity into spectral channels, and returns a spectral cube together with metadata describing the configuration.
- Parameters:
rotated_disks (list of ndarray) – List of 3D flux cubes (from
rotated_system()) for each component. Each array shape must match(init_grid_size,)*3.rotated_vel_z_cubes (list of ndarray) – Corresponding list of 3D LOS velocity fields (km/s) for each component.
pix_spatial_scale (float) – Physical scale (kpc/pixel) used for this cube; required for computing relative Hubble-flow offsets when placing multiple galaxies along the LOS.
- Returns:
spectral_cube_Jy_px (ndarray) – Spectral cube with shape
(n_channels, grid_size, grid_size)where the spectral axis corresponds to velocity-binned slices.params_gen (dict) – Metadata with keys: ‘galaxy_centers’, ‘average_vels’, ‘beam_info’, ‘n_gals’, and ‘pix_spatial_scale’.
Notes
The method internally defines a symmetric velocity range (default -600 to +600 km/s) and creates
self.n_spectral_slicesfine bins. The code then averages groups of 5 fine bins to mimic spectral binning (i.e., 5x oversampling).Components are placed at randomized centers near the cube centre and optionally offset along the LOS; small offsets are converted to a Hubble-flow velocity and added to that component’s velocity cube.
Example
>>> cube, meta = g.make_spectral_cube([disk1, disk2], [vel1, vel2], 0.1) >>> cube.shape (40, 125, 125)
- static milky_way_rot_curve_analytical(R, v_0, R_e, n)[source]#
Analytical rotation-curve approximation.
Computes an analytic circular velocity approximation used to assign tangential velocities to voxels in the toy galaxy model. The form is a shallow power-law scaled by a characteristic velocity v_0 and a scale radius R_0 derived from the Sérsic effective radius.
- Parameters:
R (float) – Galactocentric radius (kpc). Can be a scalar or NumPy array.
v_0 (float) – Characteristic rotation velocity (km/s). Typical values ~200 km/s.
R_e (float) – Sérsic effective radius (kpc).
n (float) – Sérsic index (dimensionless) used to derive the profile shape.
- Returns:
vel – Circular rotation velocity (km/s) evaluated at R.
- Return type:
float or ndarray
Notes
The function first computes the Sérsic constant b_n using a series expansion and then derives a scale radius R_0 ~ 2 * R_e / b_n^n.
The working formula is: v(R) = v_0 * 1.022 * (R / R_0)**0.0803
The exponent 0.0803 produces a gently rising/flat curve typical of disk galaxies over the radial range used here.
Edge cases#
For R == 0 the returned velocity will be 0 (handled naturally by the power-law when R is 0).
Very small R_e or extreme n values may produce R_0 values that are physically unrealistic; validate inputs when using this function.
Example
>>> GalCubeCraft.milky_way_rot_curve_analytical(np.array([0.1,1,10]), 200, 5.0, 1.0) array([...]) # velocities in km/s
References
See discussion in Lahiry et al. and empirical approximations used for compact rotation-curve modelling.
- rotated_system(params_gal_rot)[source]#
Create a rotated 3D galaxy flux cube and LOS velocity cube.
This routine constructs an isolated galaxy on a small cubic grid of size
self.init_grid_sizeand performs the following steps:Build a 3D flux density using
sersic_flux_density_3d().Compute a tangential velocity magnitude at each (x,y) using
milky_way_rot_curve_analytical()and assign vector components in the local tangent direction.Add a Gaussian random LOS velocity component with standard deviation
gal_vz_sigmato mimic dispersion.Rotate both the scalar flux cube and the velocity vector field to the requested viewing angles (inclination and position angle).
- Parameters:
params_gal_rot (dict) – Dictionary with the following keys (units in parentheses): - ‘pix_spatial_scale’ (kpc/pixel) - ‘Re’ (pixels) - ‘hz’ (pixels) - ‘Se’ (flux units) - ‘n’ (dimensionless, Sérsic index) - ‘gal_x_angle’ (degrees, inclination) - ‘gal_y_angle’ (degrees, position angle) - ‘gal_vz_sigma’ (km/s, LOS dispersion) - ‘v_0’ (km/s, characteristic rotation velocity)
- Returns:
rotated_disk_xy (ndarray) – 3D flux cube after rotations (shape
(init_grid_size,)*3).rotated_vel_z_cube_xy (ndarray) – 3D line-of-sight velocity cube (same shape) giving LOS velocity (km/s) at each voxel after rotation and projection.
Performance and memory
———————-
- The method uses explicit Python loops to assign velocities and to – rotate vectors voxel-by-voxel; this is clear but not optimal for very large grids.
self.init_grid_sizeis chosen to be small to keep runtime reasonable for examples.
Example
>>> params = {'pix_spatial_scale':0.1, 'Re':20, 'hz':2, 'Se':0.1, 'n':1.0, ... 'gal_x_angle':45, 'gal_y_angle':30, 'gal_vz_sigma':40, 'v_0':200} >>> disk, vel = g.rotated_system(params) >>> disk.shape, vel.shape ((31,31,31), (31,31,31))
Notes
The method currently assumes circular disks (axis ratio q=1) and a simple form for the rotation curve. Replace parts of the pipeline if you need more physical realism.
- static sersic_flux_density_3d(x, y, z, Se, Re, n, hz)[source]#
Compute the 3D Sérsic + exponential vertical flux density.
The returned array represents the intrinsic 3D flux distribution of a disk galaxy in physical units (same units as the coordinate grids).
- Parameters:
x (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').y (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').z (ndarray) – Coordinate grids (kpc). These should have identical shapes (for example produced by
np.meshgridwithindexing='ij').Se (float) – Flux density at the effective radius (arbitrary flux units).
Re (float) – Effective (half-light) radius in kpc.
n (float) – Sérsic index; lower values produce disk-like profiles.
hz (float) – Vertical exponential scale height in kpc.
- Returns:
S – 3D array with the same shape as the input coordinate grids giving flux density at each voxel.
- Return type:
ndarray
Notes
The radial Sérsic profile is evaluated using the standard series expansion for the constant b_n.
The profile assumes circular symmetry in the disk plane (axis ratio q = 1). To model elliptical disks, scale one of the axes before calling this routine.
The vertical structure is a symmetric exponential: exp(-|z|/hz).
Example
>>> nx = ny = nz = 21 >>> x = np.arange(nx) - (nx-1)/2 >>> X,Y,Z = np.meshgrid(x,x,x,indexing='ij') >>> S = GalCubeCraft.sersic_flux_density_3d(X, Y, Z, Se=0.1, Re=5.0, n=1.0, hz=0.5) >>> S.shape (21, 21, 21)
References
Sérsic (1963) and standard approximations for b_n (see Ciotti & Bertin, 1999 for derivations and series expansions).