Example #1
0
def itau(temp,
         dens,
         line,
         n_min=5,
         n_max=1000,
         other='',
         verbose=False,
         value='itau',
         location=LOCALDIR):
    """
    Gives the integrated optical depth for a given temperature and density.
    It assumes that the background radiation field dominates the continuum emission.
    The emission measure is unity. The output units are Hz.
    
    :param temp: Electron temperature. Must be a string of the form '8d1'.
    :type temp: string
    :param dens: Electron density.
    :type dens: float
    :param line: Line to load models for.
    :type line: string
    :param n_min: Minimum n value to include in the output. Default 1
    :type n_min: int
    :param n_max: Maximum n value to include in the output. Default 1500, Maximum allowed value 9900
    :type n_max: int
    :param other: String to search for different radiation fields and others.
    :type other: string
    :param verbose: Verbose output?
    :type verbose: bool
    :param value: ['itau'|'bbnMdn'|'None'] Value to output. itau will output the integrated optical depth. \
    bbnMdn will output the :math:`\\beta_{n,n^{\\prime}}b_{n}` times the oscillator strenght :math:`M(\\Delta n)`. \
    None will output the :math:`\\beta_{n,n^{\\prime}}b_{n}` values.
    :type value: string
    :returns: The principal quantum number and its asociated value.
    """

    t = str2val(temp)
    d = float(dens)

    dn = fc.set_dn(line)
    mdn_ = mdn(dn)

    bbn = load_betabn(temp, dens, other, line, verbose, location=location)
    nimin = best_match_indx(n_min, bbn[:, 0])
    nimax = best_match_indx(n_max, bbn[:, 0])
    n = bbn[nimin:nimax, 0]
    b = bbn[nimin:nimax, 1]

    if value == 'itau':
        i = itau_norad(n, t, b, dn, mdn_)
    elif value == 'bbnMdn':
        i = b * dn * mdn_
    else:
        i = b

    return n, i
Example #2
0
def kappa_line(Te, ne, nion, Z, Tr, trans, n_max=1500):
    """
    Computes the line absorption coefficient for CRRLs between levels :math:`n_{i}` and :math:`n_{f}`, :math:`n_{i}>n_{f}`.
    This can only go up to :math:`n_{\\rm{max}}` 1500 because of the tables used for the Einstein Anm coefficients.
    
    :param Te: Electron temperature of the gas. (K)
    :type Te: float
    :param ne: Electron density. (:math:`\\mbox{cm}^{-3}`)
    :type ne: float
    :param nion: Ion density. (:math:`\\mbox{cm}^{-3}`)
    :type nion: float
    :param Z: Electric charge of the atoms being considered.
    :type Z: int
    :param Tr: Temperature of the radiation field felt by the gas. This specifies the temperature of the field at 100 MHz. (K)
    :type Tr: float
    :param trans: Transition for which to compute the absorption coefficient.
    :type trans: string
    :param n_max: Maximum principal quantum number to include in the output.
    :type n_max: int<1500
    :returns: 
    :rtype: array
    """

    cte = np.power(c, 2.) / (16. * np.pi) * np.power(
        np.power(h, 2) / (2. * np.pi * m_e * k_B), 3. / 2.)

    bn = load_bn(val2str(Te), ne, other='case_diffuse_{0}'.format(val2str(Tr)))
    bn = bn[:np.where(bn[:, 0] == n_max)[0]]
    Anfni = np.loadtxt('{0}/rates/einstein_Anm_{1}.txt'.format(
        LOCALDIR, trans))

    # Cut the Einstein Amn coefficients table to match the bn values
    i_bn_i = best_match_indx(bn[0, 0], Anfni[:, 1])
    i_bn_f = best_match_indx(bn[-1, 0], Anfni[:, 0])
    Anfni = Anfni[i_bn_i:i_bn_f + 1]

    ni = Anfni[:, 0]
    nf = Anfni[:, 1]

    omega_ni = 2 * np.power(ni, 2)
    omega_i = 1.

    xi_ni = xi(ni, Te, Z)
    xi_nf = xi(nf, Te, Z)

    exp_ni = np.exp(xi_ni.value)
    exp_nf = np.exp(xi_nf.value)

    #print len(Anfni), len(bn[1:,-1]), len(bn[:-1,-1]), len(omega_ni[:]), len(ni), len(exp_ni), len(exp_nf)
    kl = cte.value / np.power(
        Te, 3. / 2.) * ne * nion * Anfni[:, 2] * omega_ni[:] / omega_i * (
            bn[1:, -1] * exp_ni - bn[:-1, -1] * exp_nf)

    return kl
