示例#1
0
    def __init__(self, x: Quantity, y: Quantity, z: Quantity, r: Quantity):
        if r <= 0:
            raise ValueError("r has to be greater than 0 to define a sphere")

        self.center = np.array(
            (x.to('km').value, y.to('km').value, z.to('km').value)) * km
        self.radius = r.to('km')
示例#2
0
def simple_xmm_match(
    src_ra: float,
    src_dec: float,
    distance: Quantity = Quantity(30.0, 'arcmin')) -> DataFrame:
    """
    Returns ObsIDs within a given distance from the input ra and dec values.

    :param float src_ra: RA coordinate of the source, in degrees.
    :param float src_dec: DEC coordinate of the source, in degrees.
    :param Quantity distance: The distance to search for XMM observations within, default should be
        able to match a source on the edge of an observation to the centre of the observation.
    :return: The ObsID, RA_PNT, and DEC_PNT of matching XMM observations.
    :rtype: DataFrame
    """
    rad = distance.to('deg').value
    local_census = CENSUS.copy()
    local_census["dist"] = np.sqrt((local_census["RA_PNT"] - src_ra)**2 +
                                   (local_census["DEC_PNT"] - src_dec)**2)
    matches = local_census[local_census["dist"] <= rad]
    matches = matches[~matches["ObsID"].isin(BLACKLIST["ObsID"])]
    if len(matches) == 0:
        raise NoMatchFoundError(
            "No XMM observation found within {a} of ra={r} "
            "dec={d}".format(r=round(src_ra, 4),
                             d=round(src_dec, 4),
                             a=distance))
    return matches
示例#3
0
class RateAngle(object):
    """
    Compute the RA/DEC of the centre of cutout based on having been provided a rate/angle an RA/DEC starting location.
    """
    def __init__(self, rate, angle, ra0=None, dec0=None, t0=None):
        self.rate = rate
        self.angle = angle
        self.t0 = t0
        self.ra0 = Quantity(ra0)
        self.dec0 = Quantity(dec0)
        self.coordinate = None
        print("Combining frame at {} along angle {}".format(
            self.rate, self.angle))
        r = self.rate.to('arcsec/hour').value
        a = self.angle.to('degree').value
        self.name = "R{:06.1f}A{:05.1f}".format(r, a)

        logging.debug(
            "Computing for rate: {} and angle: {} centred at {} {}".format(
                self.rate, self.angle, self.ra0, self.dec0))

    def predict(self, observation_time, **kwargs):
        """

        :param observation_time: Date to predict location of object, as Time or str
        :param kwargs: These are extra arguments that mp_ephem.BKOrbit.predict might have, that this doesn't
        :return: None
        """
        if len(kwargs) > 0:
            logging.debug("RateAngle predict ignores: {}".format(kwargs))
        if self.t0 is None:
            self.t0 = Time(observation_time)
        dr = self.rate * (Time(observation_time) - self.t0)
        logging.debug("Amount of motion: {}".format(dr.to('arcsec')))
        dra = dr * np.cos(self.angle.to('radian')) / np.cos(
            self.dec0.to('radian'))
        ddec = dr * np.sin(self.angle.to('radian'))
        logging.debug("dRA:{}, dDE:{}".format(dra, ddec))
        self.coordinate = SkyCoord(self.ra0 - dra.to('degree'),
                                   self.dec0 + ddec.to('degree'))
