예제 #1
0
파일: summary.py 프로젝트: lgrcia/prose
    def __init__(self, obs, style="paper", template_name="summary.tex"):
        LatexTemplate.__init__(self, template_name, style=style)
        self.obs = obs

        datetimes = Time(self.obs.time, format='jd', scale='utc').to_datetime()
        min_datetime = datetimes.min()
        max_datetime = datetimes.max()

        obs_duration_hours = (max_datetime - min_datetime).seconds // 3600
        obs_duration_mins = ((max_datetime - min_datetime).seconds // 60) % 60

        self.obs_duration = f"{min_datetime.strftime('%H:%M')} - {max_datetime.strftime('%H:%M')} " \
            f"[{obs_duration_hours}h{obs_duration_mins if obs_duration_mins != 0 else ''}]"

        # TODO: adapt to use PSF model block here (se we don't use the plot_... method from Observation)

        #self.mean_fwhm = np.mean(self.obs.x.fwhm.values)
        #self.obs._compute_psf_model(star=self.obs.target)
        #self.mean_target_fwhm = np.mean([self.obs.stack.fwhmx, self.obs.stack.fwhmy])
        #self.optimal_aperture = np.mean(self.obs.apertures_radii[self.obs.aperture,:])

        self.obstable = [
            ["Time", self.obs_duration],
            ["RA - DEC", f"{self.obs.RA} {self.obs.DEC}"],
            ["Images", len(self.obs.time)],
            [
                "Mean std · fwhm (epsf)",
                f"{self.obs.mean_epsf / (2 * np.sqrt(2 * np.log(2))):.2f} · {self.obs.mean_epsf:.2f} pixels"
            ],
            [
                "Fwhm (target)",
                f"{self.obs.mean_target_psf:.2f} pixels · {(self.obs.mean_target_psf*self.obs.telescope.pixel_scale.to(u.arcsec)):.2f}"
            ],
            [
                "Optimum aperture",
                f"{self.obs.optimal_aperture:.2f} pixels · "
                f"{(self.obs.optimal_aperture*self.obs.telescope.pixel_scale.to(u.arcsec)):.2f}"
            ],
            ["Telescope", self.obs.telescope.name],
            ["Filter", self.obs.filter],
            ["Exposure", f"{np.mean(self.obs.exptime)} s"],
        ]

        self.description = f"{self.obs.night_date.strftime('%Y %m %d')} $\cdot$ {self.obs.telescope.name} $\cdot$ {self.obs.filter}"
        self._trend = None
        self._transit = None
        self.dpi = 100

        # Some paths
        # ----------
        self.header = "Observation report"
예제 #2
0
def deltac_vs_time_BH15_targets(target, t_obs_prop=None, dtmax=None):
    """
    target : 'ob120169', 'ob140613', 'ob150029', 'ob150211'
    """
    target_name = copy.deepcopy(target)
    target_name = target_name.replace('OB', 'ob')
    print(target_name)
    mod_fit, data = lu_2019_lens.get_data_and_fitter(tdir[target_name])
    mod_all = mod_fit.get_best_fit_modes_model(def_best = 'maxL')
    mod = mod_all[0]

    # Sample time
    tmax = np.max(np.append(data['t_phot1'], data['t_phot2'])) + 90.0
    if dtmax is not None:
        tmax += dtmax
    t_mod_ast = np.arange(data['t_ast'].min() - 180.0, tmax, 2)
    t_mod_pho = np.arange(data['t_phot1'].min(), tmax, 2)

    # Get the linear motion curves for the source (includes parallax)
    p_unlens_mod = mod.get_astrometry_unlensed(t_mod_ast)
    p_unlens_mod_at_ast = mod.get_astrometry_unlensed(data['t_ast'])

    # Get the lensed motion curves for the source
    p_lens_mod = mod.get_astrometry(t_mod_ast)
    p_lens_mod_at_ast = mod.get_astrometry(data['t_ast'])

    x = (data['xpos'] - p_unlens_mod_at_ast[:,0]) * -1e3
    xe = data['xpos_err'] * 1e3
    y = (data['ypos'] - p_unlens_mod_at_ast[:, 1]) * 1e3
    ye = data['ypos_err'] * 1e3

    r2 = x**2 + y**2
    r = np.sqrt(r2)

    xterm = (x * xe)**2/r2
    yterm = (y * ye)**2/r2
    re = np.sqrt(xterm + yterm)

    xmod = (p_lens_mod[:, 0] - p_unlens_mod[:, 0])*-1e3
    ymod = (p_lens_mod[:, 1] - p_unlens_mod[:, 1])*1e3

    # Convert to decimal dates
    t_ast_dec = Time(data['t_ast'], format='mjd', scale='utc')
    t_mod_ast_dec = Time(t_mod_ast, format='mjd', scale='utc')

    t_ast_dec.format='decimalyear'
    t_mod_ast_dec.format='decimalyear'

    t_ast_dec = t_ast_dec.value
    t_mod_ast_dec = t_mod_ast_dec.value
    
    if t_obs_prop is not None:
        # Turn the epoch YYYY-MM-DD into a decimal.
        t_obs_prop_dec = np.zeros(len(t_obs_prop))
        for idx, tt in enumerate(t_obs_prop):
            t_strp = dt.strptime(tt, '%Y-%m-%d')
            t_dec = dtUtil.toYearFraction(t_strp)
            t_obs_prop_dec[idx] = t_dec

    plt.figure(1)
    plt.clf()
    plt.errorbar(t_ast_dec, x, yerr=xe, fmt='k.', alpha=1, zorder = 1000, label='Data')
    plt.scatter(t_mod_ast_dec, xmod, s = 1)
    plt.title(target)
    plt.xlabel('Time (Year)')
    plt.ylabel('$\delta$RA (mas)')
    if t_obs_prop is not None:
        for obs in t_obs_prop_dec:
            plt.axvline(x = obs, color='red', ls=':')
        plt.axvline(x = 0, color='red', ls=':', label='Proposed')
        plt.xlim(t_mod_ast_dec.min() - 0.1, t_mod_ast_dec.max() + 0.1)
    plt.axhline(y=0, color='black', alpha=0.5)
    plt.legend(loc=1)
    plt.title(target)
    plt.savefig(target + '_deltac_RA_vs_time.png')
    plt.show()

    plt.figure(2)
    plt.clf()
    plt.errorbar(t_ast_dec, y, yerr=ye, fmt='k.', alpha=1, zorder = 1000, label='Data')
    plt.scatter(t_mod_ast_dec, ymod, s = 1)
    plt.title(target)
    plt.xlabel('Time (Year)')
    plt.ylabel('$\delta$Dec (mas)')
    if t_obs_prop is not None:
        for obs in t_obs_prop_dec:
            plt.axvline(x = obs, color='red', ls=':')
        plt.axvline(x = 0, color='red', ls=':', label='Proposed')
        plt.xlim(t_mod_ast_dec.min() - 0.1, t_mod_ast_dec.max() + 0.1)
    plt.axhline(y=0, color='black', alpha=0.5)
    plt.legend(loc=1)
    plt.title(target)
    plt.savefig(target + '_deltac_Dec_vs_time.png')
    plt.show()

    plt.figure(3)
    plt.clf()
    plt.errorbar(t_ast_dec, r, yerr=re, fmt='k.', alpha=1, zorder = 1000, label='Data')
    plt.scatter(t_mod_ast_dec, np.sqrt(xmod**2 + ymod**2), s = 1)
    plt.xlabel('Time (Year)')
    plt.ylabel('$\delta_c$ (mas)')
    if t_obs_prop is not None:
        for obs in t_obs_prop_dec:
            plt.axvline(x = obs, color='red', ls=':')
        plt.axvline(x = 0, color='red', ls=':', label='Proposed')
        plt.xlim(t_mod_ast_dec.min() - 0.1, t_mod_ast_dec.max() + 0.1)
    plt.gca().set_ylim(bottom=0)
    plt.axhline(y=0.15, color='gray', ls='--')
    plt.legend(loc=1)
    plt.title(target)
    plt.savefig(target + '_deltac_vs_time.png')
    plt.show()
예제 #3
0
def plot_airmass(targets,
                 observer,
                 time,
                 ax=None,
                 style_kwargs=None,
                 style_sheet=None,
                 brightness_shading=False,
                 altitude_yaxis=False,
                 min_airmass=1.0,
                 min_region=None,
                 max_airmass=3.0,
                 max_region=None):
    r"""
    Plots airmass as a function of time for a given target.

    If a `~matplotlib.axes.Axes` object already exists, an additional
    airmass plot will be "stacked" on it.  Otherwise, creates a new
    `~matplotlib.axes.Axes` object and plots airmass on top of that.

    When a scalar `~astropy.time.Time` object is passed in (e.g.,
    ``Time('2000-1-1')``), the resulting plot will use a 24-hour window
    centered on the time indicated, with airmass sampled at regular
    intervals throughout.
    However, the user can control the exact number and frequency of airmass
    calculations used by passing in a non-scalar `~astropy.time.Time`
    object. For instance, ``Time(['2000-1-1 23:00:00', '2000-1-1
    23:30:00'])`` will result in a plot with only two airmass measurements.

    For examples with plots, visit the astroplan Read the Docs
    documentation [1]_.

    Parameters
    ----------
    targets : list of `~astroplan.FixedTarget` objects
        The celestial bodies of interest.
        If a single object is passed it will be converted to a list.

    observer : `~astroplan.Observer`
        The person, telescope, observatory, etc. doing the observing.

    time : `~astropy.time.Time`
        If scalar (e.g., ``Time('2000-1-1')``), will result in plotting target
        airmasses once an hour over a 24-hour window.
        If non-scalar (e.g., ``Time(['2000-1-1'])``, ``[Time('2000-1-1')]``,
        ``Time(['2000-1-1', '2000-1-2'])``),
        will result in plotting data at the exact times specified.

    ax : `~matplotlib.axes.Axes` or None, optional.
        The `~matplotlib.axes.Axes` object to be drawn on.
        If None, uses the current ``Axes``.

    style_kwargs : dict or None, optional.
        A dictionary of keywords passed into `~matplotlib.pyplot.plot_date`
        to set plotting styles.

    style_sheet : dict or `None` (optional)
        matplotlib style sheet to use. To see available style sheets in
        astroplan, print *astroplan.plots.available_style_sheets*. Defaults
        to the light theme.

    brightness_shading : bool
        Shade background of plot to scale roughly with sky brightness. Dark
        shading signifies times when the sun is below the horizon. Default
        is `False`.

    altitude_yaxis : bool
        Add alternative y-axis on the right side of the figure with target
        altitude. Default is `False`.

    min_airmass : float
        Lower limit of y-axis airmass range in the plot. Default is ``1.0``.

    max_airmass : float
        Upper limit of y-axis airmass range in the plot. Default is ``3.0``.

    min_region : float
        If set, defines an interval between ``min_airmass`` and ``min_region``
        that will be shaded. Default is `None`.

    max_region : float
        If set, defines an interval between ``max_airmass`` and ``max_region``
        that will be shaded. Default is `None`.

    Returns
    -------
    ax : `~matplotlib.axes.Axes`
        An ``Axes`` object with added airmass vs. time plot.

    Notes
    -----
    y-axis is inverted and shows airmasses between 1.0 and 3.0 by default.
    If user wishes to change these, use ``ax.<set attribute>`` before drawing
    or saving plot:

    References
    ----------
    .. [1] astroplan plotting tutorial: 
    https://astroplan.readthedocs.io/en/latest/tutorials/plots.html#time-dependent-plots

    """
    # Import matplotlib, set style sheet
    if style_sheet is not None:
        _set_mpl_style_sheet(style_sheet)

    import matplotlib.pyplot as plt
    from matplotlib import dates

    # Set up plot axes and style if needed.
    if ax is None:
        ax = plt.gca()
    if style_kwargs is None:
        style_kwargs = {}
    style_kwargs = dict(style_kwargs)
    style_kwargs.setdefault('linestyle', '-')
    style_kwargs.setdefault('linewidth', 1.5)
    style_kwargs.setdefault('fmt', '-')

    # Populate time window if needed.
    time = Time(time)
    if time.isscalar:
        time = time + np.linspace(-12, 12, 100) * u.hour
    elif len(time) == 1:
        warnings.warn(
            'You used a Time array of length 1.  You probably meant '
            'to use a scalar. (Or maybe a list with length > 1?).',
            PlotWarning)

    if not isinstance(targets, Sequence):
        targets = [targets]

    for target in targets:
        # Calculate airmass
        airmass = observer.altaz(time, target).secz
        # Mask out nonsense airmasses
        masked_airmass = np.ma.array(airmass, mask=airmass < 1)

        # Some checks & info for labels.
        try:
            target_name = target.name
        except AttributeError:
            target_name = ''

        # Plot data
        ax.plot_date(time.plot_date,
                     masked_airmass,
                     label=target_name,
                     **style_kwargs)

    # Format the time axis
    if not np.all(masked_airmass.mask):
        date_formatter = dates.DateFormatter('%H:%M')
        ax.xaxis.set_major_formatter(date_formatter)
        plt.setp(ax.get_xticklabels(), rotation=30, ha='right')

    # Shade background during night time
    if brightness_shading:
        starttime = time[0]

        #Figure out how many days are in observation to do multiple shadings
        ndays = time.max() - time.min()
        if ndays.jd > 1:
            for i in range(int(ndays.jd)):
                start = (starttime + i * u.day).datetime
                twilights = [
                    (observer.sun_set_time(Time(start),
                                           which='next').datetime, 0.0),
                    (observer.twilight_evening_civil(Time(start),
                                                     which='next').datetime,
                     0.1),
                    (observer.twilight_evening_nautical(Time(start),
                                                        which='next').datetime,
                     0.2),
                    (observer.twilight_evening_astronomical(
                        Time(start), which='next').datetime, 0.3),
                    (observer.twilight_morning_astronomical(
                        Time(start), which='next').datetime, 0.4),
                    (observer.twilight_morning_nautical(Time(start),
                                                        which='next').datetime,
                     0.3),
                    (observer.twilight_morning_civil(Time(start),
                                                     which='next').datetime,
                     0.2),
                    (observer.sun_rise_time(Time(start),
                                            which='next').datetime, 0.1),
                ]

                twilights.sort(key=operator.itemgetter(0))
                for i, twi in enumerate(twilights[1:], 1):
                    ax.axvspan(twilights[i - 1][0],
                               twilights[i][0],
                               ymin=0,
                               ymax=1,
                               color='grey',
                               alpha=twi[1])
        # Calculate and order twilights and set plotting alpha for each
        else:
            start = starttime.datetime
            twilights = [
                (observer.sun_set_time(Time(start),
                                       which='next').datetime, 0.0),
                (observer.twilight_evening_civil(Time(start),
                                                 which='next').datetime, 0.1),
                (observer.twilight_evening_nautical(Time(start),
                                                    which='next').datetime,
                 0.2),
                (observer.twilight_evening_astronomical(Time(start),
                                                        which='next').datetime,
                 0.3),
                (observer.twilight_morning_astronomical(Time(start),
                                                        which='next').datetime,
                 0.4),
                (observer.twilight_morning_nautical(Time(start),
                                                    which='next').datetime,
                 0.3),
                (observer.twilight_morning_civil(Time(start),
                                                 which='next').datetime, 0.2),
                (observer.sun_rise_time(Time(start),
                                        which='next').datetime, 0.1),
            ]

            twilights.sort(key=operator.itemgetter(0))
            for i, twi in enumerate(twilights[1:], 1):
                ax.axvspan(twilights[i - 1][0],
                           twilights[i][0],
                           ymin=0,
                           ymax=1,
                           color='grey',
                           alpha=twi[1])

    # Invert y-axis and set limits.
    y_lim = ax.get_ylim()
    if y_lim[1] > y_lim[0]:
        ax.invert_yaxis()
    ax.set_ylim([max_airmass, min_airmass])

    # Draw lo/hi limit regions, if present
    ymax, ymin = ax.get_ylim()  # should be (hi_limit, lo_limit)

    if max_region is not None:
        ax.axhspan(ymax, max_region, facecolor='#F9EB4E', alpha=0.10)
    if min_region is not None:
        ax.axhspan(min_region, ymin, facecolor='#F9EB4E', alpha=0.10)

    # Set labels.
    ax.set_ylabel("Airmass")
    ax.set_xlabel("Time from {0} [UTC]".format(min(time).datetime.date()))

    if altitude_yaxis and not _has_twin(ax):
        altitude_ticks = np.array([90, 60, 50, 40, 30, 20])
        airmass_ticks = 1. / np.cos(np.radians(90 - altitude_ticks))

        ax2 = ax.twinx()
        ax2.invert_yaxis()
        ax2.set_yticks(airmass_ticks)
        ax2.set_yticklabels(altitude_ticks)
        ax2.set_ylim(ax.get_ylim())
        ax2.set_ylabel('Altitude [degrees]')

    # Redraw figure for interactive sessions.
    ax.figure.canvas.draw()

    # Output.
    return ax
예제 #4
0
                    labels=para_names,
                    truths=reals,
                    quantiles=[.16, .50, .84],
                    show_titles=True)
fig.savefig("Corner.png")

for k in range(4):
    plt.figure()
    for i in range(nwalkers):
        plt.plot(sampler.chain[i, :, k])
    plt.title(para_names[k])
    plt.axhline(reals[k], color='k', linewidth=2)
    plt.savefig('%s_walk.png' % para_names_file[k])

times_curve = Time(np.linspace(times.min().mjd,
                               times.max().mjd, 10000),
                   format='mjd')

eta_fit = orbfits.eta_orb(srce, times_curve, Ecc, a, T0, Pb, Om_peri_dot,
                          Om_peri, samples[:, 0].mean() * u.deg,
                          samples[:, 1].mean() * u.deg,
                          samples[:, 2].mean() * u.deg, dp,
                          samples[:, 3].mean() * u.kpc, f0, pm_ra, pm_dec)
eta_real = orbfits.eta_orb(srce, times_curve, Ecc, a, T0, Pb, Om_peri_dot,
                           Om_peri, Om_orb, Om_scr, inc, dp, ds, f0, pm_ra,
                           pm_dec)

plt.figure()
plt.plot_date(times.plot_date, eta_noisy, label='Data')
plt.plot_date(times_curve.plot_date, eta_real, '-', label='Real')
plt.plot_date(times_curve.plot_date, eta_fit, '-', label='Fit (MCMC)')
 rcParams['axes.linewidth']=2
 #convert mjd to years
 time_yr=Time(time,format='mjd').decimalyear
 #find new y intercept with these new units.
 def find_b_ra(x,b):
     return fit_ra[0]*10**6*365.25*x + b
 def find_b_de(x,b):
     return fit_de[0]*10**6*365.25*x + b
 b_ra,c=optimize.curve_fit(find_b_ra,time_yr,(ra-ra[-1])*10**6)
 b_de,c=optimize.curve_fit(find_b_de,time_yr,(dec-dec[-1])*10**6)
 
 fig, (ax1,ax2)=plt.subplots(2,sharex=True)
 
 ax1.errorbar(time_yr[0:-1],(ra[0:-1]-ra[-1])*10**6,yerr=e_ra[0:-1]*10**6,fmt='.',mfc='blue',ecolor="black",elinewidth=2,ms=15)
 
 x=np.array(range(int(time_yr.min()),int(time_yr.max())+2))
 ax1.plot(x,func(fit_ra[0]*10**6*365.25,b_ra,x),color="red",linewidth=2)
 ax1.plot([time_yr[-1]],[0],'o',markersize=15, mfc='none',mec='green',mew=2)
 ax1.errorbar([time_yr[-1]],[0],yerr=[e_ra[-1]*10**6],fmt='',ecolor="green",elinewidth=2,capsize=10,capthick=2)
 ax1.xaxis.set_major_locator(ticker.MaxNLocator(integer=True))
 xticks(fontsize='medium')
 yticks(fontsize='medium')
 ax1.set_title(ivs_i,fontsize=20)
 ax1.set_ylabel('$\Delta$ RA ($\mu$as)',fontsize=20)
 plt.setp(ax1.get_xticklabels(),visible=False)
 
 
 #locator_params(axis='x',nbins=4)
 ax2.errorbar(time_yr[0:-1],(dec[0:-1]-dec[-1])*10**6,yerr=e_dec[0:-1]*10**6,fmt='.',mfc='blue',ecolor="black",elinewidth=2,ms=15)
 x=np.array(range(int(time_yr.min()),int(time_yr.max())+2))
 ax2.plot(x,func(fit_de[0]*10**6*365.25,b_de,x),color="red",linewidth=2)
예제 #6
0
class TestArithmetic:
    """Arithmetic on Time objects, using both doubles."""
    kwargs = ({}, {'axis': None}, {'axis': 0}, {'axis': 1}, {'axis': 2})
    functions = ('min', 'max', 'sort')

    def setup(self):
        mjd = np.arange(50000, 50100, 10).reshape(2, 5, 1)
        frac = np.array([0.1, 0.1 + 1.e-15, 0.1 - 1.e-15, 0.9 + 2.e-16, 0.9])
        if use_masked_data:
            frac = np.ma.array(frac)
            frac[1] = np.ma.masked
        self.t0 = Time(mjd, frac, format='mjd', scale='utc')

        # Define arrays with same ordinal properties
        frac = np.array([1, 2, 0, 4, 3])
        if use_masked_data:
            frac = np.ma.array(frac)
            frac[1] = np.ma.masked
        self.t1 = Time(mjd + frac, format='mjd', scale='utc')
        self.jd = mjd + frac

    @pytest.mark.parametrize('kw, func', itertools.product(kwargs, functions))
    def test_argfuncs(self, kw, func, masked):
        """
        Test that ``np.argfunc(jd, **kw)`` is the same as ``t0.argfunc(**kw)``
        where ``jd`` is a similarly shaped array with the same ordinal properties
        but all integer values.  Also test the same for t1 which has the same
        integral values as jd.
        """
        t0v = getattr(self.t0, 'arg' + func)(**kw)
        t1v = getattr(self.t1, 'arg' + func)(**kw)
        jdv = getattr(np, 'arg' + func)(self.jd, **kw)

        if self.t0.masked and kw == {'axis': None} and func == 'sort':
            t0v = np.ma.array(t0v, mask=self.t0.mask.reshape(t0v.shape)[t0v])
            t1v = np.ma.array(t1v, mask=self.t1.mask.reshape(t1v.shape)[t1v])
            jdv = np.ma.array(jdv, mask=self.jd.mask.reshape(jdv.shape)[jdv])

        assert np.all(t0v == jdv)
        assert np.all(t1v == jdv)
        assert t0v.shape == jdv.shape
        assert t1v.shape == jdv.shape

    @pytest.mark.parametrize('kw, func', itertools.product(kwargs, functions))
    def test_funcs(self, kw, func, masked):
        """
        Test that ``np.func(jd, **kw)`` is the same as ``t1.func(**kw)`` where
        ``jd`` is a similarly shaped array and the same integral values.
        """
        t1v = getattr(self.t1, func)(**kw)
        jdv = getattr(np, func)(self.jd, **kw)
        assert np.all(t1v.value == jdv)
        assert t1v.shape == jdv.shape

    def test_argmin(self, masked):
        assert self.t0.argmin() == 2
        assert np.all(self.t0.argmin(axis=0) == 0)
        assert np.all(self.t0.argmin(axis=1) == 0)
        assert np.all(self.t0.argmin(axis=2) == 2)

    def test_argmax(self, masked):
        assert self.t0.argmax() == self.t0.size - 2
        if masked:
            # The 0 is where all entries are masked in that axis
            assert np.all(self.t0.argmax(axis=0) == [1, 0, 1, 1, 1])
            assert np.all(self.t0.argmax(axis=1) == [4, 0, 4, 4, 4])
        else:
            assert np.all(self.t0.argmax(axis=0) == 1)
            assert np.all(self.t0.argmax(axis=1) == 4)
        assert np.all(self.t0.argmax(axis=2) == 3)

    def test_argsort(self, masked):
        order = [2, 0, 4, 3, 1] if masked else [2, 0, 1, 4, 3]
        assert np.all(self.t0.argsort() == np.array(order))
        assert np.all(self.t0.argsort(axis=0) == np.arange(2).reshape(2, 1, 1))
        assert np.all(self.t0.argsort(axis=1) == np.arange(5).reshape(5, 1))
        assert np.all(self.t0.argsort(axis=2) == np.array(order))
        ravel = np.arange(50).reshape(-1, 5)[:, order].ravel()
        if masked:
            t0v = self.t0.argsort(axis=None)
            # Manually remove elements in ravel that correspond to masked
            # entries in self.t0.  This removes the 10 entries that are masked
            # which show up at the end of the list.
            mask = self.t0.mask.ravel()[ravel]
            ravel = ravel[~mask]
            assert np.all(t0v[:-10] == ravel)
        else:
            assert np.all(self.t0.argsort(axis=None) == ravel)

    @pytest.mark.parametrize('scale', Time.SCALES)
    def test_argsort_warning(self, masked, scale):
        if scale == 'utc':
            pytest.xfail()
        with warnings.catch_warnings(record=True) as wlist:
            Time([1, 2, 3], format='jd', scale=scale).argsort()
        assert len(wlist) == 0

    def test_min(self, masked):
        assert self.t0.min() == self.t0[0, 0, 2]
        assert np.all(self.t0.min(0) == self.t0[0])
        assert np.all(self.t0.min(1) == self.t0[:, 0])
        assert np.all(self.t0.min(2) == self.t0[:, :, 2])
        assert self.t0.min(0).shape == (5, 5)
        assert self.t0.min(0, keepdims=True).shape == (1, 5, 5)
        assert self.t0.min(1).shape == (2, 5)
        assert self.t0.min(1, keepdims=True).shape == (2, 1, 5)
        assert self.t0.min(2).shape == (2, 5)
        assert self.t0.min(2, keepdims=True).shape == (2, 5, 1)

    def test_max(self, masked):
        assert self.t0.max() == self.t0[-1, -1, -2]
        assert np.all(self.t0.max(0) == self.t0[1])
        assert np.all(self.t0.max(1) == self.t0[:, 4])
        assert np.all(self.t0.max(2) == self.t0[:, :, 3])
        assert self.t0.max(0).shape == (5, 5)
        assert self.t0.max(0, keepdims=True).shape == (1, 5, 5)

    def test_ptp(self, masked):
        assert self.t0.ptp() == self.t0.max() - self.t0.min()
        assert np.all(self.t0.ptp(0) == self.t0.max(0) - self.t0.min(0))
        assert self.t0.ptp(0).shape == (5, 5)
        assert self.t0.ptp(0, keepdims=True).shape == (1, 5, 5)

    def test_sort(self, masked):
        order = [2, 0, 4, 3, 1] if masked else [2, 0, 1, 4, 3]
        assert np.all(self.t0.sort() == self.t0[:, :, order])
        assert np.all(self.t0.sort(0) == self.t0)
        assert np.all(self.t0.sort(1) == self.t0)
        assert np.all(self.t0.sort(2) == self.t0[:, :, order])
        if not masked:
            assert np.all(self.t0.sort(None) == self.t0[:, :, order].ravel())
            # Bit superfluous, but good to check.
            assert np.all(self.t0.sort(-1)[:, :, 0] == self.t0.min(-1))
            assert np.all(self.t0.sort(-1)[:, :, -1] == self.t0.max(-1))
