Ejemplo n.º 1
0
def test_eval():
    """ Tests evaluation of Linear1D and Planar2D with different model_set_axis."""
    model = Linear1D(slope=[1, 2], intercept=[3, 4], n_models=2)
    p = Polynomial1D(1, c0=[3, 4], c1=[1, 2], n_models=2)

    assert_allclose(model(xx), p(xx))
    assert_allclose(model(x, model_set_axis=False), p(x, model_set_axis=False))

    with pytest.raises(ValueError):
        model(x)

    model = Linear1D(slope=[[1, 2]],
                     intercept=[[3, 4]],
                     n_models=2,
                     model_set_axis=1)
    p = Polynomial1D(1, c0=[[3, 4]], c1=[[1, 2]], n_models=2, model_set_axis=1)

    assert_allclose(model(xx.T), p(xx.T))
    assert_allclose(model(x, model_set_axis=False), p(x, model_set_axis=False))
    with pytest.raises(ValueError):
        model(xx)

    model = Planar2D(slope_x=[1, 2],
                     slope_y=[1, 2],
                     intercept=[3, 4],
                     n_models=2)
    y = model(xx, xx)

    assert y.shape == (2, 4)

    with pytest.raises(ValueError):
        model(x)
Ejemplo n.º 2
0
def test_fitting_shapes():
    """ Test fitting model sets of Linear1D and Planar2D."""
    fitter = LinearLSQFitter()

    model = Linear1D(slope=[1, 2], intercept=[3, 4], n_models=2)
    y = model(xx)
    fit_model = fitter(model, x, y)

    model = Linear1D(slope=[[1, 2]], intercept=[[3, 4]], n_models=2, model_set_axis=1)
    fit_model = fitter(model, x, y.T)

    model = Planar2D(slope_x=[1, 2], slope_y=[1, 2], intercept=[3, 4], n_models=2)
    y = model(xx, xx)
    fit_model = fitter(model, x, x, y)
Ejemplo n.º 3
0
def linear_time_model(cadence: u.s, reference_val: u.s = 0 * u.s):
    """
    A linear model in a temporal dimension. The reference pixel is always 0.
    """
    if not reference_val:
        reference_val = 0 * cadence.unit
    return Linear1D(slope=cadence / (1 * u.pix), intercept=reference_val)
Ejemplo n.º 4
0
    def linear_solution(self):
        from astropy.modeling.models import Linear1D

        m = Linear1D(1, 0)
        if len(self.linepixtowl) > 1:
            return _linfit(m, list(self.linepixtowl.keys()),
                           list(self.linepixtowl.values()))
        else:
            raise ValueError('cannot make linear solution w < 2 points')
    def _get_wavelength_calibration(hdr):

        from astropy.modeling.models import Linear1D, Const1D

        _wcal_model = (
            Const1D(hdr.get('CRVAL1')) +
            Linear1D(slope=hdr.get('CD1_1'), intercept=hdr.get('CRPIX1') - 1))

        assert _wcal_model(0) == hdr.get('CRVAL1')

        return _wcal_model
Ejemplo n.º 6
0
def measure_SNR(spex_path, data_path, plot_path, star, ranges):
    """
    Measure the SNR of a spectrum, by fitting straight lines to pieces of the spectrum
    """
    # plot the spectrum to define regions without spectral lines
    fig, ax = plot_spectrum(star, data_path, range=[0.75, 5.6], log=True)

    # read in all bands and spectra for this star
    starobs = StarData("%s.dat" % star.lower(), path=data_path, use_corfac=True)

    # obtain flux values at a few wavelengths and fit a straight line through the data
    waves, fluxes, uncs = starobs.get_flat_data_arrays(["SpeX_SXD", "SpeX_LXD"])
    print(star)
    for range in ranges:
        min_indx = np.abs(waves - range[0]).argmin()
        max_indx = np.abs(waves - range[1]).argmin()
        func = Linear1D()
        fit = LinearLSQFitter()
        fit_result = fit(func, waves[min_indx:max_indx], fluxes[min_indx:max_indx])
        residu = fluxes[min_indx:max_indx] - fit_result(waves[min_indx:max_indx])

        # calculate the SNR from the data
        data_sxd = Table.read(
            spex_path + star + "_sxd.txt",
            format="ascii",
        )
        data_lxd = Table.read(
            spex_path + star + "_lxd.txt",
            format="ascii",
        )
        data = vstack([data_sxd, data_lxd])
        data.sort("col1")
        print("wave_range", range)
        min_indx2 = np.abs(data["col1"] - range[0]).argmin()
        max_indx2 = np.abs(data["col1"] - range[1]).argmin()

        SNR_data = np.nanmedian((data["col2"] / data["col3"])[min_indx2:max_indx2])
        print("SNR from data", SNR_data)

        # calculate the SNR from the noise around the linear fit
        mean, median, stddev = sigma_clipped_stats(residu)
        SNR_fit = np.median(fluxes[min_indx:max_indx] / stddev)
        print("SNR from fit", SNR_fit)

        # plot the fitted lines on top of the spectrum
        ax.plot(
            waves[min_indx:max_indx],
            fit_result(waves[min_indx:max_indx]),
            lw=2,
            alpha=0.8,
            color="k",
        )
        fig.savefig(plot_path + star + "_SNR_measure.pdf")