Example #3
0
def get_line_mask(freq, reffreq, v0, dv):
    """
    Return a mask with ranges where a line is expected in the given frequency range for \
    a line with a given reference frequency at expected velocity v0 and line width dv0.
    
    :param freq: Frequency axis where the line is located.
    :type freq: numpy array or list
    :param reffreq: Reference frequency for the line.
    :type reffreq: float
    :param v0: Velocity of the line.
    :type v0: float, km/s
    :param dv: Velocity range to mask.
    :type dv: float, km/s
    :returns: Mask centered at the line center and width `dv0` referenced to the input `freq`.
    """

    f0 = vel2freq(reffreq, v0 * 1e3)
    df0 = dv2df(reffreq * 1e6, dv0 * 1e3)

    df = abs(freq[0] - freq[1])

    f0_indx = utils.best_match_indx(f0, freq, df / 2.0)

    mindx0 = f0_indx - df0 / df / 1e6
    mindxf = f0_indx + df0 / df / 1e6

    return [mindx0, mindxf]
Example #4
0
def load_bn(te,
            ne,
            tr='',
            ncrit='1.5d3',
            n_min=5,
            n_max=1000,
            verbose=False,
            location=LOCALDIR):
    """
    Loads the bn values from the CRRL models.
    
    :param te: Electron temperature of the model.
    :type te: string
    :param ne: Electron density of the model.
    :type ne: string
    :param other: Radiation field of the model or any other string with model characteristics.
    :type other: string
    :param verbose: Verbose output?
    :type verbose: bool
    :returns: The :math:`b_{n}` value for the given model conditions.
    :rtype: array
    """

    #LOCALDIR = os.path.dirname(os.path.realpath(__file__))

    if tr == '-' or tr == '' or tr == 0:
        model_file = 'Carbon_opt_T_{0}_ne_{1}_ncrit_{2}_vriens_delta_500_vrinc_nmax_9900_dat'.format(
            te, ne, ncrit)
        if verbose:
            print("Loading {0}".format(model_file))
    else:
        model_file = 'Carbon_opt_T_{0}_ne_{1}_ncrit_{2}_{3}_vriens_delta_500_vrinc_nmax_9900_dat'.format(
            te, ne, ncrit, tr)
        if verbose:
            print("Loading {0}".format(model_file))
    model_path = glob.glob('{0}/{1}'.format(location, model_file))[0]
    if verbose:
        print("Loaded {0}".format(model_path))
    bn = np.loadtxt(model_path)

    nimin = best_match_indx(n_min, bn[:, 0])
    nimax = best_match_indx(n_max, bn[:, 0])
    bn = bn[nimin:nimax + 1]

    return bn