예제 #7
0
def maxdate(items):
    """Return the maximum date from a list of date strings in yyyy-mm-dd format."""
    time_list = Time(items, format="iso", in_subfmt="date", out_subfmt="date")
    return str(time_list.max())
예제 #8
0
def maxdatetime(items):
    """Return the maximum datetime from a list of datetime strings in ISO-8601 format."""
    time_list = Time(items, format="isot")
    return str(time_list.max())
예제 #9
0
for author_name, profile in tqdm(any_author_profiles.items()):

    # If they haven't published a first author paper in 3 years, and their total career span is less than 5 years,
    # then let's say dead.
    if len(profile) > 1 and (Time("2021-04-15") - Time(profile[-1][1])).value >= 3 * 365 \
    and (Time(profile[-1][1]) - Time(profile[0][1])).value <= (5 * 365):
        dead_author_names.append(author_name)

# We need to calculate summary statistics for every author so that we can test predictors.
Ns = (3, 5, 10)  # years
metrics = {}
for author_name, profile in tqdm(any_author_profiles.items()):
    times = Time([date for arxiv_id, date in profile])

    # career longevity
    t_s, t_e = (times.min(), times.max())
    metrics[author_name] = dict(longevity=(t_e - t_s).value)

    # Calculate number of papers within N years.
    for N in Ns:
        key = f"first_author_paper_within_{N}_years"

        metrics[author_name][key] = 0

        for arxiv_id, date in profile:
            first_author_name = unique_ify(
                records[arxiv_id]["all_authors"].split("; ")[0])
            if first_author_name == author_name and (
                (Time(date) - t_s).value / 365) <= N:
                metrics[author_name][key] += 1