Ejemplo n.º 7
0
def test_fit_multiplied_compound_model_with_mixed_units():
    """
    Regression test for issue #12320
    """

    fitter = LevMarLSQFitter()
    x = np.linspace(0, 1, 101) * u.s
    y = np.linspace(5, 10, 101) * u.m * u.kg / u.s

    m1 = Linear1D(slope=5 * u.m / u.s / u.s, intercept=1.0 * u.m / u.s)
    m2 = Linear1D(slope=0.0 * u.kg / u.s, intercept=10.0 * u.kg)
    truth = m1 * m2
    fit = fitter(truth, x, y)

    unfit_output = truth(x)
    fit_output = fit(x)

    assert unfit_output.unit == fit_output.unit == (u.kg * u.m / u.s)
    assert_allclose(unfit_output, fit_output)

    for name in truth.param_names:
        assert getattr(truth, name) == getattr(fit, name)
Ejemplo n.º 8
0
    def __call__(cls, lines=None, continuum=None):
        """
        See the `Absorption1D` initializer for details.
        """
        mod_list = []

        if continuum is not None:
            if issubclass(continuum.__class__, Fittable1DModel):
                mod_list.append(continuum)
            elif isinstance(continuum, six.string_types):
                continuum = getattr(models, continuum, 'Linear1D')
                mod_list.append(continuum)
            else:
                raise AttributeError("Unknown continuum type {}.".format(
                    type(continuum)))
        else:
            continuum = Linear1D(slope=0, intercept=1)
            mod_list.append(continuum)

        if lines is not None:
            if isinstance(lines, list):
                mod_list += lines
            elif issubclass(lines, Fittable1DModel):
                mod_list.append(lines)

        abs_mod = np.sum(mod_list)

        def call(self,
                 dispersion,
                 uncertainty=None,
                 dispersion_unit=None,
                 *args,
                 **kwargs):
            from ..spectra import Spectrum1D

            lines = abs_mod._submodels[1:]  # noqa

            flux = super(abs_mod.__class__,
                         self).__call__(dispersion, *args, **kwargs)
            spectrum = Spectrum1D(flux,
                                  dispersion=dispersion,
                                  dispersion_unit=dispersion_unit)

            return spectrum

        mod = type('Absorption1D', (abs_mod.__class__, ), {'__call__': call})

        return mod()
Ejemplo n.º 9
0
def test_custom_continuum():
    line1 = OpticalDepth1D(lambda_0=1216 * u.AA)
    continuum = Linear1D(slope=1 / u.AA, intercept=0 * u.Unit(''))

    # Create model from lambda
    spec_mod = Spectral1D(line1, continuum=continuum)

    wav = np.linspace(1100, 1300, 500) * u.AA
    vel = np.linspace(-100, 100, 500) * u.km / u.s

    # Test in wavelength and velocity spaces
    assert len(spec_mod(wav)) == 500
    assert len(spec_mod(vel)) == 500

    assert np.trapz(spec_mod(wav), x=wav) > 0
    assert np.trapz(spec_mod(vel), x=vel) > 0
Ejemplo n.º 10
0
def generate_phi(cmau_array, cmau_ind, log_wav, z, abs_mag_bins):
    r'''
    Generate a Schechter (1976) [1]_ function from interpolated,
    wavelength-dependent parameters.

    Parameters
    ----------
    cmau_array : numpy.ndarray
        A shape ``(5, 2, 4)`` array, containing the c, m, a, and u parameters
        for  :math:`M^*_0`, :math:\phi^*_0`, :math:`\alpha`, P, and Q for "blue"
        and "red" galaxies respectively.
    cmau_ind : {0, 1}
        Value indicating whether we're generating galaxy densities for blue
        or red galaxies.
    log_wav : float
        The log-10 value of the wavelength of the particular observations, in
        microns.
    z : float
        The redshift at which to evaluate the Schechter luminosity function.
    abs_mag_bins : numpy.ndarray
        The absolute magnitudes at which to evaluate the Schechter function.

    Returns
    -------
    phi_model : numpy.ndarray
        The Schechter function differential galaxy density at ``z``.

    References
    ----------
    .. [1] Schechter P. (1976), ApJ, 203, 297

    '''
    M_star0 = function_evaluation_lookup(cmau_array, 0, cmau_ind, log_wav)
    phi_star0 = function_evaluation_lookup(cmau_array, 1, cmau_ind, log_wav)
    alpha = function_evaluation_lookup(cmau_array, 2, cmau_ind, log_wav)
    P = function_evaluation_lookup(cmau_array, 3, cmau_ind, log_wav)
    # All other parameters are a function of wavelength, but we want Q(P).
    Q = function_evaluation_lookup(cmau_array, 4, cmau_ind, P)
    # phi*(z) = phi* * 10**(0.4 P z) = phi* exp(0.4 * ln(10) P z)
    # and thus Exponential1D being of the form exp(x / tau),
    tau = 1 / (0.4 * np.log(10) * P)
    m_star = Linear1D(slope=-Q, intercept=M_star0)
    phi_star = Exponential1D(amplitude=phi_star0, tau=tau)
    L = 10**(-0.4 * (abs_mag_bins - m_star(z)))
    phi_model = 0.4 * np.log(10) * phi_star(z) * L**(alpha + 1) * np.exp(-L)

    return phi_model
            # fit the aveDN versus diffDN combined data from all integrations
            # e.g., derive the non-linearity correction
            x = np.concatenate(x)
            y = np.concatenate(y)
            mod = fit_diffs(x, y, noexp=noexp)
            if noexp:
                polymod = mod
            else:
                polymod = mod[2]
            lincormod = polymod

        # more plots
        ints = range(nints)

        # setup ramp fit
        line_init = Linear1D()
        fit_line = LinearLSQFitter()
        mult_comp = False

        intslopes = np.zeros((nints))
        linfit_metric = np.zeros((nints))
        intexpamp = np.zeros((nints))
        for k in range(nints):
            gnum, ydata = get_ramp(hdu[0], pix_x, pix_y, k)
            ggnum, gdata, aveDN, diffDN = get_good_ramp(
                gnum, ydata, keepfirst=args.keepfirst, nmax=args.nmax)

            # correct the ramps and plot
            gdata_cor = lincor_data(lincormod, gdata, aveDN, diffDN)

            # plot the linearied 2pt diffs versus average DN