Example #5
0
def load_bn_h(te, ne, other='', n_min=5, n_max=1000, verbose=False):
    """
    Loads the bn values from the HRRL models.
    
    :param te: Electron temperature of the model.
    :type te: string
    :param ne: Electron density of the model.
    :type ne: string
    :param other: Radiation field of the model or any other string with model characteristics.
    :type other: string
    :param verbose: Verbose output?
    :type verbose: bool
    :returns: The :math:`b_{n}` value for the given model conditions.
    :rtype: array
    """

    #LOCALDIR = os.path.dirname(os.path.realpath(__file__))

    if other == '-' or other == '':
        mod_file = 'H_bn2/Hydrogen_opt_T_{1}_ne_{2}_ncrit_8d2_vriens_delta_500_vrinc_nmax_9900_dat'.format(
            LOCALDIR, te, ne)
        if verbose:
            print("Loading {0}".format(mod_file))
        mod_file = glob.glob(
            '{0}/H_bn2/Hydrogen_opt_T_{1}_ne_{2}*_ncrit_8d2_vriens_delta_500_vrinc_nmax_9900_dat'
            .format(LOCALDIR, te, ne))[0]
    else:
        mod_file = 'H_bn2/Hydrogen_opt_T_{1}_ne_{2}_ncrit_8d2_{3}_vriens_delta_500_vrinc_nmax_9900_dat'.format(
            LOCALDIR, te, ne, other)
        if verbose:
            print("Loading {0}".format(mod_file))
        mod_file = glob.glob(
            '{0}/H_bn2/Hydrogen_opt_T_{1}_ne_{2}*_ncrit_8d2_{3}_vriens_delta_500_vrinc_nmax_9900_dat'
            .format(LOCALDIR, te, ne, other))[0]

    if verbose:
        print("Loaded {0}".format(mod_file))
    bn = np.loadtxt(mod_file)

    nimin = best_match_indx(n_min, bn[:, 0])
    nimax = best_match_indx(n_max, bn[:, 0])
    bn = bn[nimin:nimax + 1]

    return bn
Example #6
0
def mask_cube_(vel, data, vel_rngs, offset=10.):
    """
    """

    logger = logging.getLogger(__name__)

    vel_indx = np.empty(vel_rngs.shape, dtype=int)

    mdata = deepcopy(data)
    mdata = np.ma.masked_invalid(mdata)

    for i, velrng in enumerate(vel_rngs):

        vel_indx[i][0] = utils.best_match_indx(velrng[0], vel)
        vel_indx[i][1] = utils.best_match_indx(velrng[1], vel)

        logger.info('Channels selected to be masked: {0} -- {1}'.format(
            vel_indx[i][0], vel_indx[i][1] + 1))

        mdata[vel_indx[i][0]:vel_indx[i][1] + 1].mask = True

    return mdata
Example #7
0
def mask_cube(vel, data, vel_rngs, offset=10.):
    """
    """

    logger = logging.getLogger(__name__)

    vel_indx = np.empty(vel_rngs.shape, dtype=int)
    nvel_indx = np.empty(vel_rngs.shape, dtype=int)
    chns = np.zeros(len(vel_rngs))
    nchns = np.zeros(len(vel_rngs))
    nvel = []
    extend = nvel.extend

    logger.info("Velocity ranges for masking: ", vel_rngs)

    for i, velrng in enumerate(vel_rngs):

        vel_indx[i][0] = utils.best_match_indx(velrng[0], vel)
        vel_indx[i][1] = utils.best_match_indx(velrng[1], vel)

        nvel.extend(vel[vel_indx[i][0]:vel_indx[i][1] + 1])

        nvel_indx[i][0] = utils.best_match_indx(velrng[0], nvel)
        nvel_indx[i][1] = utils.best_match_indx(velrng[1], nvel)

        chns[i] = vel_indx[i][1] - vel_indx[i][0] + 1
        nchns[i] = nvel_indx[i][1] - nvel_indx[i][0] + 1

    mdata = np.ones(((int(sum(chns)), ) + data.shape[1:])) * offset
    logger.info('Masked data shape: {0}'.format(mdata.shape))

    logger.info('Will select the unmasked data.')
    for i in range(len(vel_indx)):
        mdata[nvel_indx[i][0]:nvel_indx[i][1] +
              1] = data[vel_indx[i][0]:vel_indx[i][1] + 1]

    return nvel, mdata
