Spectral cube modifications (apodization, interpolation etc.)

You can use the following functions to modify your cube data and export it to a new hdf5 cube (ORCS compatible) or a fits cube.

Even if possible, you may want to use a level 3 cube since level 3 cubes contains complex data which allows for perfect interpolation, apodization etc. Older cubes contains only the real spectrum.

Note that these functions can take a very long time to run :)

[ ]:
from orcs.process import SpectralCube
import orb.utils.io
import logging
import numpy as np

Apodization (exported to a new hdf5 cube that can be used with ORCS)

With a level 2, 2.5 or 3 cube

[ ]:
cube = SpectralCube('M1_2022_SN3.merged.cm1.hdf5')
[ ]:
cube_path = 'NGC6888_North_SN3.merged.cm1.1.0.hdf5'
# You must first make a **hardcopy** of the original cube and rename it (here as outcube.hdf5)
export_path = 'outcube.hdf5'

def apodize_cube(cube_path, export_path, coeff):
    cube = SpectralCube(cube_path)
    if not np.iscomplex(cube[1000,1000,0]):
        print('WARNING: cube is not complex, opt for a level 3 cube')

    logging.getLogger().setLevel(logging.CRITICAL)
    fakespec = cube.get_spectrum(0,0, r=0)
    with cube.open_hdf5() as f:
        with orb.utils.io.open_hdf5(export_path, 'a') as fout:
            for ii in range(cube.dimx):
                print('\r', ii, '/', cube.dimx, end='')
                for ij in range(cube.dimy):
                    ispec = cube[ii,ij,:]
                    fakespec.data = ispec
                    # here is the apodization function. You can change this function and export it
                    # to an ORCS-compatible HDF5 cube as long as you don't change the axis of the data
                    # (no interpolation)
                    fakespecA = fakespec.apodize(coeff)
                    # e.g. if you want to take the absolute value instead of the real part :
                    # fakespecA.data = np.abs(fakespecA.data).astype(complex)

                    # you may now write the new spectrum in place of the old data
                    fout['data'][ii,ij,:] = fakespecA.data
    logging.getLogger().setLevel(logging.INFO)

apodize_cube(cube_path, export_path, 2)
[ ]:
logging.getLogger().setLevel(logging.INFO)

With a level 1 cube

[ ]:
cube_path = 'M57_SN3.merged.cm1.1.0.hdf5'
# You must first make a **hardcopy** of the original cube and rename it (here as outcube.hdf5)
export_path = 'outcube_old.hdf5'

cube = SpectralCube(cube_path)

def apodize_old_cube(cube_path, export_path, coeff):
    cube = SpectralCube(cube_path)
    if not np.iscomplex(cube[1000,1000,0]):
        print('WARNING: cube is not complex, opt for a level 3 cube')
    fakespec = cube.get_spectrum(0,0, r=0)
    logging.getLogger().setLevel(logging.CRITICAL)
    with cube.open_hdf5() as f:
        with orb.utils.io.open_hdf5(export_path, 'a') as fout:
            for iquad in range(9):
                quadpath = cube.oldcube._get_hdf5_quad_data_path(iquad)
                quad = f[quadpath]
                quadout = fout[quadpath]
                print('quadrant', iquad, '/ 9 =======')
                for ii in range(quad.shape[0]):
                    print('\r', ii, '/', cube.shape[0], end='')
                    for ij in range(quad.shape[1]):
                        ispec = quad[ii,ij,:]
                        fakespec.data = ispec
                        fakespec = fakespec.apodize(coeff)
                        quadout[ii,ij,:] = fakespec.data.real
    logging.getLogger().setLevel(logging.INFO)

apodize_old_cube(cube_path, export_path, 2)

Interpolation on a new axis (here a loglinear axis)

[ ]:
import orb.utils.io
import orb.utils.spectrum

cube = SpectralCube('NGC6888_North_SN3.merged.cm1.1.0.hdf5')

cube_axis = cube.get_axis(1000, 1000)

# create the projection axis
loglin_axis = 10**np.linspace(np.log10(cube_axis.data[0]), np.log10(cube_axis.data[-1]), 1000)

export_path = 'outcube.fits'

# get flambda depending on the level of the cube, to export the flux calibration with the data
if not np.iscomplex(cube[1000,1000,0]):
    print('WARNING: cube is not complex, opt for a level 3 cube')

flambda = np.ones(cube.dimz, dtype=float)
if cube.get_level() >= 3:
    if 'flambda' in cube.params:
        flambda = cube.params.flambda

    if np.size(flambda) == 1:
        flambda *= np.ones(cube.dimz, dtype=float)
    elif np.size(flambda) != cube.dimz:
        logging.warning('bad flux calibration, output will not be flux calibrated')
        flambda = np.ones(cube.dimz, dtype=float)


# write data to a HDF5 file (impossible to do with a FITS file without loading the whole data in memory)
with orb.utils.io.open_hdf5('test.hdf5', 'w') as fout:
    fout.create_dataset('/data', shape=(cube.dimx, cube.dimy, len(loglin_axis)), dtype=float)
    for ii in range(cube.dimx):
        print('\r', ii, '/', cube.dimx, end='')
        for ij in range(cube.dimy):
            ispec = cube[ii,ij,:]
            newspec = orb.utils.spectrum.project(ispec, cube_axis.data, loglin_axis, 10).astype(float)
            fout['data'][ii,ij,:] = newspec


# export the HDF5 to a FITS cube
with orb.utils.io.open_hdf5('test.hdf5', 'r') as f:
    shdu = orb.utils.io.open_large_fits_file(cube, export_path)

    for iz in range(f['data'].shape[2]):
        print('\r', iz, 'Exporting frame {}'.format(iz), end='')
        shdu.write(f['data'][:,:,iz].real.astype(np.float32).T * flambda[iz])

    shdu.close()



[ ]: