Ejemplo n.º 1
0
def generate_telluric_mask(spectrum,
                           telluric_spectrum,
                           cutoff_transmission=.9,
                           window=4 / 40):
    """
    Note: all wavelengths of spectrum and telluric spectrum should be in Angstroms.
    :param: spectrum: astropy.table.Table. spectrum['wavelength'] should give an nd.array of wavelengths.
    :param: telluric_spectrum: 2d numpy array of shape [2, N]. Row [0] should be the wavelengths, row [1] the
    :param: normalized transmission (i.e. 1 means perfect transmission through the atmosphere, 0 means total absorption)
    :param: cutoff_transmission: float between 0 and 1. Any wavelengths corresponding to transmission less than
    this value will
    :param: be added to the mask
    :param: window: float. Wavelength window in angstroms. For each masked region,
     we will expand the mask by this amount in wavelengths. This will catch the wings of telluric lines.
    """
    mask = np.zeros(spectrum['wavelength'].shape)
    wavelengths_ignore = np.sort(
        telluric_spectrum[0][telluric_spectrum[1] < cutoff_transmission])
    if len(wavelengths_ignore) == 1:
        # force length 2 so that find_nearest does not fail.
        wavelengths_ignore = np.array(
            [wavelengths_ignore[0], wavelengths_ignore[0]])
    if len(wavelengths_ignore) >= 2:
        # only try to generate the mask if there are any wavelengths we need to ignore.
        mask = np.isclose(spectrum['wavelength'].data - find_nearest(
            np.array(spectrum['wavelength'].data), wavelengths_ignore),
                          0,
                          atol=window)
    return mask
Ejemplo n.º 2
0
    def do_stage(self, image):
        lab_lines = find_nearest(image.features['wavelength'],
                                 np.sort(image.line_list))
        delta_lambda = image.features['wavelength'] - lab_lines

        sigma_delta_lambda = robust_standard_deviation(delta_lambda)
        low_scatter_lines = delta_lambda < 3. * sigma_delta_lambda

        matched_sigma_delta_lambda = robust_standard_deviation(
            delta_lambda[low_scatter_lines])
        num_detected_lines = len(image.features['wavelength'])
        num_matched_lines = np.count_nonzero(low_scatter_lines)

        feature_centroid_uncertainty = image.features['centroid_err']

        reduced_chi2 = get_reduced_chi_squared(
            delta_lambda[low_scatter_lines],
            feature_centroid_uncertainty[low_scatter_lines])
        velocity_precision = get_velocity_precision(
            image.features['wavelength'][low_scatter_lines],
            lab_lines[low_scatter_lines], num_matched_lines)

        if num_matched_lines == 0:  # get rid of nans in the matched statistics if we have zero matched lines.
            matched_sigma_delta_lambda, reduced_chi2, velocity_precision = 0, 0, 0 * units.meter / units.second

        # opensearch keys don't have to be the same as the fits headers
        qc_results = {
            'SIGLAM':
            np.round(matched_sigma_delta_lambda, 4),
            'RVPRECSN':
            np.round(
                velocity_precision.to(units.meter / units.second).value, 4),
            'WAVRCHI2':
            np.round(reduced_chi2, 4),
            'NLINEDET':
            num_detected_lines,
            'NLINES':
            num_matched_lines
        }
        qc_description = {
            'SIGLAM': 'wavecal residuals [Angstroms]',
            'RVPRECSN': 'wavecal precision [m/s]',
            'WAVRCHI2': 'reduced chisquared goodness of wavecal fit',
            'NLINEDET': 'Number of lines found on detector',
            'NLINES': 'Number of matched lines'
        }
        qc.save_qc_results(self.runtime_context, qc_results, image)
        # saving the results to the image header
        for key in qc_results.keys():
            image.meta[key] = (qc_results[key], qc_description[key])

        logger.info(f'wavecal precision (m/s) = {qc_results["RVPRECSN"]}',
                    image=image)
        if qc_results['RVPRECSN'] > 10 or qc_results['RVPRECSN'] < 3:
            logger.warning(
                f' Final calibration precision is outside the expected range '
                f'wavecal precision (m/s) = '
                f'{qc_results["RVPRECSN"]}',
                image=image)
        return image
Ejemplo n.º 3
0
 def test_line_matching(self):
     nlines, wavelength_scatter = 100, 0.1
     mock_lines = np.linspace(4000, 5000, nlines)
     line_list = np.random.permutation(mock_lines)
     features = mock_lines + np.random.randn(nlines) * wavelength_scatter
     lab_lines = find_nearest(features, np.sort(line_list))
     sigma_delta_lambda = robust_standard_deviation(features - lab_lines)
     assert np.isclose(sigma_delta_lambda, wavelength_scatter, rtol=5.e-2)
Ejemplo n.º 4
0
    def fit_wavelength_model(features, line_list, m0):
        """
        Fit a polynomial model to the feature wavelengths with light outlier rejection. This recalibrates
        the wavelength solution.
        :param features:
        :param line_list:
        :param m0: Real diffraction order number of the i=0 diffraction order. Also known as m0.
        :return: wavelength_solution. xwavecal.wavelength.WavelengthSolution.
        wavelength_solution(x, order) will give the wavelength of the len(x) points with pixel and order coordinates
        x and order, respectively.
        where x and order are ndarray's of the same shape.
        """
        # TODO update xwavecal so that WavelengthSolution does not need min_order, max_order, min_pixel, max_pixel.
        wcs = WavelengthSolution(model=WAVELENGTH_SOLUTION_MODEL,
                                 measured_lines=dict(features),
                                 min_order=0,
                                 max_order=np.max(features['order']),
                                 min_pixel=0,
                                 max_pixel=np.max(features['pixel']),
                                 reference_lines=line_list,
                                 m0=m0)
        wavelengths_to_fit = find_nearest(features['wavelength'],
                                          np.sort(line_list))
        # residuals = wavelengths_to_fit - features['wavelength']
        weights = np.ones_like(wavelengths_to_fit, dtype=float)
        # consider weights = features['flux']/features['flux_err'] or 1/features['flux_err']**2
        # reject lines who have residuals with the line list in excess of 0.1 angstroms (e.g. reject outliers)
        # establish an initial solution:
        weights[~np.isclose(
            wavelengths_to_fit, wcs.measured_lines['wavelength'], atol=0.1
        )] = 0
        wcs.model_coefficients = wcs.solve(wcs.measured_lines,
                                           wavelengths_to_fit, weights)
        # iteratively refine the wavelength solution:
        """
        # call xwavecals internal routine for iteratively refining the wavelength solution.
        # wcs.measured_lines['weight'] = 1 # uncomment and set to weights, could do any weighting scheme.
        wcs, residuals = refine_wcs(wcs, wcs.measured_lines, np.sort(line_list), SolutionRefineOnce._converged,
                                    SolutionRefineOnce._clip, max_iter=20,
                                    kwargs={'sigma': 4, 'stdfunc': robust_standard_deviation})

        logger.info(f'Final robust standard deviation after'
                    f' refining: {robust_standard_deviation(residuals)} Angstrom')
        """
        return wcs