Example #8
0
def pressure_broad_coefs(Te):
    """
    Defines the values of the constants :math:`a` and :math:`\\gamma` that go into the collisional broadening formula
    of Salgado et al. (2017).
    
    :param Te: Electron temperature.
    :type Te: float
    :returns: The values of :math:`a` and :math:`\\gamma`.
    :rtype: list
    """

    te = [
        10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700,
        800, 900, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000,
        20000, 30000
    ]

    te_indx = utils.best_match_indx(Te, te)

    a = [
        -10.974098, -10.669695, -10.494541, -10.370271, -10.273172, -10.191374,
        -10.124309, -10.064037, -10.010153, -9.9613006, -9.6200366, -9.4001678,
        -9.2336349, -9.0848840, -8.9690170, -8.8686695, -8.7802238, -8.7012421,
        -8.6299908, -8.2718376, -8.0093937, -7.8344941, -7.7083367, -7.6126791,
        -7.5375720, -7.4770500, -7.4272885, -7.3857095, -7.1811733, -7.1132522
    ]

    gammac = [
        5.4821631, 5.4354009, 5.4071360, 5.3861013, 5.3689105, 5.3535398,
        5.3409679, 5.3290318, 5.3180304, 5.3077770, 5.2283700, 5.1700702,
        5.1224893, 5.0770049, 5.0408369, 5.0086342, 4.9796105, 4.9532071,
        4.9290080, 4.8063682, 4.7057576, 4.6356118, 4.5831746, 4.5421547,
        4.5090104, 4.4815675, 4.4584053, 4.4385507, 4.3290786, 4.2814240
    ]

    a_func = interpolate.interp1d(te,
                                  a,
                                  kind='linear',
                                  bounds_error=True,
                                  fill_value=0.0)

    g_func = interpolate.interp1d(te,
                                  gammac,
                                  kind='linear',
                                  bounds_error=True,
                                  fill_value=0.0)

    return [a_func(Te), g_func(Te)]
Example #9
0
def lookup_freq(n, line):
    """
    Returns the frequency of a line given the transition number n.
    
    :param n: Principal quantum number to look up for.
    :type n: int
    :param line: Line for which the frequency is desired.
    :type line: string
    :returns: Frequency of line(n).
    :rtype: float
    """

    qns, freqs = load_ref(line)
    indx = utils.best_match_indx(n, qns)

    return freqs[indx]
Example #10
0
def get_line_mask2(freq, reffreq, dv):
    """
    Return a mask with ranges where a line is expected in the given frequency range for \
    a line with a given reference frequency and line width dv.
    
    :param freq: Frequency axis where the line is located.
    :type freq: numpy array or list
    :param reffreq: Reference frequency for the line.
    :type reffreq: float
    :param dv: Velocity range to mask.
    :type dv: float, km/s
    :returns: Mask centered at the line center and width `dv0` referenced to the input `freq`.
    """

    df = dv2df(reffreq, dv * 1e3)
    df_chan = utils.get_min_sep(freq)
    f0_indx = utils.best_match_indx(reffreq, np.asarray(freq))

    f_mini = int(f0_indx - df / df_chan)
    if f_mini < 0:
        f_mini = 0
    f_maxi = int(f0_indx + df / df_chan)

    return [f_mini, f_maxi]
Example #11
0
def stack_cubes(cubes,
                outfits,
                vmax,
                vmin,
                dv,
                weight_list=None,
                v_axis=3,
                overwrite=False,
                algo='channel'):
    """
    """

    logger = logging.getLogger(__name__)

    cubel = cubes  #glob.glob(cubes)
    logger.debug(cubel)

    if dv == 0:
        logger.info('Velocity width not specified.'),
        logger.info('Will try to determine from input data.')
        for i, cube in enumerate(cubel):
            hdu = fits.open(cube)
            head = hdu[0].header
            x = crrls.get_axis(head, v_axis)
            if i == 0:
                dv = utils.get_min_sep(x)
                vmax_min = max(x)
                vmin_max = min(x)
            else:
                dv = max(dv, utils.get_min_sep(x))
                vmax_min = min(vmax_min, max(x))
                vmin_max = max(vmin_max, min(x))
            logger.debug('Cube: {0}'.format(cube))
            logger.debug('Cube velocity limits: {0} {1} {2}'.format(
                min(x), max(x), utils.get_min_sep(x)))
        logger.info('Will use a velocity width of {0}'.format(dv))

        # Check velocity ranges to avoid latter crashes.
        if vmax_min < vmax:
            logger.info('Requested maximum velocity is larger '\
                        'than one of the cubes velocity axis.')
            #logger.info('Conflicting cube: {0}'.format(cube))
            logger.info('v_max={0}, v_max_min={1}'.format(vmax, vmax_min))
            logger.info('Will now exit')