예제 #10
0
def save_report(self, destination, fields, remove_temp=True):

    def draw_table(table, table_start, marg=5, table_cell=(20,4)):

        pdf.set_draw_color(200,200,200)

        for i, datum in enumerate(table):
            pdf.set_font("helvetica", size=6)
            pdf.set_fill_color(249,249,249)

            pdf.rect(table_start[0] + 5, table_start[1] + 1.2 + i*table_cell[1],
                    table_cell[0]*3, table_cell[1], "FD" if i%2 == 0 else "D")

            pdf.set_text_color(100,100,100)

            value = datum[1]
            if value is None:
                value = "--"
            else:
                value = str(value)

            pdf.text(
                table_start[0] + marg + 2,
                table_start[1] + marg + i*table_cell[1] - 1.2, datum[0])

            pdf.set_text_color(50,50,50)
            pdf.text(
                table_start[0] + marg + 2 + table_cell[0],
                table_start[1] + marg + i*table_cell[1] - 1.2, value)

    if path.isdir(destination):
        file_name = "{}_report.pdf".format(self.products_denominator)
    else:
        file_name = path.basename(destination.strip(".html").strip(".pdf"))

    temp_folder = path.join(path.dirname(destination), "temp")

    if path.isdir("temp"):
        shutil.rmtree(temp_folder)

    if os.path.exists(temp_folder):
        shutil.rmtree(temp_folder)

    os.mkdir(temp_folder)

    self.show_stars(10, view="reference")
    star_plot = path.join(temp_folder, "starplot.png")
    fig = plt.gcf()
    fig.patch.set_alpha(0)
    plt.savefig(star_plot)
    plt.close()
    
    plt.figure(figsize=(6,10))
    self.plot_raw_diff()
    lc_report_plot = path.join(temp_folder, "lcreport.png")
    fig = plt.gcf()
    fig.patch.set_alpha(0)
    plt.savefig(lc_report_plot)
    plt.close()
    
    plt.figure(figsize=(6,10))
    self.plot_systematics(fields=fields)
    syst_plot = path.join(temp_folder, "systplot.png")
    fig = plt.gcf()
    fig.patch.set_alpha(0)
    plt.savefig(syst_plot)
    plt.close()

    if self.comparison_stars is not None:
        plt.figure(figsize=(6,10))
        self.plot_comps_lcs()
        lc_comps_plot = path.join(temp_folder, "lccompreport.png")
        fig = plt.gcf()
        fig.patch.set_alpha(0)
        plt.savefig(lc_comps_plot)
        plt.close()
        
    plt.figure(figsize=(10,3.5))
    psf_p = self.plot_psf_fit()
    
    psf_fit = path.join(temp_folder, "psf_fit.png")
    plt.savefig(psf_fit, dpi=60)
    plt.close()
    theta = psf_p["theta"]
    std_x = psf_p["std_x"]
    std_y = psf_p["std_y"]

    marg_x = 10
    marg_y = 8

    pdf = prose_FPDF(orientation='L', unit='mm', format='A4')
    pdf.add_page()

    pdf.set_draw_color(200,200,200)

    pdf.set_font("helvetica", size=12)
    pdf.set_text_color(50,50,50)
    pdf.text(marg_x, 10, txt="{}".format(self.target["name"]))

    pdf.set_font("helvetica", size=6)
    pdf.set_text_color(74,144,255)
    pdf.text(marg_x, 17, txt="simbad")
    pdf.link(marg_x, 15, 8, 3, self.simbad)

    pdf.set_text_color(150,150,150)
    pdf.set_font("Helvetica", size=7)
    pdf.text(marg_x, 14, txt="{} · {} · {}".format(
        self.observation_date, self.telescope.name, self.filter))

    pdf.image(star_plot, x=78, y=17, h=93.5)
    pdf.image(lc_report_plot, x=172, y=17, h=95)
    pdf.image(syst_plot, x=227, y=17, h=95)

    if self.comparison_stars is not None:
        pdf.image(lc_comps_plot, x=227, y=110, h=95)
        
    datetimes = Time(self.jd, format='jd', scale='utc').to_datetime()
    min_datetime = datetimes.min()
    max_datetime = datetimes.max()

    obs_duration = "{} - {} [{}h{}]".format(
    min_datetime.strftime("%H:%M"), 
    max_datetime.strftime("%H:%M"), 
    (max_datetime-min_datetime).seconds//3600,
    ((max_datetime-min_datetime).seconds//60)%60)
    
    max_psf = np.max([std_x, std_y])
    min_psf = np.min([std_x, std_y])
    ellipticity = (max_psf**2 - min_psf**2)/max_psf**2

    draw_table([
        ["Time", obs_duration],
        ["RA - DEC", "{} - {}".format(*self.target["radec"])],
        ["images", len(self.time)],
        ["GAIA id", None],
        ["mean std · fwhm", "{:.2f} · {:.2f} pixels".format(np.mean(self.fwhm)/(2*np.sqrt(2*np.log(2))), np.mean(self.fwhm))],
        ["Telescope", self.telescope.name],
        ["Filter", self.filter],
        ["exposure", "{} s".format(np.mean(self.data.exptime))],
    ], (5, 20))

    draw_table([
        ["PSF std · fwhm (x)", "{:.2f} · {:.2f} pixels".format(std_x, 2*np.sqrt(2*np.log(2))*std_x)],
        ["PSF std · fwhm (y)", "{:.2f} · {:.2f} pixels".format(std_y, 2*np.sqrt(2*np.log(2))*std_y)],
        ["PSF ellipicity", "{:.2f}".format(ellipticity)],
    ], (5, 78))

    pdf.image(psf_fit, x=5.5, y=55, w=65)

    pdf_path = path.join(destination, "{}.pdf".format(file_name.strip(".html").strip(".pdf")))
    pdf.output(pdf_path)

    if path.isdir("temp") and remove_temp:
        shutil.rmtree(temp_folder)

    print("report saved at {}".format(pdf_path))
예제 #11
0
class TestArithmetic():
    """Arithmetic on Time objects, using both doubles."""
    kwargs = ({}, {'axis': None}, {'axis': 0}, {'axis': 1}, {'axis': 2})
    functions = ('min', 'max', 'sort')

    def setup(self):
        mjd = np.arange(50000, 50100, 10).reshape(2, 5, 1)
        frac = np.array([0.1, 0.1+1.e-15, 0.1-1.e-15, 0.9+2.e-16, 0.9])
        if use_masked_data:
            frac = np.ma.array(frac)
            frac[1] = np.ma.masked
        self.t0 = Time(mjd, frac, format='mjd', scale='utc')

        # Define arrays with same ordinal properties
        frac = np.array([1, 2, 0, 4, 3])
        if use_masked_data:
            frac = np.ma.array(frac)
            frac[1] = np.ma.masked
        self.t1 = Time(mjd + frac, format='mjd', scale='utc')
        self.jd = mjd + frac

    @pytest.mark.parametrize('kw, func', itertools.product(kwargs, functions))
    def test_argfuncs(self, kw, func, masked):
        """
        Test that np.argfunc(jd, **kw) is the same as t0.argfunc(**kw) where
        jd is a similarly shaped array with the same ordinal properties but
        all integer values.  Also test the same for t1 which has the same
        integral values as jd.
        """
        t0v = getattr(self.t0, 'arg' + func)(**kw)
        t1v = getattr(self.t1, 'arg' + func)(**kw)
        jdv = getattr(np, 'arg' + func)(self.jd, **kw)

        if self.t0.masked and kw == {'axis': None} and func == 'sort':
            t0v = np.ma.array(t0v, mask=self.t0.mask.reshape(t0v.shape)[t0v])
            t1v = np.ma.array(t1v, mask=self.t1.mask.reshape(t1v.shape)[t1v])
            jdv = np.ma.array(jdv, mask=self.jd.mask.reshape(jdv.shape)[jdv])

        assert np.all(t0v == jdv)
        assert np.all(t1v == jdv)
        assert t0v.shape == jdv.shape
        assert t1v.shape == jdv.shape

    @pytest.mark.parametrize('kw, func', itertools.product(kwargs, functions))
    def test_funcs(self, kw, func, masked):
        """
        Test that np.func(jd, **kw) is the same as t1.func(**kw) where
        jd is a similarly shaped array and the same integral values.
        """
        t1v = getattr(self.t1, func)(**kw)
        jdv = getattr(np, func)(self.jd, **kw)
        assert np.all(t1v.value == jdv)
        assert t1v.shape == jdv.shape

    def test_argmin(self, masked):
        assert self.t0.argmin() == 2
        assert np.all(self.t0.argmin(axis=0) == 0)
        assert np.all(self.t0.argmin(axis=1) == 0)
        assert np.all(self.t0.argmin(axis=2) == 2)

    def test_argmax(self, masked):
        assert self.t0.argmax() == self.t0.size - 2
        if masked:
            # The 0 is where all entries are masked in that axis
            assert np.all(self.t0.argmax(axis=0) == [1, 0, 1, 1, 1])
            assert np.all(self.t0.argmax(axis=1) == [4, 0, 4, 4, 4])
        else:
            assert np.all(self.t0.argmax(axis=0) == 1)
            assert np.all(self.t0.argmax(axis=1) == 4)
        assert np.all(self.t0.argmax(axis=2) == 3)

    def test_argsort(self, masked):
        order = [2, 0, 4, 3, 1] if masked else [2, 0, 1, 4, 3]
        assert np.all(self.t0.argsort() == np.array(order))
        assert np.all(self.t0.argsort(axis=0) == np.arange(2).reshape(2, 1, 1))
        assert np.all(self.t0.argsort(axis=1) == np.arange(5).reshape(5, 1))
        assert np.all(self.t0.argsort(axis=2) == np.array(order))
        ravel = np.arange(50).reshape(-1, 5)[:, order].ravel()
        if masked:
            t0v = self.t0.argsort(axis=None)
            # Manually remove elements in ravel that correspond to masked
            # entries in self.t0.  This removes the 10 entries that are masked
            # which show up at the end of the list.
            mask = self.t0.mask.ravel()[ravel]
            ravel = ravel[~mask]
            assert np.all(t0v[:-10] == ravel)
        else:
            assert np.all(self.t0.argsort(axis=None) == ravel)

    def test_min(self, masked):
        assert self.t0.min() == self.t0[0, 0, 2]
        assert np.all(self.t0.min(0) == self.t0[0])
        assert np.all(self.t0.min(1) == self.t0[:, 0])
        assert np.all(self.t0.min(2) == self.t0[:, :, 2])
        assert self.t0.min(0).shape == (5, 5)
        assert self.t0.min(0, keepdims=True).shape == (1, 5, 5)
        assert self.t0.min(1).shape == (2, 5)
        assert self.t0.min(1, keepdims=True).shape == (2, 1, 5)
        assert self.t0.min(2).shape == (2, 5)
        assert self.t0.min(2, keepdims=True).shape == (2, 5, 1)

    def test_max(self, masked):
        assert self.t0.max() == self.t0[-1, -1, -2]
        assert np.all(self.t0.max(0) == self.t0[1])
        assert np.all(self.t0.max(1) == self.t0[:, 4])
        assert np.all(self.t0.max(2) == self.t0[:, :, 3])
        assert self.t0.max(0).shape == (5, 5)
        assert self.t0.max(0, keepdims=True).shape == (1, 5, 5)

    def test_ptp(self, masked):
        assert self.t0.ptp() == self.t0.max() - self.t0.min()
        assert np.all(self.t0.ptp(0) == self.t0.max(0) - self.t0.min(0))
        assert self.t0.ptp(0).shape == (5, 5)
        assert self.t0.ptp(0, keepdims=True).shape == (1, 5, 5)

    def test_sort(self, masked):
        order = [2, 0, 4, 3, 1] if masked else [2, 0, 1, 4, 3]
        assert np.all(self.t0.sort() == self.t0[:, :, order])
        assert np.all(self.t0.sort(0) == self.t0)
        assert np.all(self.t0.sort(1) == self.t0)
        assert np.all(self.t0.sort(2) == self.t0[:, :, order])
        if not masked:
            assert np.all(self.t0.sort(None) ==
                          self.t0[:, :, order].ravel())
            # Bit superfluous, but good to check.
            assert np.all(self.t0.sort(-1)[:, :, 0] == self.t0.min(-1))
            assert np.all(self.t0.sort(-1)[:, :, -1] == self.t0.max(-1))
예제 #12
0
    def save_report(self, destination=None, remove_temp=True):

        if destination is None:
            destination = self.phot.folder

        def draw_table(table, table_start, marg=5, table_cell=(20, 4)):

            pdf.set_draw_color(200, 200, 200)

            for i, datum in enumerate(table):
                pdf.set_font("helvetica", size=6)
                pdf.set_fill_color(249, 249, 249)

                pdf.rect(table_start[0] + 5,
                         table_start[1] + 1.2 + i * table_cell[1],
                         table_cell[0] * 3, table_cell[1],
                         "FD" if i % 2 == 0 else "D")

                pdf.set_text_color(100, 100, 100)

                value = datum[1]
                if value is None:
                    value = "--"
                else:
                    value = str(value)

                pdf.text(table_start[0] + marg + 2,
                         table_start[1] + marg + i * table_cell[1] - 1.2,
                         datum[0])

                pdf.set_text_color(50, 50, 50)
                pdf.text(table_start[0] + marg + 2 + table_cell[0] * 1.2,
                         table_start[1] + marg + i * table_cell[1] - 1.2,
                         value)

        if path.isdir(destination):
            file_name = "{}_NEB_{}arcmin.pdf".format(
                self.phot.products_denominator, self.radius)
        else:
            file_name = path.basename(destination.strip(".html").strip(".pdf"))

        temp_folder = path.join(path.dirname(destination), "temp")

        if path.isdir("temp"):
            shutil.rmtree(temp_folder)

        if os.path.exists(temp_folder):
            shutil.rmtree(temp_folder)

        os.mkdir(temp_folder)

        star_plot = path.join(temp_folder, "starplot.png")
        self.show(10)
        fig = plt.gcf()
        fig.patch.set_alpha(0)
        plt.savefig(star_plot)
        plt.close()

        lcs = []
        a = np.arange(len(self.nearby_ids))
        if len(self.nearby_ids) > 30:
            split = [
                np.arange(0, 30),
                *np.array([a[i:i + 7 * 8] for i in range(30, len(a), 7 * 8)])
            ]
        else:
            split = [np.arange(0, len(self.nearby_ids))]

        for i, idxs in enumerate(split):
            lcs_path = path.join(temp_folder, "lcs{}.png".format(i))
            lcs.append(lcs_path)
            if i == 0:
                self.plot(np.arange(0, np.min([30, len(self.nearby_ids)])),
                          W=5)
            else:
                self.plot(idxs, W=8)
            viz.paper_style()
            fig = plt.gcf()
            fig.patch.set_alpha(0)
            plt.savefig(lcs_path)
            plt.close()

        lcs = np.array(lcs)

        plt.figure(figsize=(10, 3.5))
        psf_p = self.phot.plot_psf_fit(cmap="viridis", c="C0")

        psf_fit = path.join(temp_folder, "psf_fit.png")
        plt.savefig(psf_fit, dpi=60)
        plt.close()
        theta = psf_p["theta"]
        std_x = psf_p["std_x"]
        std_y = psf_p["std_y"]

        marg_x = 10
        marg_y = 8

        pdf = viz.prose_FPDF(orientation='L', unit='mm', format='A4')
        pdf.add_page()

        pdf.set_draw_color(200, 200, 200)

        pdf.set_font("helvetica", size=12)
        pdf.set_text_color(50, 50, 50)
        pdf.text(marg_x, 10, txt="{}".format(self.phot.target["name"]))

        pdf.set_font("helvetica", size=6)
        pdf.set_text_color(50, 50, 50)
        pdf.text(240, 15, txt="Nearby Eclipsing Binary diagnostic")

        pdf.set_font("helvetica", size=6)
        pdf.set_text_color(74, 144, 255)
        pdf.text(marg_x, 17, txt="simbad")
        pdf.link(marg_x, 15, 8, 3, self.phot.simbad)

        pdf.set_text_color(150, 150, 150)
        pdf.set_font("Helvetica", size=7)
        pdf.text(marg_x,
                 14,
                 txt="{} · {} · {}".format(self.phot.observation_date,
                                           self.phot.telescope.name,
                                           self.phot.filter))

        datetimes = Time(self.phot.jd, format='jd', scale='utc').to_datetime()
        min_datetime = datetimes.min()
        max_datetime = datetimes.max()

        obs_duration = "{} - {} [{}h{}]".format(
            min_datetime.strftime("%H:%M"), max_datetime.strftime("%H:%M"),
            (max_datetime - min_datetime).seconds // 3600,
            ((max_datetime - min_datetime).seconds // 60) % 60)

        max_psf = np.max([std_x, std_y])
        min_psf = np.min([std_x, std_y])
        ellipticity = (max_psf**2 - min_psf**2) / max_psf**2

        draw_table([
            ["Time", obs_duration],
            [
                "RA Dec", "{:.4f} {:.4f}".format(self.phot.skycoord.ra,
                                                 self.phot.skycoord.dec)
            ],
            ["images", len(self.time)],
            ["GAIA id", None],
            [
                "mean fwhm", "{:.2f} pixels ({:.2f}\")".format(
                    np.mean(self.phot.fwhm),
                    np.mean(self.phot.fwhm) * self.phot.telescope.pixel_scale)
            ],
            ["Telescope", self.phot.telescope.name],
            ["Filter", self.phot.filter],
            ["exposure", "{} s".format(np.mean(self.phot.data.exptime))],
        ], (5 + 12, 20 + 100))

        draw_table(
            [[
                "stack PSF fwhm (x)", "{:.2f} pixels ({:.2f}\")".format(
                    psf_p["fwhm_x"],
                    psf_p["fwhm_x"] * self.phot.telescope.pixel_scale)
            ],
             [
                 "stack PSF fwhm (y)", "{:.2f} pixels ({:.2f}\")".format(
                     psf_p["fwhm_y"],
                     psf_p["fwhm_y"] * self.phot.telescope.pixel_scale)
             ], ["stack PSF model", "Moffat2D"],
             ["stack PSF ellipicity", "{:.2f}".format(ellipticity)],
             [
                 "diff. flux std", "{:.3f} ppt (5 min bins)".format(
                     np.mean(
                         utils.binning(self.phot.time,
                                       self.phot.lc.flux,
                                       5 / (24 * 60),
                                       std=True)[2]) * 1e3)
             ]], (5 + 12, 78 + 100))

        pdf.image(psf_fit, x=5.5 + 12, y=55 + 100, w=65)
        pdf.image(star_plot, x=5, y=20, h=93.5)
        pdf.image(lcs[0], x=100, y=22, w=185)

        for lcs_path in lcs[1::]:
            pdf.add_page()
            pdf.image(lcs_path, x=5, y=22, w=280)

        pdf_path = path.join(
            destination,
            "{}.pdf".format(file_name.strip(".html").strip(".pdf")))
        pdf.output(pdf_path)

        if path.isdir("temp") and remove_temp:
            shutil.rmtree(temp_folder)

        print("report saved at {}".format(pdf_path))
예제 #13
0
파일: iod.py 프로젝트: carise/thor
def iod(observations,
        observation_selection_method="combinations",
        chi2_threshold=10**3,
        contamination_percentage=20.0,
        iterate=True,
        light_time=True,
        backend="THOR",
        backend_kwargs=None):
    """
    Run initial orbit determination on a set of observations believed to belong to a single
    object. 

    Parameters
    ----------
    observations : `~pandas.DataFrame`
        Data frame containing the observations of a possible linkage. 
    observation_selection_method : {'first+middle+last', 'thirds', 'combinations'}, optional
        Which method to use to select observations. 
        [Default = 'combinations']
    chi2_threshold : float, optional
        Minimum chi2 required for an initial orbit to be accepted. Note that chi2 here needs to be 
        interpreted carefully, residuals of order a few arcseconds easily contribute significantly 
        to the total chi2. 
    contamination_percentage : float, optional
        Maximum percent of observations that can flagged as outliers. 
    iterate : bool, optional
        Iterate the preliminary orbit solution using the state transition iterator. 
    light_time : bool, optional
        Correct preliminary orbit for light travel time.
    backend : {'THOR', 'PYOORB'}, optional
        Which backend to use for ephemeris generation.
    backend_kwargs : dict, optional
        Settings and additional parameters to pass to selected 
        backend.

    Returns
    -------
    orbit : `~pandas.DataFrame` (7)
        Preliminary orbit with epoch_mjd (in UTC) and the the cartesian state vector. Also
        has a column for number of observations after outliers have been removed, arc length
        of the remaining observations and the value of chi2 for the solution.
    orbit_members : `~pandas.DataFrame` (3)
        Data frame with two columns: orbit_id and observation IDs.
    outliers : `~numpy.ndarray`, None
        Observation IDs of potential outlier detections.
    """
    # Extract column names
    obs_id_col = "obs_id"
    time_col = "mjd_utc"
    ra_col = "RA_deg"
    dec_col = "Dec_deg"
    ra_err_col = "RA_sigma_deg"
    dec_err_col = "Dec_sigma_deg"
    obs_code_col = "observatory_code"
    obs_x_col = "obs_x"
    obs_y_col = "obs_y"
    obs_z_col = "obs_z"

    # Extract observation IDs, sky-plane positions, sky-plane position uncertainties, times of observation,
    # and the location of the observer at each time
    obs_ids_all = observations[obs_id_col].values
    coords_all = observations[[ra_col, dec_col]].values
    ra = observations[ra_col].values
    dec = observations[dec_col].values
    sigma_ra = observations[ra_err_col].values
    sigma_dec = observations[dec_err_col].values

    coords_obs_all = observations[[obs_x_col, obs_y_col, obs_z_col]].values
    times_all = observations[time_col].values
    times_all = Time(times_all, scale="utc", format="mjd")

    backend_kwargs = _backendHandler(backend, "ephemeris")

    if backend == "THOR":
        backend_kwargs["light_time"] = light_time

        observers = observations[[
            obs_code_col, time_col, obs_x_col, obs_y_col, obs_z_col
        ]]

    elif backend == "PYOORB":
        if light_time == False:
            err = (
                "PYOORB does not support turning light time correction off.")
            raise ValueError(err)

        observers = {}
        for code in observations[obs_code_col].unique():
            observers[code] = Time(observations[observations[obs_code_col] ==
                                                code][time_col].values,
                                   scale="utc",
                                   format="mjd")
    else:
        err = ("backend should be one of 'THOR' or 'PYOORB'")
        raise ValueError(err)

    chi2_sol = 1e10
    orbit_sol = None
    obs_ids_sol = None
    remaining_ids = None
    arc_length = None
    outliers = np.array([])
    converged = False
    num_obs = len(observations)
    num_outliers = int(num_obs * contamination_percentage / 100.)

    orbit = pd.DataFrame(columns=[
        "orbit_id", "epoch_mjd_utc", "obj_x", "obj_y", "obj_z", "obj_vx",
        "obj_vy", "obj_vz", "arc_length", "num_obs", "chi2"
    ])
    orbit_members = pd.DataFrame(columns=["orbit_id", "obs_id"])

    # Select observation IDs to use for IOD
    obs_ids = selectObservations(
        observations,
        method=observation_selection_method,
    )
    if len(obs_ids) == 0:
        return orbit, orbit_members, outliers

    j = 0
    while not converged:
        if j == len(obs_ids):
            break

        ids = obs_ids[j]
        mask = np.isin(obs_ids_all, ids)

        # Grab sky-plane positions of the selected observations, the heliocentric ecliptic position of the observer,
        # and the times at which the observations occur
        coords = coords_all[mask, :]
        coords_obs = coords_obs_all[mask, :]
        times = times_all[mask]

        # Run IOD
        orbits_iod = gaussIOD(coords,
                              times.utc.mjd,
                              coords_obs,
                              light_time=light_time,
                              iterate=iterate,
                              max_iter=100,
                              tol=1e-15)
        if len(orbits_iod) == 0:
            j += 1
            continue

        # Propagate initial orbit to all observation times
        ephemeris = generateEphemeris(orbits_iod[:, 1:],
                                      Time(orbits_iod[:, 0],
                                           scale="utc",
                                           format="mjd"),
                                      observers,
                                      backend=backend,
                                      backend_kwargs=backend_kwargs)
        ephemeris = ephemeris[[
            'orbit_id', 'mjd_utc', 'RA_deg', 'Dec_deg', 'obj_x', 'obj_y',
            'obj_z', 'obj_vx', 'obj_vy', 'obj_vz'
        ]].values

        # For each unique initial orbit calculate residuals and chi-squared
        # Find the orbit which yields the lowest chi-squared
        orbit_ids = np.unique(ephemeris[:, 0])
        for i, orbit_id in enumerate(orbit_ids):
            orbit_name = np.array(
                ["{}_v{}".format("_".join(ids.astype(str)), i + 1)])
            # Select unique orbit solution
            orbit_i = ephemeris[np.where(ephemeris[:, 0] == orbit_id)]

            # Calculate residuals in RA, make sure to fix any potential wrap around errors
            pred_dec = np.radians(orbit_i[:, 3])
            residual_ra = (ra - orbit_i[:, 2]) * np.cos(pred_dec)
            residual_ra = np.where(residual_ra > 180., 360. - residual_ra,
                                   residual_ra)

            # Calculate residuals in Dec
            residual_dec = dec - orbit_i[:, 3]

            # Calculate chi2
            chi2 = ((residual_ra**2 / sigma_ra**2) +
                    (residual_dec**2 / sigma_dec**2))
            chi2_total = np.sum(chi2)

            # All chi2 values are above the threshold, continue loop
            if np.all(chi2 >= chi2_threshold):
                continue

            # If the total chi2 is less than the threshold accept the orbit
            elif chi2_total <= chi2_threshold:
                orbit_sol = orbits_iod[i:i + 1, :]
                obs_ids_sol = ids
                chi2_sol = chi2_total
                remaining_ids = obs_ids_all
                arc_length = times_all.max() - times_all.min()
                converged = True

            # Let's now test to see if we can remove some outliers, we
            # anticipate we get to this stage if the three selected observations
            # belonging to one object yield a good initial orbit but the presence of outlier
            # observations is skewing the sum total of the residuals and chi2
            elif num_outliers > 0:
                for o in range(num_outliers):
                    # Select i highest observations that contribute to
                    # chi2 (and thereby the residuals)
                    remove = chi2[~mask].argsort()[-(o + 1):]

                    # Grab the obs_ids for these outliers
                    obs_id_outlier = obs_ids_all[~mask][remove]

                    # Subtract the outlier's chi2 contribution
                    # from the total chi2
                    chi2_new = chi2_total - np.sum(chi2[~mask][remove])

                    # If the updated chi2 total is lower than our desired
                    # threshold, accept the soluton. If not, keep going.
                    if chi2_new <= chi2_threshold:
                        orbit_sol = orbits_iod[i:i + 1, :]
                        obs_ids_sol = ids
                        chi2_sol = chi2_new
                        outliers = obs_id_outlier
                        num_obs = len(observations) - len(remove)
                        ids_mask = np.isin(obs_ids_all, outliers, invert=True)
                        arc_length = times_all[ids_mask].max(
                        ) - times_all[ids_mask].min()
                        remaining_ids = obs_ids_all[ids_mask]
                        converged = True
                        break

            else:
                continue

        j += 1

    if converged:
        orbit = pd.DataFrame(orbit_sol,
                             columns=[
                                 "epoch_mjd_utc", "obj_x", "obj_y", "obj_z",
                                 "obj_vx", "obj_vy", "obj_vz"
                             ])
        orbit["arc_length"] = arc_length
        orbit["num_obs"] = num_obs
        orbit["chi2"] = chi2_sol
        orbit.insert(0, "orbit_id", orbit_name)

        orbit_members = pd.DataFrame(np.vstack(
            [[orbit_name[0] for i in range(num_obs)], remaining_ids]).T,
                                     columns=[
                                         "orbit_id",
                                         "obs_id",
                                     ])

    return orbit, orbit_members, outliers
예제 #14
0
def maxdate(items):
    """Return the maximum time from a list of time strings."""
    time_list = Time(items)
    return time_list.max()