Ejemplo n.º 12
0
def fit_features_spec(star, path):
    """
    Fit the features directly from the spectrum with different profiles

    Parameters
    ----------
    star : string
        Name of the reddened star for which to fit the features in the spectrum

    path : string
        Path to the data files

    Returns
    -------
    waves : np.ndarray
        Numpy array with wavelengths

    flux_sub : np.ndarray
        Numpy array with continuum subtracted fluxes

    results : list
        List with the fitted models for different profiles
    """
    # obtain the spectrum of the reddened star
    stardata = StarData(star + ".dat", path)
    npts = stardata.data["SpeX_LXD"].npts
    waves = stardata.data["SpeX_LXD"].waves.value
    flux_unc = stardata.data["SpeX_LXD"].uncs

    # "manually" obtain the continuum from the spectrum (i.e. read the flux at 2.4 and 3.6 micron)
    plot_spectrum(
        star,
        path,
        mlam4=True,
        range=[2, 4.5],
        exclude=["IRS", "STIS_Opt"],
    )

    # fit the continuum reference points with a straight line
    ref_waves = [2.4, 3.6]
    fluxes = [3.33268e-12, 4.053e-12]
    func = Linear1D()
    fit = LinearLSQFitter()
    fit_result = fit(func, ref_waves, fluxes)

    # subtract the continuum from the fluxes
    fluxes = stardata.data["SpeX_LXD"].fluxes.value * waves ** 4 - fit_result(waves)

    # define different profiles
    # 2 Gaussians (stddev=FWHM/(2sqrt(2ln2)))
    gauss = Gaussian1D(mean=3, stddev=0.13) + Gaussian1D(
        mean=3.4, stddev=0.06, fixed={"mean": True}
    )

    # 2 Drudes
    drude = Drude1D(x_0=3, fwhm=0.3) + Drude1D(x_0=3.4, fwhm=0.15, fixed={"x_0": True})

    # 2 Lorentzians
    lorentz = Lorentz1D(x_0=3, fwhm=0.3) + Lorentz1D(
        x_0=3.4, fwhm=0.15, fixed={"x_0": True}
    )

    # 2 asymmetric Gaussians
    Gaussian_asym = custom_model(gauss_asymmetric)
    gauss_asym = Gaussian_asym(x_o=3, gamma_o=0.3) + Gaussian_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 2 "asymmetric" Drudes
    Drude_asym = custom_model(drude_asymmetric)
    drude_asym = Drude_asym(x_o=3, gamma_o=0.3) + Drude_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 2 asymmetric Lorentzians
    Lorentzian_asym = custom_model(lorentz_asymmetric)
    lorentz_asym = Lorentzian_asym(x_o=3, gamma_o=0.3) + Lorentzian_asym(
        x_o=3.4, gamma_o=0.15, fixed={"x_o": True}
    )

    # 1 asymmetric Drude
    drude_asym1 = Drude_asym(x_o=3, gamma_o=0.3)

    profiles = [
        gauss,
        drude,
        lorentz,
        gauss_asym,
        drude_asym,
        lorentz_asym,
        drude_asym1,
    ]

    # fit the different profiles
    fit2 = LevMarLSQFitter()
    results = []
    mask1 = (waves > 2.4) & (waves < 3.6)
    mask2 = mask1 * (npts > 0)

    for profile in profiles:
        fit_result = fit2(
            profile,
            waves[mask2],
            fluxes[mask2],
            weights=1 / flux_unc[mask2],
            maxiter=10000,
        )
        results.append(fit_result)
        print(fit_result)
        print(
            "Chi2",
            np.sum(((fluxes[mask2] - fit_result(waves[mask2])) / flux_unc[mask2]) ** 2),
        )

    return waves[mask1], fluxes[mask1], npts[mask1], results
Ejemplo n.º 13
0
def linear_spectral_model(spectral_width: u.nm, reference_val: u.nm):
    """
    A linear model in a spectral dimension. The reference pixel is always 0.
    """
    return Linear1D(slope=spectral_width / (1 * u.pix),
                    intercept=reference_val)