#            sys.exit(1)
        if vmin_max > vmin:
            logger.info('Requested minimum velocity is smaller '\
                        'than one of the cubes velocity axis.')
            #logger.info('Conflicting cube: {0}'.format(cube))
            logger.info('v_min={0}, v_min_max={1}'.format(vmin, vmin_max))
            logger.info('Will now exit')


#            sys.exit(1)

    shape = hdu[0].shape
    if len(shape) > 3:
        logger.info('Will drop first axis.')
        s = 1
    else:
        s = 0

    nvaxis = np.arange(vmin, vmax + dv, dv)
    stack = np.ma.zeros((len(nvaxis), ) + shape[s + 1:])
    covrg = np.zeros((len(nvaxis), ) + shape[s + 1:])

    # Check if there is a weight list
    if weight_list:
        try:
            wl = np.loadtxt(weight_list,
                            dtype=[('fits', np.str_, 256), ('w', np.float64)])
        except ValueError:
            wl = np.loadtxt(weight_list,
                            dtype=[('fits', np.str_, 256),
                                   ('w', np.str_, 256)])
    weight = np.ma.zeros((len(nvaxis), ) + shape[s + 1:])

    logger.info('Output stack will have dimensions: {0}'.format(stack.shape))

    for i, cube in enumerate(cubel):

        logger.info('Adding cube {0}/{1}'.format(i, len(cubel) - 1))

        # Load the data
        hdu = fits.open(cube)
        head = hdu[0].header
        data = np.ma.masked_invalid(hdu[0].data)

        # Check the weight
        if weight_list:
            w = wl['w'][np.where(wl['fits'] == cube)]
        else:
            w = 1
        logger.info('Will use a weight of {0}.'.format(w))
        try:
            aux_weight = np.ones(stack.shape) * w
        except TypeError:
            w = np.ma.masked_invalid(fits.open(w[0])[0].data)
        #aux_weight = np.ones(shape[s+1:])*w

        if len(data.shape) > 3:
            logger.info('Will drop first axis.')
            data = data[0]

        # Get the cube axes
        ra = crrls.get_axis(head, 1)
        de = crrls.get_axis(head, 2)
        ve = crrls.get_axis(head, v_axis)

        logger.info('RA axis limits: {0} {1}'.format(min(ra), max(ra)))
        logger.info('DEC axis limits: {0} {1}'.format(min(de), max(de)))
        logger.info('VEL axis limits: {0} {1}'.format(min(ve), max(ve)))

        vmin_idx = utils.best_match_indx(vmin, ve)
        vmax_idx = utils.best_match_indx(vmax, ve)
        [vmin_idx, vmax_idx] = sorted([vmin_idx, vmax_idx])
        if vmin_idx > 0: vmin_idx -= 1
        if vmax_idx < len(ve): vmax_idx += 1
        data_ = data[vmin_idx:vmax_idx]
        ve_ = ve[vmin_idx:vmax_idx]

        # Check that the axes are in ascending order
        if ve_[0] > ve_[1]:
            vs = -1
        else:
            vs = 1

        if ra[0] > ra[1]:
            vr = -1
        else:
            vr = 1

        # Interpolate the data
        interp = RegularGridInterpolator((ve_[::vs], de, ra[::vr]),
                                         data_[::vs, :, ::vr])

        # Add the data to the stack
        if 'vector' in algo.lower():

            logger.info('Will use one vector to reconstruct the cube')
            pts = np.array([[nvaxis[k], de[j], ra[i]] \
                            for k in range(len(nvaxis)) \
                            for j in range(len(de)) \
                            for i in range(len(ra))])
            aux_weight = np.ones(stack.shape) * w
            stack += interp(pts).reshape(stack.shape) * aux_weight

        elif 'channel' in algo.lower():

            logger.info('Will reconstruct the cube one channel at a time')

            for k in range(len(nvaxis)):

                pts = np.array([[nvaxis[k], de[j], ra[i]] \
                                for j in range(len(de)) \
                                for i in range(len(ra[::vr]))])

                newshape = shape[s + 1:]  # Only spatial dimensions
                aux_weight = np.ones(newshape) * w
                aux_cov = np.ones(newshape)

                try:
                    stack_ = np.ma.masked_invalid(
                        interp(pts).reshape(newshape) * aux_weight)
                    stack_.fill_value = 0.
                    aux_weight[stack_.mask] = 0
                    aux_cov[stack_.mask] = 0
                    weight[k] += aux_weight
                    covrg[k] += aux_cov
                    stack[k] += stack_.filled()

                except ValueError:
                    logger.info('Point outside range: ' \
                                'vel={0}, ra={1}..{2}, dec={3}..{4}'.format(pts[0,0],
                                                                            min(pts[:,2]),
                                                                            max(pts[:,2]),
                                                                            min(pts[:,1]),
                                                                            max(pts[:,1])))

        else:
            logger.info('Cube reconstruction algorithm unrecognized.')
            logger.info('Will exit now.')
            sys.exit(1)

    # Divide by the number of input cubes to get the mean
    stack = stack / weight
    stack.fill_value = np.nan
    weight.fill_value = np.nan

    # Write to a fits file
    hdulist = fits.PrimaryHDU(stack.filled())
    # Copy the header from the first channel image
    hdulist.header = fits.open(cubel[0])[0].header.copy()
    hdulist.header['CTYPE3'] = 'VELO'
    hdulist.header['CRVAL3'] = nvaxis[0]
    hdulist.header['CDELT3'] = dv
    hdulist.header['CRPIX3'] = 1
    hdulist.header['CUNIT3'] = 'm/s'
    hdulist.writeto(outfits, overwrite=overwrite)

    stack_head = hdulist.header.copy()
    hdulist = fits.PrimaryHDU(weight.filled())
    hdulist.header = stack_head
    hdulist.header['CTYPE3'] = 'VELO'
    hdulist.header['CRVAL3'] = nvaxis[0]
    hdulist.header['CDELT3'] = dv
    hdulist.header['CRPIX3'] = 1
    hdulist.header['CUNIT3'] = 'm/s'
    hdulist.writeto(outfits.split('.fits')[0] + '_weight.fits',
                    overwrite=overwrite)

    hdulist = fits.PrimaryHDU(covrg)
    hdulist.header = stack_head
    hdulist.header['CTYPE3'] = 'VELO'
    hdulist.header['CRVAL3'] = nvaxis[0]
    hdulist.header['CDELT3'] = dv
    hdulist.header['CRPIX3'] = 1
    hdulist.header['CUNIT3'] = 'm/s'
    hdulist.writeto(outfits.split('.fits')[0] + '_coverage.fits',
                    overwrite=overwrite)