示例#4
0
def _calculate_orbital_wavelength_variation(data_array,
                                            date_data_created,
                                            slit_pixel_range=None,
                                            spline_smoothing=False,
                                            fit_individual_profiles=False,
                                            spacecraft_velocity=None,
                                            orbital_phase=None,
                                            roll_angle=None):
    """Calculates orbital corrections of spectral line positions using level 2 files.

    For data generated from the April 2014 pipeline, thermal and spacecraft velocity components
    have both been subtracted in the level 2 files.  Therefore, this routine calculates the
    residual orbital (thermal) variation.  For data generated from the Oct 2013 pipeline,
    this routine calculates the total of thermal and spacecraft velocity components.

    Parameters
    ----------
    data_array: `xarray.DataArray`
        IRIS spectrograph data from spectral window Mg II k 2796 as generated by
        `sunpy.spectra.sources.IRISRaster.`
    date_data_created: `datetime.datetime`
        Date the data was created by IRIS pipeline.  Used to determine where spacecraft
        velocity etc. needs to be accounted for.
    spacecraft_velocity: `astropy.units.quantity.Quantity`
        Velocity of spacecraft at each exposure in data_array.
        Must be set if date_data_created < 1 April 2014.
    orbital_phase: `numpy.array`
        Orbital phase of spacecraft at each exposure in data_array.  Available from
        auxiliary data in IRIS spectrograph fits files.
        Must be set if date_data_created < 1 April 2014.
    roll_angle: `astropy.units.quantity.Quantity`
        Roll angle of spacecraft. Must be set if date_data_created < 1 April 2014.

    Returns
    -------
    orbital_wavelength_variation: `astropy.table.Table`
        Contains the following columns:
        time: `datetime.datetime` objects
            Observation times of wavelength variations.
        FUV: `astropy.quantity.Quantity`
            Wavelength variation in the FUV.
        NUV: `astropy.quantity.Quantity`
            Wavelength variation in the NUV.

    """
    # Define vacuum rest wavelength of Ni I 2799 line.
    wavelength_nii = 2799.474 * u.Angstrom
    # Define factor converting NUV spectral pixel size to Angstrom
    specsize = 0.0255
    # Define date of new pipeline.
    date_new_pipeline = datetime.datetime(2014, 4, 1)
    if date_data_created < date_new_pipeline:
        # Check that there are measurement times with good values of
        # spacecraft velocity and orbital phase.
        bad_aux = np.asarray(np.isfinite(spacecraft_velocity) *
                             np.isfinite(orbital_phase) * (-1),
                             dtype=bool)
    # Generate wavelength vector containing only Ni I line.
    wavelength_window = Quantity(data_array.coords["wavelength"].values,
                                 unit=data_array.attrs["units"]["wavelength"])
    wavelength_roi_index = np.arange(len(wavelength_window))[np.logical_and(
        wavelength_window >= 2799.3 * u.Angstrom,
        wavelength_window <= 2799.8 * u.Angstrom)]
    # Check that there are at least 5 points in wavelength region.
    # Must have at least this many for a gaussian fit.
    if len(wavelength_roi_index) < 5:
        wavelength_roi_index = np.arange(5) + wavelength_roi_index[0]
    # Extract wavelength of region around Ni I line as array in units
    # of Angstroms.
    wavelength_roi = wavelength_window.to(
        u.Angstrom).value[wavelength_roi_index]
    # Keep only data within wavelength region of interest.
    data_array = data_array.isel(
        spectral_axis=slice(wavelength_roi_index[0], wavelength_roi_index[-1] +
                            1))
    # If user selected a sub-region of the slit, reduce data to just
    # that region.
    if slit_pixel_range:
        if len(slit_pixel_range) == 2:
            data_array = data_array.isel(
                slit_axis, slice(slit_pixel_range[0], slit_pixel_range[1]))
        else:
            raise TypeError(
                "slit_pixel_range must be tuple of length 2 giving lower and "
                +
                "upper bounds of section of slit over which to average line fits."
            )

    # Derive residual orbital variation.
    # Define array to hold averaged position of Ni I line at different
    # times.
    mean_line_wavelengths = np.empty(len(data_array.time)) * np.nan
    # Define initial guess for gaussian model.
    g_init = _gaussian1d_on_linear_bg(amplitude=-2.,
                                      mean=wavelength_nii.value,
                                      standard_deviation=2.,
                                      constant_term=50.,
                                      linear_term=1.5)
    # Define fitting method.
    fit_g = fitting.LevMarLSQFitter()
    # Depending on user choice, either fit line as measured by each
    # pixel then average line position, or fit average line spectrum
    # from all slit pixels.
    if fit_individual_profiles:
        pixels_in_slit = len(raster.slit_axis)
        for k in range(len(raster.time)):
            pixel_line_wavelengths = np.empty(pixels_in_slit) * np.nan
            data_single_time = raster.isel(raster_axis=k)
            # Iterate through each pixel along slit and perform fit to
            # Ni I line.
            for j in range(2, pixels_in_slit - 2):
                # Average over 5 pixels to improve signal-to-noise.
                intensity_mean_5pix = data_single_time.isel(
                    slit_axis=slice(j - 2, j + 3)).mean(axis=0)
                # Fit gaussian to Ni I line.
                g = fit_g(g_init, wavelength_roi, intensity_mean_5pix)
                # Check that fit is within physically reasonable
                # limits.  If so, store line center wavelength in
                # mean_line_wavelengths array. Else leave element as
                # defined, i.e. NaN.
                if np.isfinite(g.amplitude) and g.amplitude < 0. and \
                            wavelength_roi[0] < g.mean < wavelength_roi[-1]:
                    pixel_line_wavelengths[j] = g.mean
            # Take average of Ni I line position from fits in each
            # pixel.
            mean_line_wavelengths[k] = np.nanmean(pixel_line_wavelengths)
    else:
        # Else average all line profiles then perform fit.
        # Iterate through each measurement time and fit a gaussian to
        # Ni I line.
        for k in range(len(raster.time)):
            # Get data averaged over slit.
            data_single_time = raster.isel(raster_axis=k)
            data_slit_averaged = data_single_time.to_masked_array().mean(
                axis=0).data
            # Fit Ni I line with a gaussian.
            # Perform fit.
            g = fit_g(g_init, wavelength_roi, data_slit_averaged)
            # Check that fit is within physically reasonable limits.
            # If so, store line center wavelength in
            # mean_line_wavelengths array. Else leave element as
            # defined, i.e. NaN.
            if np.isfinite(g.amplitude) and g.amplitude < 0. and \
                        wavelength_roi[0] < g.mean < wavelength_roi[-1]:
                mean_line_wavelengths[k] = g.mean
            # If data produced by old pipeline, subtract spacecraft velocity
            # from the line position.
            if date_created < date_new_pipeline:
                mean_line_wavelengths[k] = \
                    mean_line_wavelengths[k]-spacecraft_velocity[k]/3e8*wavelength_nii.to(u.Angstrom).value

    # Mark abnormal values.  Thermal drift is of the order of 2
    # unsummed wavelength pixels peak-to-peak.
    w_abnormal = np.where(
        np.abs(mean_line_wavelengths -
               np.nanmedian(mean_line_wavelengths)) >= specsize * 2)[0]
    if len(w_abnormal) > 0:
        mean_line_wavelengths[w_abnormal] = np.nan
    # Further data reduction required for files from old pipeline.
    if date_created < date_new_pipeline:
        dw_th_A = mean_line_wavelengths - np.nanmean(mean_line_wavelengths)
        # Change the unit from Angstrom into unsummed wavelength pixel.
        dw_th_p = dw_th_A / specsize
        # Adjust reference wavelength using orbital phase information.
        if not (np.isfinite(orbital_phase)).all():
            warnings.warn(
                "Orbital phase values are invalid.  Thermal drift may be offset by at most one pixel."
            )
            dw_th = dw_th
            # For absolute wavelength calibration of NUV, the
            # following amount (unit Angstrom) has to be
            # subtracted from the wavelengths.
            abswvl_nuv = np.nanmean(mean_line_wavelengths) - wavelength_nii.to(
                u.Angstrom).value
        else:
            # Define empirical sine fitting at 0 roll angle shifted by
            # different phase.
            sine_params = [
                -0.66615146, -1.0, 53.106583 - roll_angle / 360. * 2 * np.pi
            ]
            phase_adj = np.nanmean(
                sine_params[0] *
                np.sin(sine_params[1] * orbital_phase + sine_params[2]))
            # thermal component of the orbital variation, in the unit of unsummed wavelength pixel
            dw_th = dw_th_p + phase_adj
            # For absolute wavelength calibration of NUV the following
            # amount (unit Angstrom) has to be subtracted from the
            # wavelengths.
            abswvl_nuv = np.nanmean(mean_line_wavelengths) - wavelength_nii.to(
                u.Angstrom).value - phase_adj * specsize
    else:
        # Calculate relative variation of the line position.
        dw_th = mean_line_wavelengths - np.nanmean(mean_line_wavelengths)

    # If spline_smoothing=True, perform spline fit a smoothing to
    # eliminate the 5 minute photospheric oscillation.
    if spline_smoothing:
        # Define spacing of spline knots in seconds.
        spline_knot_spacing = 300.
        # Create array of time in seconds from first time and
        # calculate duration of fitting period.
        time_s = np.asarray(x.coords["time"] - x.coords["time"][0],
                            dtype=float) / 1e9
        duration = time_s[-1] - time_s[0]
        # Check whether there is enough good data for a spline fit.
        if duration < spline_knot_spacing:
            raise ValueError("Not enough data for spline fit.")
        # Check whether there is enough good data for a spline fit.
        wgood = np.isfinite(mean_line_wavelengths)
        ngood = float(sum(wgood))
        wbad = not (np.isfinite(mean_line_wavelengths))
        nbad = float(sum(wbad))
        if nbad / ngood > 0.25:
            raise ValuError("Not enough good data for spline fit.")
        # Smooth residual thermal variation curve to eliminate the
        # 5-min photospheric oscillation.
        # Determine number of smoothing point using 3 point
        # lagrangian derivative.
        deriv_time = np.array([(time_s[i + 1] - time_s[i - 1]) / 2.
                               for i in range(1,
                                              len(time_s) - 1)])
        deriv_time = np.insert(
            deriv_time, 0, (-3 * time_s[0] + 4 * time_s[1] - time_s[2]) / 2)
        deriv_time = np.insert(
            deriv_time, -1, (3 * time_s[-1] - 4 * time_s[-2] + time_s[-3]) / 2)
        n_smooth = int(spline_knot_spacing / deriv_time.mean())
        if n_smooth < len(wgood):
            dw_good = convolve(dw_th[good], Box1DKernel(n_smooth))
        else:
            dw_good = dw_th[good]
        time_good = time_s[good]
        # Fit spline.
        tck = interpolate.splrep(time_good, dw_good, s=0)
        dw_th = interpolate.splev(time_s, tck)

    # Derive residual orbital curves in FUV and NUV and store
    # in a table.
    times = [
        datetime.datetime.utcfromtimestamp(t / 1e9)
        for t in raster.coords["time"].values.tolist()
    ]
    # Depeding on which pipeline produced the files...
    if date_created < date_new_pipeline:
        dw_orb_fuv = dw_th * (-0.013) + spacecraft_velocity.to(
            u.km / u.s).value / (3.e5) * 1370. * u.Angstrom
        dw_orb_nuv = dw_th * 0.0255 + spacecraft_velocity.to(
            u.km / u.s).value / (3.e5) * 2800. * u.Angstrom
    else:
        dw_orb_fuv = dw_th * (-1) * u.Angstrom
        dw_orb_nuv = dw_th * u.Angstrom

    orbital_wavelength_variation = Table([times, dw_orb_fuv, dw_orb_nuv],
                                         names=("time",
                                                "wavelength variation FUV",
                                                "wavelength variation NUV"))
    return orbital_wavelength_variation
示例#5
0
    def __init__(self, x0: Quantity, y0: Quantity, z0: Quantity, x1: Quantity,
                 y1: Quantity, z1: Quantity):
        self.p1 = np.array(
            (x0.to('km').value, y0.to('km').value, z0.to('km').value),
            dtype=float) * km
        self.p2 = np.array(
            (x1.to('km').value, y0.to('km').value, z0.to('km').value),
            dtype=float) * km
        self.p3 = np.array(
            (x0.to('km').value, y1.to('km').value, z0.to('km').value),
            dtype=float) * km
        self.p4 = np.array(
            (x0.to('km').value, y0.to('km').value, z1.to('km').value),
            dtype=float) * km

        if (x0, y0, z0) == (x1, y1, z1):
            raise ValueError(
                "p0 is equal to p1, a Cuboid of zero volume is not supported.")