Ejemplo n.º 14
0
    def build_nirspec_lamp_output_wcs(self):
        """
        Create a spatial/spectral WCS output frame for NIRSpec lamp mode

        Creates output frame by linearly fitting x_msa, y_msa along the slit and
        producing a lookup table to interpolate wavelengths in the dispersion
        direction.

        Returns
        -------
        output_wcs : `~gwcs.WCS` object
            A gwcs WCS object defining the output frame WCS
        """
        model = self.input_models[0]
        wcs = model.meta.wcs
        bbox = wcs.bounding_box
        grid = wcstools.grid_from_bounding_box(bbox)
        x_msa, y_msa, lam = np.array(wcs(*grid))
        # Handle vertical (MIRI) or horizontal (NIRSpec) dispersion.  The
        # following 2 variables are 0 or 1, i.e. zero-indexed in x,y WCS order
        spectral_axis = find_dispersion_axis(model)
        spatial_axis = spectral_axis ^ 1

        # Compute the wavelength array, trimming NaNs from the ends
        # In many cases, a whole slice is NaNs, so ignore those warnings
        warnings.simplefilter("ignore")
        wavelength_array = np.nanmedian(lam, axis=spectral_axis)
        warnings.resetwarnings()
        wavelength_array = wavelength_array[~np.isnan(wavelength_array)]

        # Find the center ra and dec for this slit at central wavelength
        lam_center_index = int((bbox[spectral_axis][1] -
                                bbox[spectral_axis][0]) / 2)
        x_msa_array = x_msa.T[lam_center_index]
        y_msa_array = y_msa.T[lam_center_index]
        x_msa_array = x_msa_array[~np.isnan(x_msa_array)]
        y_msa_array = y_msa_array[~np.isnan(y_msa_array)]

        # Estimate and fit the spatial sampling
        fitter = LinearLSQFitter()
        fit_model = Linear1D()
        xstop = x_msa_array.shape[0] / self.pscale_ratio
        xstep = 1 / self.pscale_ratio
        ystop = y_msa_array.shape[0] / self.pscale_ratio
        ystep = 1 / self.pscale_ratio
        pix_to_x_msa = fitter(fit_model, np.arange(0, xstop, xstep), x_msa_array)
        pix_to_y_msa = fitter(fit_model, np.arange(0, ystop, ystep), y_msa_array)

        step = 1 / self.pscale_ratio
        stop = wavelength_array.shape[0] / self.pscale_ratio
        points = np.arange(0, stop, step)
        pix_to_wavelength = Tabular1D(points=points,
                                      lookup_table=wavelength_array,
                                      bounds_error=False, fill_value=None,
                                      name='pix2wavelength')

        # Tabular models need an inverse explicitly defined.
        # If the wavelength array is descending instead of ascending, both
        # points and lookup_table need to be reversed in the inverse transform
        # for scipy.interpolate to work properly
        points = wavelength_array
        lookup_table = np.arange(0, stop, step)

        if not np.all(np.diff(wavelength_array) > 0):
            points = points[::-1]
            lookup_table = lookup_table[::-1]
        pix_to_wavelength.inverse = Tabular1D(points=points,
                                              lookup_table=lookup_table,
                                              bounds_error=False, fill_value=None,
                                              name='wavelength2pix')

        # For the input mapping, duplicate the spatial coordinate
        mapping = Mapping((spatial_axis, spatial_axis, spectral_axis))
        mapping.inverse = Mapping((2, 1))

        # The final transform
        # define the output wcs
        transform = mapping | pix_to_x_msa & pix_to_y_msa & pix_to_wavelength

        det = cf.Frame2D(name='detector', axes_order=(0, 1))
        sky = cf.Frame2D(name=f'resampled_{model.meta.wcs.output_frame.name}', axes_order=(0, 1))
        spec = cf.SpectralFrame(name='spectral', axes_order=(2,),
                                unit=(u.micron,), axes_names=('wavelength',))
        world = cf.CompositeFrame([sky, spec], name='world')

        pipeline = [(det, transform),
                    (world, None)]

        output_wcs = WCS(pipeline)

        # Compute the output array size and bounding box
        output_array_size = [0, 0]
        output_array_size[spectral_axis] = int(np.ceil(len(wavelength_array) / self.pscale_ratio))
        x_size = len(x_msa_array)
        output_array_size[spatial_axis] = int(np.ceil(x_size / self.pscale_ratio))
        # turn the size into a numpy shape in (y, x) order
        output_wcs.array_shape = output_array_size[::-1]
        output_wcs.pixel_shape = output_array_size
        bounding_box = resample_utils.wcs_bbox_from_shape(output_array_size[::-1])
        output_wcs.bounding_box = bounding_box

        return output_wcs
Ejemplo n.º 15
0
# exponential and linear functions of redshift respectively. Samples are
# generated for a given `sky_area` and over a given redshift range `z_range` up
# to a limiting apparent magnitude `mag_lim` with shot noise. The values of the
# parameters are taken from the B-band luminosity model for star-forming
# galaxies in López-Sanjuan et al. 2017 [1]_.

from astropy.cosmology import FlatLambdaCDM
from astropy.modeling.models import Linear1D, Exponential1D
from astropy.table import Table
from astropy.units import Quantity
from matplotlib import pyplot as plt
import numpy as np
from skypy.galaxies import schechter_lf

z_range = np.linspace(0.2, 1.0, 100)
m_star = Linear1D(-1.03, -20.485)
phi_star = Exponential1D(0.00312608, -43.4294)
alpha, mag_lim = -1.29, 30
sky_area = Quantity(2.381, "deg2")
cosmology = FlatLambdaCDM(H0=70, Om0=0.3)
redshift, magnitude = schechter_lf(z_range,
                                   m_star,
                                   phi_star,
                                   alpha,
                                   mag_lim,
                                   sky_area,
                                   cosmology,
                                   noise=True)