Example #12
0
def stack_interpol(specs, output, vmax, vmin, dv, x_col, y_col, weight, weight_list=None, weight_list_cols='0,1'):
    """
    """

    logger = logging.getLogger(__name__)
    #specs = glob.glob(spec)
    
    # If only one file is passed, it probably contains the list
    #if len(specs) == 1:
        #specs = np.genfromtxt(specs[0], dtype=str)
        
    # Setup weight list columns, if need be.
    if weight_list:
        wlc0,wlc1 = list(map(int, weight_list_cols.split(',')))
        print(wlc0,wlc1)
    
    logger.info('Will use the following files as input: ')
    logger.info(specs)
    logger.info('The stack consists of {0} lines'.format(len(specs)))
    
    # Determine velocity resolution of stack.
    if dv == 0:
        logger.info('Will determine the velocity resolution of the stack.')
        for i,s in enumerate(specs):
            data = np.loadtxt(s)
            x = data[:,x_col]
            if i == 0:
                dv = utils.get_min_sep(x)
            else:
                dv = max(dv, utils.get_min_sep(x))
            logger.info('Spectrum {0} has velocity resolution of: {1}'.format(s, utils.get_min_sep(x)))
    logger.info('Stack velocity resolution: {0}'.format(dv))

    # Setup the grid for the stack.
    xgrid = np.arange(vmin, vmax, dv)
    ygrid = np.zeros(len(xgrid))      # the temperatures
    zgrid = np.zeros(len(xgrid))      # the number of tb points in every stacked channel
    
    for i,s in enumerate(specs):
        
        logger.info('Working on file: {0}'.format(s))
        
        data = np.loadtxt(s)
        x = data[:,x_col]
        y = data[:,y_col]
        
        # Sort by velocity
        o = x.argsort()
        x = x[o]
        y = y[o]
        
        # Catch NaNs and invalid values:
        mask_x = np.ma.masked_equal(x, -9999).mask
        mask_y = np.isnan(y)
        mask = mask_x | mask_y
        
        # Interpolate non masked ranges indepently.
        my = np.ma.masked_where(mask, y)
        mx = np.ma.masked_where(mask, x)
        valid = np.ma.flatnotmasked_contiguous(mx)
        y_aux = np.zeros(len(xgrid))
        if not isinstance(valid, slice):
            for j,rng in enumerate(valid):
                #print "slice {0}: {1}".format(i, rng)
                if len(x[rng]) > 1:
                    interp_y = interpolate.interp1d(x[rng], y[rng],
                                                    kind='linear',
                                                    bounds_error=False,
                                                    fill_value=0.0)
                    y_aux += interp_y(xgrid)
                elif not np.isnan(x[rng]):
                    #print x[rng]
                    y_aux[utils.best_match_indx(x[rng], xgrid)] += y[rng]                    
        else:
            #print "slice: {0}".format(valid)
            interp_y = interpolate.interp1d(x[valid], y[valid],
                                            kind='linear',
                                            bounds_error=False,
                                            fill_value=0.0)
            y_aux += interp_y(xgrid)
        
        # Check which channels have data.
        ychan = [1 if ch != 0 else 0 for ch in y_aux]
        
        # Determine the weight.
        w = None
        if not weight:
            w = np.ones(len(xgrid))
            
        elif weight == 'list':
            wl = np.loadtxt(weight_list, dtype=str)
            try:
                w = float(wl[:,wlc1][np.where(wl[:,wlc0] == s)[0][0]])
            except IndexError:
                logger.error("Weight for spectrum {} not found in {} .".format(s, weight_list))
                logger.error("Will not include it in the stack.")
                continue
            
        elif weight == 'sigma':
            w = 1./crrls.get_rms(my)
            
        elif weight == 'sigma2':
            w = 1./np.power(crrls.get_rms(my), 2)
        
        logger.info('Will use a weight of: {0}'.format(w))
        
        # Stack!
        zgrid = zgrid + np.multiply(ychan, w)
        ygrid = ygrid + y_aux*np.multiply(ychan, w)
            
    # Divide by the total weight to preserve optical depth
    ygrid = np.divide(ygrid, zgrid)
    
    logger.info('Saving stack to: {0}'.format(output))
    np.savetxt(output, np.c_[xgrid, ygrid, zgrid], header="x axis, " \
                                                          "stacked y axis, " \
                                                          "y axis weight")