# %%
# ALHAMBRA B-Band Luminosity Function
Ejemplo n.º 16
0
    def build_nirspec_output_wcs(self, refmodel=None):
        """
        Create a spatial/spectral WCS covering footprint of the input
        """
        all_wcs = [m.meta.wcs for m in self.input_models if m is not refmodel]
        if refmodel:
            all_wcs.insert(0, refmodel.meta.wcs)
        else:
            refmodel = self.input_models[0]

        refwcs = refmodel.meta.wcs

        s2d = refwcs.get_transform('slit_frame', 'detector')
        d2s = refwcs.get_transform('detector', 'slit_frame')
        s2w = refwcs.get_transform('slit_frame', 'world')

        # estimate position of the target without relying in the meta.target:
        bbox = refwcs.bounding_box

        grid = wcstools.grid_from_bounding_box(bbox)
        _, s, lam = np.array(d2s(*grid))
        sd = s * refmodel.data
        ld = lam * refmodel.data
        good_s = np.isfinite(sd)
        if np.any(good_s):
            total = np.sum(refmodel.data[good_s])
            wmean_s = np.sum(sd[good_s]) / total
            wmean_l = np.sum(ld[good_s]) / total
        else:
            wmean_s = 0.5 * (refmodel.slit_ymax - refmodel.slit_ymin)
            wmean_l = d2s(*np.mean(bbox, axis=1))[2]

        targ_ra, targ_dec, _ = s2w(0, wmean_s, wmean_l)

        ref_lam = _find_nirspec_output_sampling_wavelengths(
            all_wcs,
            targ_ra, targ_dec
        )
        ref_lam = np.array(ref_lam)

        n_lam = ref_lam.size
        if not n_lam:
            raise ValueError("Not enough data to construct output WCS.")

        x_slit = np.zeros(n_lam)
        lam = 1e-6 * ref_lam

        # Find the spatial pixel scale:
        y_slit_min, y_slit_max = self._max_virtual_slit_extent(all_wcs, targ_ra, targ_dec)

        nsampl = 50
        xy_min = s2d(
            nsampl * [0],
            nsampl * [y_slit_min],
            lam[(tuple((i * n_lam) // nsampl for i in range(nsampl)), )]
        )
        xy_max = s2d(
            nsampl * [0],
            nsampl * [y_slit_max],
            lam[(tuple((i * n_lam) // nsampl for i in range(nsampl)), )]
        )

        good = np.logical_and(np.isfinite(xy_min), np.isfinite(xy_max))
        if not np.any(good):
            raise ValueError("Error estimating output WCS pixel scale.")

        xy1 = s2d(x_slit, np.full(n_lam, refmodel.slit_ymin), lam)
        xy2 = s2d(x_slit, np.full(n_lam, refmodel.slit_ymax), lam)
        xylen = np.nanmax(np.linalg.norm(np.array(xy1) - np.array(xy2), axis=0)) + 1
        pscale = (refmodel.slit_ymax - refmodel.slit_ymin) / xylen

        # compute image span along Y-axis (length of the slit in the detector plane)
        # det_slit_span = np.linalg.norm(np.subtract(xy_max, xy_min))
        det_slit_span = np.nanmax(np.linalg.norm(np.subtract(xy_max, xy_min), axis=0))
        ny = int(np.ceil(det_slit_span * self.pscale_ratio + 0.5)) + 1

        border = 0.5 * (ny - det_slit_span * self.pscale_ratio) - 0.5

        if xy_min[1][1] < xy_max[1][1]:
            y_slit_model = Linear1D(
                slope=pscale / self.pscale_ratio,
                intercept=y_slit_min - border * pscale * self.pscale_ratio
            )
        else:
            y_slit_model = Linear1D(
                slope=-pscale / self.pscale_ratio,
                intercept=y_slit_max + border * pscale * self.pscale_ratio
            )

        # extrapolate 1/2 pixel at the edges and make tabular model w/inverse:
        lam = lam.tolist()
        pixel_coord = list(range(n_lam))

        if len(pixel_coord) > 1:
            # left:
            slope = (lam[1] - lam[0]) / pixel_coord[1]
            lam.insert(0, -0.5 * slope + lam[0])
            pixel_coord.insert(0, -0.5)
            # right:
            slope = (lam[-1] - lam[-2]) / (pixel_coord[-1] - pixel_coord[-2])
            lam.append(slope * (pixel_coord[-1] + 0.5) + lam[-2])
            pixel_coord.append(pixel_coord[-1] + 0.5)

        else:
            lam = 3 * lam
            pixel_coord = [-0.5, 0, 0.5]

        wavelength_transform = Tabular1D(points=pixel_coord,
                                         lookup_table=lam,
                                         bounds_error=False, fill_value=np.nan)
        wavelength_transform.inverse = Tabular1D(points=lam,
                                                 lookup_table=pixel_coord,
                                                 bounds_error=False,
                                                 fill_value=np.nan)
        self.data_size = (ny, len(ref_lam))

        # Construct the final transform
        mapping = Mapping((0, 1, 0))
        mapping.inverse = Mapping((2, 1))
        out_det2slit = mapping | Identity(1) & y_slit_model & wavelength_transform

        # Create coordinate frames
        det = cf.Frame2D(name='detector', axes_order=(0, 1))
        slit_spatial = cf.Frame2D(name='slit_spatial', axes_order=(0, 1),
                                  unit=("", ""), axes_names=('x_slit', 'y_slit'))
        spec = cf.SpectralFrame(name='spectral', axes_order=(2,),
                                unit=(u.micron,), axes_names=('wavelength',))
        slit_frame = cf.CompositeFrame([slit_spatial, spec], name='slit_frame')
        sky = cf.CelestialFrame(name='sky', axes_order=(0, 1),
                                reference_frame=coord.ICRS())
        world = cf.CompositeFrame([sky, spec], name='world')

        pipeline = [(det, out_det2slit), (slit_frame, s2w), (world, None)]
        output_wcs = WCS(pipeline)

        # Compute bounding box and output array shape.  Add one to the y (slit)
        # height to account for the half pixel at top and bottom due to pixel
        # coordinates being centers of pixels
        bounding_box = resample_utils.wcs_bbox_from_shape(self.data_size)
        output_wcs.bounding_box = bounding_box
        output_wcs.array_shape = self.data_size

        return output_wcs
Ejemplo n.º 17
0
    def build_interpolated_output_wcs(self, refmodel=None):
        """
        Create a spatial/spectral WCS output frame

        Creates output frame by linearly fitting RA, Dec along the slit and
        producing a lookup table to interpolate wavelengths in the dispersion
        direction.

        Parameters
        ----------
        refmodel : `~jwst.datamodels.DataModel`
            The reference input image from which the fiducial WCS is created.
            If not specified, the first image in self.input_models is used.

        Returns
        -------
        output_wcs : `~gwcs.WCS` object
            A gwcs WCS object defining the output frame WCS
        """
        if refmodel is None:
            refmodel = self.input_models[0]
        refwcs = refmodel.meta.wcs
        bb = refwcs.bounding_box

        grid = wcstools.grid_from_bounding_box(bb)
        ra, dec, lam = np.array(refwcs(*grid))

        spectral_axis = find_dispersion_axis(lam)
        spatial_axis = spectral_axis ^ 1

        # Compute the wavelength array, trimming NaNs from the ends
        wavelength_array = np.nanmedian(lam, axis=spectral_axis)
        wavelength_array = wavelength_array[~np.isnan(wavelength_array)]

        # Compute RA and Dec up the slit (spatial direction) at the center
        # of the dispersion.  Use spectral_axis to determine slicing dimension
        lam_center_index = int(
            (bb[spectral_axis][1] - bb[spectral_axis][0]) / 2)
        if not spectral_axis:
            ra_array = ra.T[lam_center_index]
            dec_array = dec.T[lam_center_index]
        else:
            ra_array = ra[lam_center_index]
            dec_array = dec[lam_center_index]
        ra_array = ra_array[~np.isnan(ra_array)]
        dec_array = dec_array[~np.isnan(dec_array)]

        fitter = LinearLSQFitter()
        fit_model = Linear1D()
        pix_to_ra = fitter(fit_model, np.arange(ra_array.shape[0]), ra_array)
        pix_to_dec = fitter(fit_model, np.arange(dec_array.shape[0]),
                            dec_array)

        # Tabular interpolation model, pixels -> lambda
        pix_to_wavelength = Tabular1D(lookup_table=wavelength_array,
                                      bounds_error=False,
                                      fill_value=None,
                                      name='pix2wavelength')

        # Tabular models need an inverse explicitly defined.
        # If the wavelength array is decending instead of ascending, both
        # points and lookup_table need to be reversed in the inverse transform
        # for scipy.interpolate to work properly
        points = wavelength_array
        lookup_table = np.arange(wavelength_array.shape[0])
        if not np.all(np.diff(wavelength_array) > 0):
            points = points[::-1]
            lookup_table = lookup_table[::-1]
        pix_to_wavelength.inverse = Tabular1D(points=points,
                                              lookup_table=lookup_table,
                                              bounds_error=False,
                                              fill_value=None,
                                              name='wavelength2pix')

        # For the input mapping, duplicate the spatial coordinate
        mapping = Mapping((spatial_axis, spatial_axis, spectral_axis))

        # Sometimes the slit is perpendicular to the RA or Dec axis.
        # For example, if the slit is perpendicular to RA, that means
        # the slope of pix_to_ra will be nearly zero, so make sure
        # mapping.inverse uses pix_to_dec.inverse.  The auto definition
        # of mapping.inverse is to use the 2nd spatial coordinate, i.e. Dec.
        if np.isclose(pix_to_dec.slope, 0, atol=1e-8):
            mapping_tuple = (0, 1)
            # Account for vertical or horizontal dispersion on detector
            if spatial_axis:
                mapping.inverse = Mapping(mapping_tuple[::-1])
            else:
                mapping.inverse = Mapping(mapping_tuple)

        # The final transform
        transform = mapping | pix_to_ra & pix_to_dec & pix_to_wavelength

        det = cf.Frame2D(name='detector', axes_order=(0, 1))
        sky = cf.CelestialFrame(name='sky',
                                axes_order=(0, 1),
                                reference_frame=coord.ICRS())
        spec = cf.SpectralFrame(name='spectral',
                                axes_order=(2, ),
                                unit=(u.micron, ),
                                axes_names=('wavelength', ))
        world = cf.CompositeFrame([sky, spec], name='world')

        pipeline = [(det, transform), (world, None)]

        output_wcs = WCS(pipeline)

        # compute the output array size in WCS axes order, i.e. (x, y)
        output_array_size = [0, 0]
        output_array_size[spectral_axis] = len(wavelength_array)
        output_array_size[spatial_axis] = len(ra_array)

        # turn the size into a numpy shape in (y, x) order
        self.data_size = tuple(output_array_size[::-1])

        bounding_box = resample_utils.wcs_bbox_from_shape(self.data_size)
        output_wcs.bounding_box = bounding_box

        return output_wcs
Ejemplo n.º 18
0
    def build_interpolated_output_wcs(self, refmodel=None):
        """
        Create a spatial/spectral WCS output frame using all the input models

        Creates output frame by linearly fitting RA, Dec along the slit and
        producing a lookup table to interpolate wavelengths in the dispersion
        direction.

        Parameters
        ----------
        refmodel : `~jwst.datamodels.DataModel`
            The reference input image from which the fiducial WCS is created.
            If not specified, the first image in self.input_models is used.

        Returns
        -------
        output_wcs : `~gwcs.WCS` object
            A gwcs WCS object defining the output frame WCS
        """

        # for each input model convert slit x,y to ra,dec,lam
        # use first input model to set spatial scale
        # use center of appended ra and dec arrays to set up
        # center of final ra,dec
        # append all ra,dec, wavelength array for each slit
        # use first model to initialize wavelenth array
        # append wavelengths that fall outside the endpoint of
        # of wavelength array when looping over additional data

        all_wavelength = []
        all_ra_slit = []
        all_dec_slit = []

        for im, model in enumerate(self.input_models):
            wcs = model.meta.wcs
            bb = wcs.bounding_box
            grid = wcstools.grid_from_bounding_box(bb)
            ra, dec, lam = np.array(wcs(*grid))
            spectral_axis = find_dispersion_axis(model)
            spatial_axis = spectral_axis ^ 1

            # Compute the wavelength array, trimming NaNs from the ends
            # In many cases, a whole slice is NaNs, so ignore those warnings
            warnings.simplefilter("ignore")
            wavelength_array = np.nanmedian(lam, axis=spectral_axis)
            warnings.resetwarnings()
            wavelength_array = wavelength_array[~np.isnan(wavelength_array)]

            # We need to estimate the spatial sampling to use for the output WCS.
            # Tt is assumed the spatial sampling is the same for all the input
            # models. So we can use the first input model to set the spatial
            # sampling.

            # Steps to do this for first input model:
            # 1. find the middle of the spectrum in wavelength
            # 2. Pull out the ra and dec at the center of the slit.
            # 3. Find the mean ra,dec and the center of the slit this will
            #    represent the tangent point
            # 4. Convert ra,dec -> tangent plane projection: x_tan,y_tan
            # 5. using x_tan, y_tan perform a linear fit to find spatial sampling
            # first input model sets intializes wavelength array and defines
            # the spatial scale of the output wcs
            if im == 0:
                for iw in wavelength_array:
                    all_wavelength.append(iw)

                lam_center_index = int(
                    (bb[spectral_axis][1] - bb[spectral_axis][0]) / 2)
                if spatial_axis == 0:
                    ra_center = ra[lam_center_index, :]
                    dec_center = dec[lam_center_index, :]
                else:
                    ra_center = ra[:, lam_center_index]
                    dec_center = dec[:, lam_center_index]
                # find the ra and dec for this slit using center of slit
                ra_center_pt = np.nanmean(ra_center)
                dec_center_pt = np.nanmean(dec_center)

                if resample_utils.is_sky_like(model.meta.wcs.output_frame):
                    # convert ra and dec to tangent projection
                    tan = Pix2Sky_TAN()
                    native2celestial = RotateNative2Celestial(
                        ra_center_pt, dec_center_pt, 180)
                    undist2sky1 = tan | native2celestial
                    # Filter out RuntimeWarnings due to computed NaNs in the WCS
                    warnings.simplefilter("ignore")
                    # at this center of slit find x,y tangent projection - x_tan, y_tan
                    x_tan, y_tan = undist2sky1.inverse(ra, dec)
                    warnings.resetwarnings()
                else:
                    # for non sky-like output frames, no need to do tangent plane projections
                    # but we still use the same variables
                    x_tan, y_tan = ra, dec

                # pull out data from center
                if spectral_axis == 0:
                    x_tan_array = x_tan.T[lam_center_index]
                    y_tan_array = y_tan.T[lam_center_index]
                else:
                    x_tan_array = x_tan[lam_center_index]
                    y_tan_array = y_tan[lam_center_index]

                x_tan_array = x_tan_array[~np.isnan(x_tan_array)]
                y_tan_array = y_tan_array[~np.isnan(y_tan_array)]

                # estimate the spatial sampling
                fitter = LinearLSQFitter()
                fit_model = Linear1D()
                xstop = x_tan_array.shape[0] / self.pscale_ratio
                xstep = 1 / self.pscale_ratio
                ystop = y_tan_array.shape[0] / self.pscale_ratio
                ystep = 1 / self.pscale_ratio
                pix_to_xtan = fitter(fit_model, np.arange(0, xstop, xstep),
                                     x_tan_array)
                pix_to_ytan = fitter(fit_model, np.arange(0, ystop, ystep),
                                     y_tan_array)

            # append all ra and dec values to use later to find min and max
            # ra and dec
            ra_use = ra.flatten()
            ra_use = ra_use[~np.isnan(ra_use)]
            dec_use = dec.flatten()
            dec_use = dec_use[~np.isnan(dec_use)]
            all_ra_slit.append(ra_use)
            all_dec_slit.append(dec_use)

            # now check wavelength array to see if we need to add to it
            this_minw = np.min(wavelength_array)
            this_maxw = np.max(wavelength_array)
            all_minw = np.min(all_wavelength)
            all_maxw = np.max(all_wavelength)

            if this_minw < all_minw:
                addpts = wavelength_array[wavelength_array < all_minw]
                for ip in range(len(addpts)):
                    all_wavelength.append(addpts[ip])
            if this_maxw > all_maxw:
                addpts = wavelength_array[wavelength_array > all_maxw]
                for ip in range(len(addpts)):
                    all_wavelength.append(addpts[ip])

        # done looping over set of models

        all_ra = np.hstack(all_ra_slit)
        all_dec = np.hstack(all_dec_slit)
        all_wave = np.hstack(all_wavelength)
        all_wave = all_wave[~np.isnan(all_wave)]
        all_wave = np.sort(all_wave, axis=None)
        # Tabular interpolation model, pixels -> lambda
        wavelength_array = np.unique(all_wave)
        # Check if the data is MIRI LRS FIXED Slit. If it is then
        # the wavelength array needs to be flipped so that the resampled
        # dispersion direction matches the disperion direction on the detector.
        if self.input_models[0].meta.exposure.type == 'MIR_LRS-FIXEDSLIT':
            wavelength_array = np.flip(wavelength_array, axis=None)

        step = 1 / self.pscale_ratio
        stop = wavelength_array.shape[0] / self.pscale_ratio
        points = np.arange(0, stop, step)
        pix_to_wavelength = Tabular1D(points=points,
                                      lookup_table=wavelength_array,
                                      bounds_error=False,
                                      fill_value=None,
                                      name='pix2wavelength')

        # Tabular models need an inverse explicitly defined.
        # If the wavelength array is decending instead of ascending, both
        # points and lookup_table need to be reversed in the inverse transform
        # for scipy.interpolate to work properly
        points = wavelength_array
        lookup_table = np.arange(0, stop, step)

        if not np.all(np.diff(wavelength_array) > 0):
            points = points[::-1]
            lookup_table = lookup_table[::-1]
        pix_to_wavelength.inverse = Tabular1D(points=points,
                                              lookup_table=lookup_table,
                                              bounds_error=False,
                                              fill_value=None,
                                              name='wavelength2pix')

        # For the input mapping, duplicate the spatial coordinate
        mapping = Mapping((spatial_axis, spatial_axis, spectral_axis))

        # Sometimes the slit is perpendicular to the RA or Dec axis.
        # For example, if the slit is perpendicular to RA, that means
        # the slope of pix_to_xtan will be nearly zero, so make sure
        # mapping.inverse uses pix_to_ytan.inverse.  The auto definition
        # of mapping.inverse is to use the 2nd spatial coordinate, i.e. Dec.

        if np.isclose(pix_to_ytan.slope, 0, atol=1e-8):
            mapping_tuple = (0, 1)
            # Account for vertical or horizontal dispersion on detector
            if spatial_axis:
                mapping.inverse = Mapping(mapping_tuple[::-1])
            else:
                mapping.inverse = Mapping(mapping_tuple)

        # The final transform
        # redefine the ra, dec center tangent point to include all data

        # check if all_ra crosses 0 degress - this makes it hard to
        # define the min and max ra correctly
        all_ra = wrap_ra(all_ra)
        ra_min = np.amin(all_ra)
        ra_max = np.amax(all_ra)

        ra_center_final = (ra_max + ra_min) / 2.0
        dec_min = np.amin(all_dec)
        dec_max = np.amax(all_dec)
        dec_center_final = (dec_max + dec_min) / 2.0
        tan = Pix2Sky_TAN()
        if len(self.input_models
               ) == 1:  # single model use ra_center_pt to be consistent
            # with how resample was done before
            ra_center_final = ra_center_pt
            dec_center_final = dec_center_pt

        if resample_utils.is_sky_like(model.meta.wcs.output_frame):
            native2celestial = RotateNative2Celestial(ra_center_final,
                                                      dec_center_final, 180)
            undist2sky = tan | native2celestial
            # find the spatial size of the output - same in x,y
            x_tan_all, _ = undist2sky.inverse(all_ra, all_dec)
        else:
            x_tan_all, _ = all_ra, all_dec
        x_min = np.amin(x_tan_all)
        x_max = np.amax(x_tan_all)
        x_size = int(np.ceil((x_max - x_min) / np.absolute(pix_to_xtan.slope)))

        # single model use size of x_tan_array
        # to be consistent with method before
        if len(self.input_models) == 1:
            x_size = len(x_tan_array)

        # define the output wcs
        if resample_utils.is_sky_like(model.meta.wcs.output_frame):
            transform = mapping | (pix_to_xtan & pix_to_ytan
                                   | undist2sky) & pix_to_wavelength
        else:
            transform = mapping | (pix_to_xtan
                                   & pix_to_ytan) & pix_to_wavelength

        det = cf.Frame2D(name='detector', axes_order=(0, 1))
        if resample_utils.is_sky_like(model.meta.wcs.output_frame):
            sky = cf.CelestialFrame(name='sky',
                                    axes_order=(0, 1),
                                    reference_frame=coord.ICRS())
        else:
            sky = cf.Frame2D(
                name=f'resampled_{model.meta.wcs.output_frame.name}',
                axes_order=(0, 1))
        spec = cf.SpectralFrame(name='spectral',
                                axes_order=(2, ),
                                unit=(u.micron, ),
                                axes_names=('wavelength', ))
        world = cf.CompositeFrame([sky, spec], name='world')

        pipeline = [(det, transform), (world, None)]

        output_wcs = WCS(pipeline)

        # import ipdb; ipdb.set_trace()

        # compute the output array size in WCS axes order, i.e. (x, y)
        output_array_size = [0, 0]
        output_array_size[spectral_axis] = int(
            np.ceil(len(wavelength_array) / self.pscale_ratio))
        output_array_size[spatial_axis] = int(
            np.ceil(x_size / self.pscale_ratio))
        # turn the size into a numpy shape in (y, x) order
        self.data_size = tuple(output_array_size[::-1])
        bounding_box = resample_utils.wcs_bbox_from_shape(self.data_size)
        output_wcs.bounding_box = bounding_box

        return output_wcs
    # plot the model
    mod_x = np.linspace(0.0, 65000.0, num=100)
    ax[3].plot(mod_x, mod(mod_x) / lincormod(mod_x), label="full/linear")

    ints = range(nints)

    # apply the correction

    line_init = (Exponential1D(
        x_0=-2.0,
        amplitude=-500.,
        bounds={
            "amplitude": [-100000.0, -100.0],
            "x_0": [-4.0, 0.0]
        },
    ) + Linear1D())
    fit_line = LevMarLSQFitter()
    mult_comp = True

    line_init = Linear1D()
    fit_line = LinearLSQFitter()
    mult_comp = False

    intslopes = np.zeros((nints))
    linfit_metric = np.zeros((nints))
    intexpamp = np.zeros((nints))
    for k in range(nints):
        gnum, ydata = get_ramp(hdu[0],
                               pix_x,
                               pix_y,
                               k,